diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index ec4d25b20..016ba5c78 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -304,7 +304,6 @@ IngestionEvent_ObservationCreate, IngestionEvent_ObservationUpdate, IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, IngestionEvent_SpanCreate, IngestionEvent_SpanUpdate, IngestionEvent_TraceCreate, @@ -319,8 +318,6 @@ OptionalObservationBody, ScoreBody, ScoreEvent, - SdkLogBody, - SdkLogEvent, TraceBody, TraceEvent, UpdateEventBody, @@ -669,7 +666,6 @@ "IngestionEvent_ObservationCreate": ".ingestion", "IngestionEvent_ObservationUpdate": ".ingestion", "IngestionEvent_ScoreCreate": ".ingestion", - "IngestionEvent_SdkLog": ".ingestion", "IngestionEvent_SpanCreate": ".ingestion", "IngestionEvent_SpanUpdate": ".ingestion", "IngestionEvent_TraceCreate": ".ingestion", @@ -820,8 +816,6 @@ "Score_Correction": ".commons", "Score_Numeric": ".commons", "Score_Text": ".commons", - "SdkLogBody": ".ingestion", - "SdkLogEvent": ".ingestion", "ServiceProviderConfig": ".scim", "ServiceUnavailableError": ".health", "Session": ".commons", @@ -1124,7 +1118,6 @@ def __dir__(): "IngestionEvent_ObservationCreate", "IngestionEvent_ObservationUpdate", "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", "IngestionEvent_SpanCreate", "IngestionEvent_SpanUpdate", "IngestionEvent_TraceCreate", @@ -1275,8 +1268,6 @@ def __dir__(): "Score_Correction", "Score_Numeric", "Score_Text", - "SdkLogBody", - "SdkLogEvent", "ServiceProviderConfig", "ServiceUnavailableError", "Session", diff --git a/langfuse/api/comments/client.py b/langfuse/api/comments/client.py index 138c58b8f..f1f8036a3 100644 --- a/langfuse/api/comments/client.py +++ b/langfuse/api/comments/client.py @@ -1,5 +1,6 @@ # This file was auto-generated by Fern from our API Definition. +import datetime as dt import typing from ..commons.types.comment import Comment @@ -36,6 +37,7 @@ def create( object_id: str, content: str, author_user_id: typing.Optional[str] = OMIT, + object_start_time: typing.Optional[dt.datetime] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateCommentResponse: """ @@ -58,6 +60,9 @@ def create( author_user_id : typing.Optional[str] The id of the user who created the comment. Must be a member of the organization that owns the project, otherwise an error will be thrown. + object_start_time : typing.Optional[dt.datetime] + The start time of the referenced object (for observations, the observation's start time). Optional performance hint - when provided, Langfuse narrows the lookup to validate the reference faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -90,6 +95,7 @@ def create( object_id=object_id, content=content, author_user_id=author_user_id, + object_start_time=object_start_time, request_options=request_options, ) return _response.data @@ -221,6 +227,7 @@ async def create( object_id: str, content: str, author_user_id: typing.Optional[str] = OMIT, + object_start_time: typing.Optional[dt.datetime] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateCommentResponse: """ @@ -243,6 +250,9 @@ async def create( author_user_id : typing.Optional[str] The id of the user who created the comment. Must be a member of the organization that owns the project, otherwise an error will be thrown. + object_start_time : typing.Optional[dt.datetime] + The start time of the referenced object (for observations, the observation's start time). Optional performance hint - when provided, Langfuse narrows the lookup to validate the reference faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -283,6 +293,7 @@ async def main() -> None: object_id=object_id, content=content, author_user_id=author_user_id, + object_start_time=object_start_time, request_options=request_options, ) return _response.data diff --git a/langfuse/api/comments/raw_client.py b/langfuse/api/comments/raw_client.py index 811a7335c..276337be4 100644 --- a/langfuse/api/comments/raw_client.py +++ b/langfuse/api/comments/raw_client.py @@ -1,5 +1,6 @@ # This file was auto-generated by Fern from our API Definition. +import datetime as dt import typing from json.decoder import JSONDecodeError @@ -34,6 +35,7 @@ def create( object_id: str, content: str, author_user_id: typing.Optional[str] = OMIT, + object_start_time: typing.Optional[dt.datetime] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[CreateCommentResponse]: """ @@ -56,6 +58,9 @@ def create( author_user_id : typing.Optional[str] The id of the user who created the comment. Must be a member of the organization that owns the project, otherwise an error will be thrown. + object_start_time : typing.Optional[dt.datetime] + The start time of the referenced object (for observations, the observation's start time). Optional performance hint - when provided, Langfuse narrows the lookup to validate the reference faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -72,6 +77,7 @@ def create( "objectId": object_id, "content": content, "authorUserId": author_user_id, + "objectStartTime": object_start_time, }, request_options=request_options, omit=OMIT, @@ -398,6 +404,7 @@ async def create( object_id: str, content: str, author_user_id: typing.Optional[str] = OMIT, + object_start_time: typing.Optional[dt.datetime] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[CreateCommentResponse]: """ @@ -420,6 +427,9 @@ async def create( author_user_id : typing.Optional[str] The id of the user who created the comment. Must be a member of the organization that owns the project, otherwise an error will be thrown. + object_start_time : typing.Optional[dt.datetime] + The start time of the referenced object (for observations, the observation's start time). Optional performance hint - when provided, Langfuse narrows the lookup to validate the reference faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -436,6 +446,7 @@ async def create( "objectId": object_id, "content": content, "authorUserId": author_user_id, + "objectStartTime": object_start_time, }, request_options=request_options, omit=OMIT, diff --git a/langfuse/api/comments/types/create_comment_request.py b/langfuse/api/comments/types/create_comment_request.py index 53a756ca9..0d92a905b 100644 --- a/langfuse/api/comments/types/create_comment_request.py +++ b/langfuse/api/comments/types/create_comment_request.py @@ -1,5 +1,6 @@ # This file was auto-generated by Fern from our API Definition. +import datetime as dt import typing import pydantic @@ -42,6 +43,13 @@ class CreateCommentRequest(UniversalBaseModel): The id of the user who created the comment. Must be a member of the organization that owns the project, otherwise an error will be thrown. """ + object_start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="objectStartTime") + ] = pydantic.Field(default=None) + """ + The start time of the referenced object (for observations, the observation's start time). Optional performance hint - when provided, Langfuse narrows the lookup to validate the reference faster. It only affects speed - an incorrect or omitted value never changes the result. + """ + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True ) diff --git a/langfuse/api/commons/types/dataset.py b/langfuse/api/commons/types/dataset.py index d312b291a..b7e1d6bfe 100644 --- a/langfuse/api/commons/types/dataset.py +++ b/langfuse/api/commons/types/dataset.py @@ -23,14 +23,16 @@ class Dataset(UniversalBaseModel): """ input_schema: typing_extensions.Annotated[ - typing.Optional[typing.Any], FieldMetadata(alias="inputSchema") + typing.Optional[typing.Dict[str, 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") + typing.Optional[typing.Dict[str, typing.Any]], + FieldMetadata(alias="expectedOutputSchema"), ] = pydantic.Field(default=None) """ JSON Schema for validating dataset item expected outputs diff --git a/langfuse/api/datasets/client.py b/langfuse/api/datasets/client.py index 6a3b9b0c8..5da2c1fb8 100644 --- a/langfuse/api/datasets/client.py +++ b/langfuse/api/datasets/client.py @@ -120,8 +120,8 @@ def create( 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, + input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expected_output_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> Dataset: """ @@ -135,10 +135,10 @@ def create( metadata : typing.Optional[typing.Any] - input_schema : typing.Optional[typing.Any] + input_schema : typing.Optional[typing.Dict[str, 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] + expected_output_schema : typing.Optional[typing.Dict[str, 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] @@ -448,8 +448,8 @@ async def create( 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, + input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expected_output_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> Dataset: """ @@ -463,10 +463,10 @@ async def create( metadata : typing.Optional[typing.Any] - input_schema : typing.Optional[typing.Any] + input_schema : typing.Optional[typing.Dict[str, 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] + expected_output_schema : typing.Optional[typing.Dict[str, 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] diff --git a/langfuse/api/datasets/raw_client.py b/langfuse/api/datasets/raw_client.py index 1b0f49b77..c90ebe2e8 100644 --- a/langfuse/api/datasets/raw_client.py +++ b/langfuse/api/datasets/raw_client.py @@ -250,8 +250,8 @@ def create( 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, + input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expected_output_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[Dataset]: """ @@ -265,10 +265,10 @@ def create( metadata : typing.Optional[typing.Any] - input_schema : typing.Optional[typing.Any] + input_schema : typing.Optional[typing.Dict[str, 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] + expected_output_schema : typing.Optional[typing.Dict[str, 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] @@ -934,8 +934,8 @@ async def create( 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, + input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expected_output_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[Dataset]: """ @@ -949,10 +949,10 @@ async def create( metadata : typing.Optional[typing.Any] - input_schema : typing.Optional[typing.Any] + input_schema : typing.Optional[typing.Dict[str, 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] + expected_output_schema : typing.Optional[typing.Dict[str, 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] diff --git a/langfuse/api/datasets/types/create_dataset_request.py b/langfuse/api/datasets/types/create_dataset_request.py index 9d9b01089..a02ad3831 100644 --- a/langfuse/api/datasets/types/create_dataset_request.py +++ b/langfuse/api/datasets/types/create_dataset_request.py @@ -13,14 +13,16 @@ class CreateDatasetRequest(UniversalBaseModel): description: typing.Optional[str] = None metadata: typing.Optional[typing.Any] = None input_schema: typing_extensions.Annotated[ - typing.Optional[typing.Any], FieldMetadata(alias="inputSchema") + typing.Optional[typing.Dict[str, 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") + typing.Optional[typing.Dict[str, 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. diff --git a/langfuse/api/evaluation_commons/types/evaluation_rule_filter.py b/langfuse/api/evaluation_commons/types/evaluation_rule_filter.py index f281dbb17..41bf07064 100644 --- a/langfuse/api/evaluation_commons/types/evaluation_rule_filter.py +++ b/langfuse/api/evaluation_commons/types/evaluation_rule_filter.py @@ -34,7 +34,7 @@ class EvaluationRuleFilter_Datetime(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -97,7 +97,7 @@ class EvaluationRuleFilter_String(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -160,7 +160,7 @@ class EvaluationRuleFilter_Number(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -223,7 +223,7 @@ class EvaluationRuleFilter_StringOptions(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -286,7 +286,7 @@ class EvaluationRuleFilter_CategoryOptions(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -350,7 +350,7 @@ class EvaluationRuleFilter_ArrayOptions(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -413,7 +413,7 @@ class EvaluationRuleFilter_StringObject(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -477,7 +477,7 @@ class EvaluationRuleFilter_NumberObject(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -541,7 +541,7 @@ class EvaluationRuleFilter_Boolean(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` @@ -604,7 +604,7 @@ class EvaluationRuleFilter_Null(UniversalBaseModel): - `datetime`: `"="`, `">"`, `"<"`, `">="`, `"<="` - `stringOptions`: `any of`, `none of` - `arrayOptions`: `any of`, `none of`, `all of` - - `stringObject`: same operators as `string` + - `stringObject`: `"="`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set` (use `is set` / `is not set` for key presence; an empty value for `contains`, `starts with`, or `ends with` is treated as `is set`) - `boolean`: `"="`, `"<>"` - `null`: `is null`, `is not null` diff --git a/langfuse/api/evaluators/types/cursor_meta.py b/langfuse/api/evaluators/types/cursor_meta.py index 3ee6e1a0a..7e4c64a67 100644 --- a/langfuse/api/evaluators/types/cursor_meta.py +++ b/langfuse/api/evaluators/types/cursor_meta.py @@ -3,13 +3,12 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.cursor_meta_response import CursorMetaResponse -class CursorMeta(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) +class CursorMeta(CursorMetaResponse): """ - Opaque cursor for the next page. Omitted when there is no next page. + Cursor pagination metadata. """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/experiments/client.py b/langfuse/api/experiments/client.py index a3d469217..b786fbe35 100644 --- a/langfuse/api/experiments/client.py +++ b/langfuse/api/experiments/client.py @@ -47,7 +47,10 @@ def list( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiments on or after this datetime. + Required. Retrieve only experiments with events on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -145,7 +148,10 @@ def list_items( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiment items started on or after this datetime. + Required. Retrieve only experiment items started on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -263,7 +269,10 @@ async def list( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiments on or after this datetime. + Required. Retrieve only experiments with events on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -368,7 +377,10 @@ async def list_items( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiment items started on or after this datetime. + Required. Retrieve only experiment items started on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: diff --git a/langfuse/api/experiments/raw_client.py b/langfuse/api/experiments/raw_client.py index 8736786df..c65bbdaa1 100644 --- a/langfuse/api/experiments/raw_client.py +++ b/langfuse/api/experiments/raw_client.py @@ -45,7 +45,10 @@ def list( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiments on or after this datetime. + Required. Retrieve only experiments with events on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -205,7 +208,10 @@ def list_items( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiment items started on or after this datetime. + Required. Retrieve only experiment items started on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -374,7 +380,10 @@ async def list( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiments on or after this datetime. + Required. Retrieve only experiments with events on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: @@ -534,7 +543,10 @@ async def list_items( Parameters ---------- from_start_time : dt.datetime - Retrieve only experiment items started on or after this datetime. + Required. Retrieve only experiment items started on or after this + datetime. A lower bound is required so the query stays fast on + large projects; pair it with `toStartTime` to select a specific + window. fields : typing.Optional[str] Comma-separated list of field groups to include. Available groups: diff --git a/langfuse/api/experiments/types/experiments_response_meta.py b/langfuse/api/experiments/types/experiments_response_meta.py index 038bcc45d..61f2de4ea 100644 --- a/langfuse/api/experiments/types/experiments_response_meta.py +++ b/langfuse/api/experiments/types/experiments_response_meta.py @@ -3,13 +3,12 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.cursor_meta_response import CursorMetaResponse -class ExperimentsResponseMeta(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) +class ExperimentsResponseMeta(CursorMetaResponse): """ - Versioned base64url cursor for retrieving the next page. Absent when there are no more results. + Metadata for cursor-based pagination. """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/__init__.py b/langfuse/api/ingestion/__init__.py index 5cd4ba3bd..7a0d7c762 100644 --- a/langfuse/api/ingestion/__init__.py +++ b/langfuse/api/ingestion/__init__.py @@ -23,7 +23,6 @@ IngestionEvent_ObservationCreate, IngestionEvent_ObservationUpdate, IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, IngestionEvent_SpanCreate, IngestionEvent_SpanUpdate, IngestionEvent_TraceCreate, @@ -38,8 +37,6 @@ OptionalObservationBody, ScoreBody, ScoreEvent, - SdkLogBody, - SdkLogEvent, TraceBody, TraceEvent, UpdateEventBody, @@ -67,7 +64,6 @@ "IngestionEvent_ObservationCreate": ".types", "IngestionEvent_ObservationUpdate": ".types", "IngestionEvent_ScoreCreate": ".types", - "IngestionEvent_SdkLog": ".types", "IngestionEvent_SpanCreate": ".types", "IngestionEvent_SpanUpdate": ".types", "IngestionEvent_TraceCreate": ".types", @@ -82,8 +78,6 @@ "OptionalObservationBody": ".types", "ScoreBody": ".types", "ScoreEvent": ".types", - "SdkLogBody": ".types", - "SdkLogEvent": ".types", "TraceBody": ".types", "TraceEvent": ".types", "UpdateEventBody": ".types", @@ -140,7 +134,6 @@ def __dir__(): "IngestionEvent_ObservationCreate", "IngestionEvent_ObservationUpdate", "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", "IngestionEvent_SpanCreate", "IngestionEvent_SpanUpdate", "IngestionEvent_TraceCreate", @@ -155,8 +148,6 @@ def __dir__(): "OptionalObservationBody", "ScoreBody", "ScoreEvent", - "SdkLogBody", - "SdkLogEvent", "TraceBody", "TraceEvent", "UpdateEventBody", diff --git a/langfuse/api/ingestion/client.py b/langfuse/api/ingestion/client.py index c6ea6d805..4e78c2e2a 100644 --- a/langfuse/api/ingestion/client.py +++ b/langfuse/api/ingestion/client.py @@ -29,7 +29,7 @@ def with_raw_response(self) -> RawIngestionClient: return self._raw_client @typing_extensions.deprecated( - "On Langfuse Cloud, Langfuse v3 is deprecated and this endpoint will be removed on November 16, 2026. Send data via the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` instead, see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). Self-hosted deployments are unaffected by this date; the endpoint becomes unavailable when they upgrade to Langfuse v4.", + "On Langfuse Cloud, this is the deprecated Langfuse v3 ingestion API and it is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs; if you use custom auto-instrumentation, send them to the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` (for example with curl); see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`; all other public APIs may have data delays of several minutes. Self-hosted deployments are unaffected by the November 16 date; this behavior begins only when they enable v4-only write mode.", category=None, ) def batch( @@ -42,7 +42,15 @@ def batch( """ **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 + This is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. + + To write scores, prefer `POST /api/public/scores`: https://langfuse.com/docs/api-and-data-platform/features/scores-api + + To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl: https://langfuse.com/integrations/native/opentelemetry + + To read data back, use the v4 read APIs: https://langfuse.com/docs/api-and-data-platform/features/observations-api and https://langfuse.com/docs/metrics/features/metrics-api + + The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -59,7 +67,7 @@ def batch( Parameters ---------- batch : typing.Sequence[IngestionEvent] - Batch of tracing events to be ingested. Discriminated by attribute `type`. + Batch of events to be ingested, discriminated by attribute `type`. From November 16, 2026 on Langfuse Cloud, only `score-create` events are accepted; all other event types are rejected. metadata : typing.Optional[typing.Any] Optional. Metadata field used by the Langfuse SDKs for debugging. @@ -134,7 +142,7 @@ def with_raw_response(self) -> AsyncRawIngestionClient: return self._raw_client @typing_extensions.deprecated( - "On Langfuse Cloud, Langfuse v3 is deprecated and this endpoint will be removed on November 16, 2026. Send data via the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` instead, see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). Self-hosted deployments are unaffected by this date; the endpoint becomes unavailable when they upgrade to Langfuse v4.", + "On Langfuse Cloud, this is the deprecated Langfuse v3 ingestion API and it is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs; if you use custom auto-instrumentation, send them to the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` (for example with curl); see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`; all other public APIs may have data delays of several minutes. Self-hosted deployments are unaffected by the November 16 date; this behavior begins only when they enable v4-only write mode.", category=None, ) async def batch( @@ -147,7 +155,15 @@ async def batch( """ **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 + This is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. + + To write scores, prefer `POST /api/public/scores`: https://langfuse.com/docs/api-and-data-platform/features/scores-api + + To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl: https://langfuse.com/integrations/native/opentelemetry + + To read data back, use the v4 read APIs: https://langfuse.com/docs/api-and-data-platform/features/observations-api and https://langfuse.com/docs/metrics/features/metrics-api + + The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -164,7 +180,7 @@ async def batch( Parameters ---------- batch : typing.Sequence[IngestionEvent] - Batch of tracing events to be ingested. Discriminated by attribute `type`. + Batch of events to be ingested, discriminated by attribute `type`. From November 16, 2026 on Langfuse Cloud, only `score-create` events are accepted; all other event types are rejected. metadata : typing.Optional[typing.Any] Optional. Metadata field used by the Langfuse SDKs for debugging. diff --git a/langfuse/api/ingestion/raw_client.py b/langfuse/api/ingestion/raw_client.py index 67d22c556..95b01adb3 100644 --- a/langfuse/api/ingestion/raw_client.py +++ b/langfuse/api/ingestion/raw_client.py @@ -27,7 +27,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper @typing_extensions.deprecated( - "On Langfuse Cloud, Langfuse v3 is deprecated and this endpoint will be removed on November 16, 2026. Send data via the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` instead, see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). Self-hosted deployments are unaffected by this date; the endpoint becomes unavailable when they upgrade to Langfuse v4.", + "On Langfuse Cloud, this is the deprecated Langfuse v3 ingestion API and it is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs; if you use custom auto-instrumentation, send them to the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` (for example with curl); see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`; all other public APIs may have data delays of several minutes. Self-hosted deployments are unaffected by the November 16 date; this behavior begins only when they enable v4-only write mode.", category=None, ) def batch( @@ -40,7 +40,15 @@ def batch( """ **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 + This is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. + + To write scores, prefer `POST /api/public/scores`: https://langfuse.com/docs/api-and-data-platform/features/scores-api + + To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl: https://langfuse.com/integrations/native/opentelemetry + + To read data back, use the v4 read APIs: https://langfuse.com/docs/api-and-data-platform/features/observations-api and https://langfuse.com/docs/metrics/features/metrics-api + + The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -57,7 +65,7 @@ def batch( Parameters ---------- batch : typing.Sequence[IngestionEvent] - Batch of tracing events to be ingested. Discriminated by attribute `type`. + Batch of events to be ingested, discriminated by attribute `type`. From November 16, 2026 on Langfuse Cloud, only `score-create` events are accepted; all other event types are rejected. metadata : typing.Optional[typing.Any] Optional. Metadata field used by the Langfuse SDKs for debugging. @@ -167,7 +175,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper @typing_extensions.deprecated( - "On Langfuse Cloud, Langfuse v3 is deprecated and this endpoint will be removed on November 16, 2026. Send data via the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` instead, see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). Self-hosted deployments are unaffected by this date; the endpoint becomes unavailable when they upgrade to Langfuse v4.", + "On Langfuse Cloud, this is the deprecated Langfuse v3 ingestion API and it is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs; if you use custom auto-instrumentation, send them to the OpenTelemetry endpoint at `POST /api/public/otel/v1/traces` (for example with curl); see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`; all other public APIs may have data delays of several minutes. Self-hosted deployments are unaffected by the November 16 date; this behavior begins only when they enable v4-only write mode.", category=None, ) async def batch( @@ -180,7 +188,15 @@ async def batch( """ **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 + This is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types. + + To write scores, prefer `POST /api/public/scores`: https://langfuse.com/docs/api-and-data-platform/features/scores-api + + To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl: https://langfuse.com/integrations/native/opentelemetry + + To read data back, use the v4 read APIs: https://langfuse.com/docs/api-and-data-platform/features/observations-api and https://langfuse.com/docs/metrics/features/metrics-api + + The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -197,7 +213,7 @@ async def batch( Parameters ---------- batch : typing.Sequence[IngestionEvent] - Batch of tracing events to be ingested. Discriminated by attribute `type`. + Batch of events to be ingested, discriminated by attribute `type`. From November 16, 2026 on Langfuse Cloud, only `score-create` events are accepted; all other event types are rejected. metadata : typing.Optional[typing.Any] Optional. Metadata field used by the Langfuse SDKs for debugging. diff --git a/langfuse/api/ingestion/types/__init__.py b/langfuse/api/ingestion/types/__init__.py index 4addfd9c7..4fd3b0e7e 100644 --- a/langfuse/api/ingestion/types/__init__.py +++ b/langfuse/api/ingestion/types/__init__.py @@ -23,7 +23,6 @@ IngestionEvent_ObservationCreate, IngestionEvent_ObservationUpdate, IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, IngestionEvent_SpanCreate, IngestionEvent_SpanUpdate, IngestionEvent_TraceCreate, @@ -39,8 +38,6 @@ 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 @@ -67,7 +64,6 @@ "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", @@ -82,8 +78,6 @@ "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", @@ -140,7 +134,6 @@ def __dir__(): "IngestionEvent_ObservationCreate", "IngestionEvent_ObservationUpdate", "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", "IngestionEvent_SpanCreate", "IngestionEvent_SpanUpdate", "IngestionEvent_TraceCreate", @@ -155,8 +148,6 @@ def __dir__(): "OptionalObservationBody", "ScoreBody", "ScoreEvent", - "SdkLogBody", - "SdkLogEvent", "TraceBody", "TraceEvent", "UpdateEventBody", diff --git a/langfuse/api/ingestion/types/create_event_event.py b/langfuse/api/ingestion/types/create_event_event.py index e0cc820e1..a0b80a166 100644 --- a/langfuse/api/ingestion/types/create_event_event.py +++ b/langfuse/api/ingestion/types/create_event_event.py @@ -8,6 +8,10 @@ class CreateEventEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: CreateEventBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/create_generation_event.py b/langfuse/api/ingestion/types/create_generation_event.py index d62d6cc41..9ee974a10 100644 --- a/langfuse/api/ingestion/types/create_generation_event.py +++ b/langfuse/api/ingestion/types/create_generation_event.py @@ -8,6 +8,10 @@ class CreateGenerationEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: CreateGenerationBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/create_observation_event.py b/langfuse/api/ingestion/types/create_observation_event.py index 06d927f36..68f0c73f1 100644 --- a/langfuse/api/ingestion/types/create_observation_event.py +++ b/langfuse/api/ingestion/types/create_observation_event.py @@ -8,6 +8,10 @@ class CreateObservationEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: ObservationBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/create_span_event.py b/langfuse/api/ingestion/types/create_span_event.py index 6e60cf1fe..9b40bcf6b 100644 --- a/langfuse/api/ingestion/types/create_span_event.py +++ b/langfuse/api/ingestion/types/create_span_event.py @@ -8,6 +8,10 @@ class CreateSpanEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: CreateSpanBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/ingestion_event.py b/langfuse/api/ingestion/types/ingestion_event.py index 03202e635..3ee88e030 100644 --- a/langfuse/api/ingestion/types/ingestion_event.py +++ b/langfuse/api/ingestion/types/ingestion_event.py @@ -12,7 +12,6 @@ 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 @@ -102,18 +101,6 @@ class IngestionEvent_EventCreate(UniversalBaseModel): ) -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 @@ -147,7 +134,6 @@ class IngestionEvent_ObservationUpdate(UniversalBaseModel): IngestionEvent_GenerationCreate, IngestionEvent_GenerationUpdate, IngestionEvent_EventCreate, - IngestionEvent_SdkLog, IngestionEvent_ObservationCreate, IngestionEvent_ObservationUpdate, ], diff --git a/langfuse/api/ingestion/types/ingestion_response.py b/langfuse/api/ingestion/types/ingestion_response.py index b9781fab5..063b9a773 100644 --- a/langfuse/api/ingestion/types/ingestion_response.py +++ b/langfuse/api/ingestion/types/ingestion_response.py @@ -3,7 +3,10 @@ import typing import pydantic +import typing_extensions +from ...commons.types.deprecation import Deprecation from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .ingestion_error import IngestionError from .ingestion_success import IngestionSuccess @@ -11,6 +14,9 @@ class IngestionResponse(UniversalBaseModel): successes: typing.List[IngestionSuccess] errors: typing.List[IngestionError] + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/ingestion/types/trace_event.py b/langfuse/api/ingestion/types/trace_event.py index 54127597a..3c89ec52b 100644 --- a/langfuse/api/ingestion/types/trace_event.py +++ b/langfuse/api/ingestion/types/trace_event.py @@ -8,6 +8,10 @@ class TraceEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: TraceBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/update_generation_event.py b/langfuse/api/ingestion/types/update_generation_event.py index e2c7fe284..359c0934b 100644 --- a/langfuse/api/ingestion/types/update_generation_event.py +++ b/langfuse/api/ingestion/types/update_generation_event.py @@ -8,6 +8,10 @@ class UpdateGenerationEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: UpdateGenerationBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/update_observation_event.py b/langfuse/api/ingestion/types/update_observation_event.py index 5c33e7591..0f6f8786d 100644 --- a/langfuse/api/ingestion/types/update_observation_event.py +++ b/langfuse/api/ingestion/types/update_observation_event.py @@ -8,6 +8,10 @@ class UpdateObservationEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: ObservationBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/ingestion/types/update_span_event.py b/langfuse/api/ingestion/types/update_span_event.py index 20214ac9d..a2138ced6 100644 --- a/langfuse/api/ingestion/types/update_span_event.py +++ b/langfuse/api/ingestion/types/update_span_event.py @@ -8,6 +8,10 @@ class UpdateSpanEvent(BaseEvent): + """ + Sunset warning: this is the deprecated Langfuse v3 ingestion API. It is shut down on November 16, 2026, except for score events: from that date it accepts only `score-create` events and rejects all other event types, including this one. To write scores, prefer `POST /api/public/scores`; see the [Scores API docs](https://langfuse.com/docs/api-and-data-platform/features/scores-api). To write traces and observations, always prefer upgrading to the current Python and JS SDKs. If you use custom auto-instrumentation, send them to the OpenTelemetry endpoint (`POST /api/public/otel/v1/traces`), for example with curl; see the [OpenTelemetry integration docs](https://langfuse.com/integrations/native/opentelemetry). To read data back, use the v4 read APIs; see the [Observations API docs](https://langfuse.com/docs/api-and-data-platform/features/observations-api) and the [Metrics API docs](https://langfuse.com/docs/metrics/features/metrics-api). The only path to live data is OpenTelemetry ingestion combined with `GET /api/public/v2/observations` and `GET /api/public/v2/metrics`. All other public APIs may have data delays of several minutes. + """ + body: UpdateSpanBody model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/legacy/observations_v1/client.py b/langfuse/api/legacy/observations_v1/client.py index 871191ee5..596fb0a13 100644 --- a/langfuse/api/legacy/observations_v1/client.py +++ b/langfuse/api/legacy/observations_v1/client.py @@ -35,6 +35,7 @@ def get( self, observation_id: str, *, + start_time: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, ) -> ObservationsViewSingle: """ @@ -45,6 +46,9 @@ def get( observation_id : str The unique langfuse identifier of an observation, can be an event, span or generation + start_time : typing.Optional[dt.datetime] + The start time of the observation (ISO 8601 with offset, e.g. 2024-01-01T00:00:00Z). Optional performance hint - when provided, Langfuse narrows the lookup to make the request substantially faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -69,7 +73,7 @@ def get( ) """ _response = self._raw_client.get( - observation_id, request_options=request_options + observation_id, start_time=start_time, request_options=request_options ) return _response.data @@ -150,7 +154,7 @@ def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" @@ -291,6 +295,7 @@ async def get( self, observation_id: str, *, + start_time: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, ) -> ObservationsViewSingle: """ @@ -301,6 +306,9 @@ async def get( observation_id : str The unique langfuse identifier of an observation, can be an event, span or generation + start_time : typing.Optional[dt.datetime] + The start time of the observation (ISO 8601 with offset, e.g. 2024-01-01T00:00:00Z). Optional performance hint - when provided, Langfuse narrows the lookup to make the request substantially faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -333,7 +341,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._raw_client.get( - observation_id, request_options=request_options + observation_id, start_time=start_time, request_options=request_options ) return _response.data @@ -414,7 +422,7 @@ async def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" diff --git a/langfuse/api/legacy/observations_v1/raw_client.py b/langfuse/api/legacy/observations_v1/raw_client.py index 73343c130..026ce6672 100644 --- a/langfuse/api/legacy/observations_v1/raw_client.py +++ b/langfuse/api/legacy/observations_v1/raw_client.py @@ -34,6 +34,7 @@ def get( self, observation_id: str, *, + start_time: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ObservationsViewSingle]: """ @@ -44,6 +45,9 @@ def get( observation_id : str The unique langfuse identifier of an observation, can be an event, span or generation + start_time : typing.Optional[dt.datetime] + The start time of the observation (ISO 8601 with offset, e.g. 2024-01-01T00:00:00Z). Optional performance hint - when provided, Langfuse narrows the lookup to make the request substantially faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -54,6 +58,11 @@ def get( _response = self._client_wrapper.httpx_client.request( f"api/public/observations/{jsonable_encoder(observation_id)}", method="GET", + params={ + "startTime": serialize_datetime(start_time) + if start_time is not None + else None, + }, request_options=request_options, ) try: @@ -211,7 +220,7 @@ def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" @@ -411,6 +420,7 @@ async def get( self, observation_id: str, *, + start_time: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ObservationsViewSingle]: """ @@ -421,6 +431,9 @@ async def get( observation_id : str The unique langfuse identifier of an observation, can be an event, span or generation + start_time : typing.Optional[dt.datetime] + The start time of the observation (ISO 8601 with offset, e.g. 2024-01-01T00:00:00Z). Optional performance hint - when provided, Langfuse narrows the lookup to make the request substantially faster. It only affects speed - an incorrect or omitted value never changes the result. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -431,6 +444,11 @@ async def get( _response = await self._client_wrapper.httpx_client.request( f"api/public/observations/{jsonable_encoder(observation_id)}", method="GET", + params={ + "startTime": serialize_datetime(start_time) + if start_time is not None + else None, + }, request_options=request_options, ) try: @@ -588,7 +606,7 @@ async def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" diff --git a/langfuse/api/metrics/client.py b/langfuse/api/metrics/client.py index f2272409b..707112cc2 100644 --- a/langfuse/api/metrics/client.py +++ b/langfuse/api/metrics/client.py @@ -29,6 +29,8 @@ def metrics( """ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. + Together with OpenTelemetry ingestion and Observations API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## V2 Differences - Supports `observations`, `scores-numeric`, `scores-boolean`, and `scores-categorical` views only (traces view not supported) - Direct access to tags and release fields on observations @@ -255,6 +257,8 @@ async def metrics( """ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. + Together with OpenTelemetry ingestion and Observations API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## V2 Differences - Supports `observations`, `scores-numeric`, `scores-boolean`, and `scores-categorical` views only (traces view not supported) - Direct access to tags and release fields on observations diff --git a/langfuse/api/metrics/raw_client.py b/langfuse/api/metrics/raw_client.py index ce52e03c2..dbb81c772 100644 --- a/langfuse/api/metrics/raw_client.py +++ b/langfuse/api/metrics/raw_client.py @@ -26,6 +26,8 @@ def metrics( """ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. + Together with OpenTelemetry ingestion and Observations API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## V2 Differences - Supports `observations`, `scores-numeric`, `scores-boolean`, and `scores-categorical` views only (traces view not supported) - Direct access to tags and release fields on observations @@ -306,6 +308,8 @@ async def metrics( """ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. + Together with OpenTelemetry ingestion and Observations API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## V2 Differences - Supports `observations`, `scores-numeric`, `scores-boolean`, and `scores-categorical` views only (traces view not supported) - Direct access to tags and release fields on observations diff --git a/langfuse/api/observations/client.py b/langfuse/api/observations/client.py index 0bab55257..5e717adb6 100644 --- a/langfuse/api/observations/client.py +++ b/langfuse/api/observations/client.py @@ -51,6 +51,8 @@ def get_many( """ Get a list of observations with cursor-based pagination and flexible field selection. + Together with OpenTelemetry ingestion and Metrics API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## 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 @@ -153,7 +155,7 @@ def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" @@ -335,6 +337,8 @@ async def get_many( """ Get a list of observations with cursor-based pagination and flexible field selection. + Together with OpenTelemetry ingestion and Metrics API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## 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 @@ -437,7 +441,7 @@ async def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" diff --git a/langfuse/api/observations/raw_client.py b/langfuse/api/observations/raw_client.py index a4d6b82e7..84dba4891 100644 --- a/langfuse/api/observations/raw_client.py +++ b/langfuse/api/observations/raw_client.py @@ -49,6 +49,8 @@ def get_many( """ Get a list of observations with cursor-based pagination and flexible field selection. + Together with OpenTelemetry ingestion and Metrics API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## 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 @@ -151,7 +153,7 @@ def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" @@ -392,6 +394,8 @@ async def get_many( """ Get a list of observations with cursor-based pagination and flexible field selection. + Together with OpenTelemetry ingestion and Metrics API v2, this is the only real-time read path. Other public API endpoints can delay data by about 10 minutes. + ## 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 @@ -494,7 +498,7 @@ async def get_many( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - boolean: "=", "<>" // - null: "is null", "is not null" diff --git a/langfuse/api/observations/types/observations_v2meta.py b/langfuse/api/observations/types/observations_v2meta.py index 8f86a6512..46d9f42aa 100644 --- a/langfuse/api/observations/types/observations_v2meta.py +++ b/langfuse/api/observations/types/observations_v2meta.py @@ -3,17 +3,12 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.cursor_meta_response import CursorMetaResponse -class ObservationsV2Meta(UniversalBaseModel): +class ObservationsV2Meta(CursorMetaResponse): """ - 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. + Metadata for cursor-based pagination. """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/langfuse/api/opentelemetry/client.py b/langfuse/api/opentelemetry/client.py index 13177e5e6..31af9bc42 100644 --- a/langfuse/api/opentelemetry/client.py +++ b/langfuse/api/opentelemetry/client.py @@ -38,6 +38,8 @@ def export_traces( This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + Together with Observations API v2 and Metrics API v2, this is the only real-time write path. Other public API endpoints can delay data by about 10 minutes. Direct exporters must send `x-langfuse-ingestion-version: 4`; current Python and JS SDKs already do. + **Supported Formats:** - Binary Protobuf: `Content-Type: application/x-protobuf` - JSON Protobuf: `Content-Type: application/json` @@ -166,6 +168,8 @@ async def export_traces( This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + Together with Observations API v2 and Metrics API v2, this is the only real-time write path. Other public API endpoints can delay data by about 10 minutes. Direct exporters must send `x-langfuse-ingestion-version: 4`; current Python and JS SDKs already do. + **Supported Formats:** - Binary Protobuf: `Content-Type: application/x-protobuf` - JSON Protobuf: `Content-Type: application/json` diff --git a/langfuse/api/opentelemetry/raw_client.py b/langfuse/api/opentelemetry/raw_client.py index 6b68f909b..14c74c589 100644 --- a/langfuse/api/opentelemetry/raw_client.py +++ b/langfuse/api/opentelemetry/raw_client.py @@ -36,6 +36,8 @@ def export_traces( This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + Together with Observations API v2 and Metrics API v2, this is the only real-time write path. Other public API endpoints can delay data by about 10 minutes. Direct exporters must send `x-langfuse-ingestion-version: 4`; current Python and JS SDKs already do. + **Supported Formats:** - Binary Protobuf: `Content-Type: application/x-protobuf` - JSON Protobuf: `Content-Type: application/json` @@ -171,6 +173,8 @@ async def export_traces( This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + Together with Observations API v2 and Metrics API v2, this is the only real-time write path. Other public API endpoints can delay data by about 10 minutes. Direct exporters must send `x-langfuse-ingestion-version: 4`; current Python and JS SDKs already do. + **Supported Formats:** - Binary Protobuf: `Content-Type: application/x-protobuf` - JSON Protobuf: `Content-Type: application/json` diff --git a/langfuse/api/opentelemetry/types/otel_span.py b/langfuse/api/opentelemetry/types/otel_span.py index f6ef51a36..a97613103 100644 --- a/langfuse/api/opentelemetry/types/otel_span.py +++ b/langfuse/api/opentelemetry/types/otel_span.py @@ -66,7 +66,7 @@ class OtelSpan(UniversalBaseModel): Span attributes including Langfuse-specific attributes (langfuse.observation.*) """ - status: typing.Optional[typing.Any] = pydantic.Field(default=None) + status: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ Span status object """ diff --git a/langfuse/api/prompts/client.py b/langfuse/api/prompts/client.py index eff5e572f..b4a978c44 100644 --- a/langfuse/api/prompts/client.py +++ b/langfuse/api/prompts/client.py @@ -98,6 +98,7 @@ def list( limit: typing.Optional[int] = None, from_updated_at: typing.Optional[dt.datetime] = None, to_updated_at: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> PromptMetaListResponse: """ @@ -123,6 +124,32 @@ def list( to_updated_at : typing.Optional[dt.datetime] Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + filter : typing.Optional[str] + JSON string containing an array of filter conditions, combined with AND. + When provided, this takes precedence over name, version, + label, tag, fromUpdatedAt, and toUpdatedAt. An empty array applies no + filters. Project scoping and pagination always apply. + + Each condition requires `type`, `column`, `operator`, and `value`. + Supported types and columns: + - `string`: `id`, `name`, `type`. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is not empty`. + - `stringOptions`: `id`, `name`, `type`. Operators: `any of`, `none of`. Value: non-empty string array. + - `number`: `version`. Operators: `=`, `>`, `<`, `>=`, `<=`. Value: number. + - `datetime`: `createdAt`, `updatedAt`. Operators: `>`, `<`, `>=`, `<=`. Value: ISO 8601 timestamp. + - `arrayOptions`: `labels`, `tags`. Operators: `any of`, `all of`, `none of`. Value: non-empty string array. + - `stringObject`: `config`. Requires an additional `key` field. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set`. Value: string (use an empty string for key-presence operators). + + String equality is case-sensitive. String search operators use + case-insensitive SQL pattern matching: `%` matches any sequence and + `_` matches a single character. A backslash escapes these characters; + JSON-escape the backslash in the filter value. Asterisks are literal. + For config, an empty search value is treated as `is set`. + + Filters apply to prompt versions before aggregation: returned versions, + labels, and lastConfig reflect only matching versions. + + Example: [{"type":"string","column":"name","operator":"starts with","value":"tools/"},{"type":"string","column":"name","operator":"ends with","value":"/description"}] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -152,6 +179,7 @@ def list( limit=limit, from_updated_at=from_updated_at, to_updated_at=to_updated_at, + filter=filter, request_options=request_options, ) return _response.data @@ -358,6 +386,7 @@ async def list( limit: typing.Optional[int] = None, from_updated_at: typing.Optional[dt.datetime] = None, to_updated_at: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> PromptMetaListResponse: """ @@ -383,6 +412,32 @@ async def list( to_updated_at : typing.Optional[dt.datetime] Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + filter : typing.Optional[str] + JSON string containing an array of filter conditions, combined with AND. + When provided, this takes precedence over name, version, + label, tag, fromUpdatedAt, and toUpdatedAt. An empty array applies no + filters. Project scoping and pagination always apply. + + Each condition requires `type`, `column`, `operator`, and `value`. + Supported types and columns: + - `string`: `id`, `name`, `type`. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is not empty`. + - `stringOptions`: `id`, `name`, `type`. Operators: `any of`, `none of`. Value: non-empty string array. + - `number`: `version`. Operators: `=`, `>`, `<`, `>=`, `<=`. Value: number. + - `datetime`: `createdAt`, `updatedAt`. Operators: `>`, `<`, `>=`, `<=`. Value: ISO 8601 timestamp. + - `arrayOptions`: `labels`, `tags`. Operators: `any of`, `all of`, `none of`. Value: non-empty string array. + - `stringObject`: `config`. Requires an additional `key` field. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set`. Value: string (use an empty string for key-presence operators). + + String equality is case-sensitive. String search operators use + case-insensitive SQL pattern matching: `%` matches any sequence and + `_` matches a single character. A backslash escapes these characters; + JSON-escape the backslash in the filter value. Asterisks are literal. + For config, an empty search value is treated as `is set`. + + Filters apply to prompt versions before aggregation: returned versions, + labels, and lastConfig reflect only matching versions. + + Example: [{"type":"string","column":"name","operator":"starts with","value":"tools/"},{"type":"string","column":"name","operator":"ends with","value":"/description"}] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -420,6 +475,7 @@ async def main() -> None: limit=limit, from_updated_at=from_updated_at, to_updated_at=to_updated_at, + filter=filter, request_options=request_options, ) return _response.data diff --git a/langfuse/api/prompts/raw_client.py b/langfuse/api/prompts/raw_client.py index a2d81a9d1..c4816cbe0 100644 --- a/langfuse/api/prompts/raw_client.py +++ b/langfuse/api/prompts/raw_client.py @@ -161,6 +161,7 @@ def list( limit: typing.Optional[int] = None, from_updated_at: typing.Optional[dt.datetime] = None, to_updated_at: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[PromptMetaListResponse]: """ @@ -186,6 +187,32 @@ def list( to_updated_at : typing.Optional[dt.datetime] Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + filter : typing.Optional[str] + JSON string containing an array of filter conditions, combined with AND. + When provided, this takes precedence over name, version, + label, tag, fromUpdatedAt, and toUpdatedAt. An empty array applies no + filters. Project scoping and pagination always apply. + + Each condition requires `type`, `column`, `operator`, and `value`. + Supported types and columns: + - `string`: `id`, `name`, `type`. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is not empty`. + - `stringOptions`: `id`, `name`, `type`. Operators: `any of`, `none of`. Value: non-empty string array. + - `number`: `version`. Operators: `=`, `>`, `<`, `>=`, `<=`. Value: number. + - `datetime`: `createdAt`, `updatedAt`. Operators: `>`, `<`, `>=`, `<=`. Value: ISO 8601 timestamp. + - `arrayOptions`: `labels`, `tags`. Operators: `any of`, `all of`, `none of`. Value: non-empty string array. + - `stringObject`: `config`. Requires an additional `key` field. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set`. Value: string (use an empty string for key-presence operators). + + String equality is case-sensitive. String search operators use + case-insensitive SQL pattern matching: `%` matches any sequence and + `_` matches a single character. A backslash escapes these characters; + JSON-escape the backslash in the filter value. Asterisks are literal. + For config, an empty search value is treated as `is set`. + + Filters apply to prompt versions before aggregation: returned versions, + labels, and lastConfig reflect only matching versions. + + Example: [{"type":"string","column":"name","operator":"starts with","value":"tools/"},{"type":"string","column":"name","operator":"ends with","value":"/description"}] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -208,6 +235,7 @@ def list( "toUpdatedAt": serialize_datetime(to_updated_at) if to_updated_at is not None else None, + "filter": filter, }, request_options=request_options, ) @@ -642,6 +670,7 @@ async def list( limit: typing.Optional[int] = None, from_updated_at: typing.Optional[dt.datetime] = None, to_updated_at: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[PromptMetaListResponse]: """ @@ -667,6 +696,32 @@ async def list( to_updated_at : typing.Optional[dt.datetime] Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + filter : typing.Optional[str] + JSON string containing an array of filter conditions, combined with AND. + When provided, this takes precedence over name, version, + label, tag, fromUpdatedAt, and toUpdatedAt. An empty array applies no + filters. Project scoping and pagination always apply. + + Each condition requires `type`, `column`, `operator`, and `value`. + Supported types and columns: + - `string`: `id`, `name`, `type`. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is not empty`. + - `stringOptions`: `id`, `name`, `type`. Operators: `any of`, `none of`. Value: non-empty string array. + - `number`: `version`. Operators: `=`, `>`, `<`, `>=`, `<=`. Value: number. + - `datetime`: `createdAt`, `updatedAt`. Operators: `>`, `<`, `>=`, `<=`. Value: ISO 8601 timestamp. + - `arrayOptions`: `labels`, `tags`. Operators: `any of`, `all of`, `none of`. Value: non-empty string array. + - `stringObject`: `config`. Requires an additional `key` field. Operators: `=`, `contains`, `does not contain`, `starts with`, `ends with`, `is set`, `is not set`. Value: string (use an empty string for key-presence operators). + + String equality is case-sensitive. String search operators use + case-insensitive SQL pattern matching: `%` matches any sequence and + `_` matches a single character. A backslash escapes these characters; + JSON-escape the backslash in the filter value. Asterisks are literal. + For config, an empty search value is treated as `is set`. + + Filters apply to prompt versions before aggregation: returned versions, + labels, and lastConfig reflect only matching versions. + + Example: [{"type":"string","column":"name","operator":"starts with","value":"tools/"},{"type":"string","column":"name","operator":"ends with","value":"/description"}] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -689,6 +744,7 @@ async def list( "toUpdatedAt": serialize_datetime(to_updated_at) if to_updated_at is not None else None, + "filter": filter, }, request_options=request_options, ) diff --git a/langfuse/api/scores/client.py b/langfuse/api/scores/client.py index d035dc0bd..95f94389a 100644 --- a/langfuse/api/scores/client.py +++ b/langfuse/api/scores/client.py @@ -231,7 +231,7 @@ def get_many( Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with, is set, is not set. Use is set / is not set to filter on metadata key presence. An empty value for contains, starts with, or ends with is treated as is set (key presence). request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -544,7 +544,7 @@ async def get_many( Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with, is set, is not set. Use is set / is not set to filter on metadata key presence. An empty value for contains, starts with, or ends with is treated as is set (key presence). request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/langfuse/api/scores/raw_client.py b/langfuse/api/scores/raw_client.py index b87521d7a..e4295f233 100644 --- a/langfuse/api/scores/raw_client.py +++ b/langfuse/api/scores/raw_client.py @@ -297,7 +297,7 @@ def get_many( Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with, is set, is not set. Use is set / is not set to filter on metadata key presence. An empty value for contains, starts with, or ends with is treated as is set (key presence). request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -789,7 +789,7 @@ async def get_many( Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with, is set, is not set. Use is set / is not set to filter on metadata key presence. An empty value for contains, starts with, or ends with is treated as is set (key presence). request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/langfuse/api/scores_v3/types/get_scores_v3meta.py b/langfuse/api/scores_v3/types/get_scores_v3meta.py index 7dfcfe0e1..832793364 100644 --- a/langfuse/api/scores_v3/types/get_scores_v3meta.py +++ b/langfuse/api/scores_v3/types/get_scores_v3meta.py @@ -3,15 +3,11 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.cursor_meta_response import CursorMetaResponse -class GetScoresV3Meta(UniversalBaseModel): +class GetScoresV3Meta(CursorMetaResponse): limit: int - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - URL-safe base64 (base64url) cursor for the next page. Absent when there are no more results. - """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/trace/client.py b/langfuse/api/trace/client.py index 2cf76a4a0..5674177c1 100644 --- a/langfuse/api/trace/client.py +++ b/langfuse/api/trace/client.py @@ -198,7 +198,7 @@ def list( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - booleanObject: "=", "<>" // - boolean: "=", "<>" @@ -574,7 +574,7 @@ async def list( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - booleanObject: "=", "<>" // - boolean: "=", "<>" diff --git a/langfuse/api/trace/raw_client.py b/langfuse/api/trace/raw_client.py index 7cb22a519..0797b9919 100644 --- a/langfuse/api/trace/raw_client.py +++ b/langfuse/api/trace/raw_client.py @@ -326,7 +326,7 @@ def list( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - booleanObject: "=", "<>" // - boolean: "=", "<>" @@ -938,7 +938,7 @@ async def list( // - categoryOptions: "any of", "none of" // - arrayOptions: "any of", "none of", "all of" // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "is set", "is not set" // - numberObject: "=", ">", "<", ">=", "<=" // - booleanObject: "=", "<>" // - boolean: "=", "<>" diff --git a/langfuse/api/unstable/__init__.py b/langfuse/api/unstable/__init__.py index 2a249200a..2ebbf8399 100644 --- a/langfuse/api/unstable/__init__.py +++ b/langfuse/api/unstable/__init__.py @@ -20,7 +20,7 @@ TooManyRequestsError, UnauthorizedError, ) - from . import dashboard_widgets, dashboards, errors + from . import dashboard_widgets, dashboards, errors, skills from .dashboard_widgets import ( CreateDashboardWidgetRequest, DashboardWidget, @@ -59,6 +59,21 @@ UpdateDashboardRequest, WidgetPlacement, ) + from .skills import ( + CreateSkillVersionRequest, + DeleteSkillVersionResponse, + SkillFile, + SkillFileContentResponse, + SkillListPagination, + SkillMeta, + SkillMetaListResponse, + SkillVersion, + SkillVersionFileCreateInput, + SkillVersionFileInput, + SkillVersionFileReference, + UpdateSkillLabelsRequest, + UpdateSkillRequest, + ) _dynamic_imports: typing.Dict[str, str] = { "AccessDeniedError": ".errors", "BadRequestError": ".errors", @@ -69,6 +84,7 @@ "CreateDashboardRequest": ".dashboards", "CreateDashboardWidgetRequest": ".dashboard_widgets", "CreatePresetPlacement": ".dashboards", + "CreateSkillVersionRequest": ".skills", "CreateWidgetPlacement": ".dashboards", "Dashboard": ".dashboards", "DashboardDefinition": ".dashboards", @@ -92,6 +108,7 @@ "DeleteDashboardPlacementResponse": ".dashboards", "DeleteDashboardResponse": ".dashboards", "DeleteDashboardWidgetResponse": ".dashboard_widgets", + "DeleteSkillVersionResponse": ".skills", "InternalServerError": ".errors", "MethodNotAllowedError": ".errors", "NotFoundError": ".errors", @@ -100,15 +117,27 @@ "PublicApiErrorCode": ".errors", "PublicApiErrorDetails": ".errors", "PublicApiValidationIssue": ".errors", + "SkillFile": ".skills", + "SkillFileContentResponse": ".skills", + "SkillListPagination": ".skills", + "SkillMeta": ".skills", + "SkillMetaListResponse": ".skills", + "SkillVersion": ".skills", + "SkillVersionFileCreateInput": ".skills", + "SkillVersionFileInput": ".skills", + "SkillVersionFileReference": ".skills", "TooManyRequestsError": ".errors", "UnauthorizedError": ".errors", "UpdateDashboardPlacementRequest": ".dashboards", "UpdateDashboardRequest": ".dashboards", "UpdateDashboardWidgetRequest": ".dashboard_widgets", + "UpdateSkillLabelsRequest": ".skills", + "UpdateSkillRequest": ".skills", "WidgetPlacement": ".dashboards", "dashboard_widgets": ".dashboard_widgets", "dashboards": ".dashboards", "errors": ".errors", + "skills": ".skills", } @@ -149,6 +178,7 @@ def __dir__(): "CreateDashboardRequest", "CreateDashboardWidgetRequest", "CreatePresetPlacement", + "CreateSkillVersionRequest", "CreateWidgetPlacement", "Dashboard", "DashboardDefinition", @@ -172,6 +202,7 @@ def __dir__(): "DeleteDashboardPlacementResponse", "DeleteDashboardResponse", "DeleteDashboardWidgetResponse", + "DeleteSkillVersionResponse", "InternalServerError", "MethodNotAllowedError", "NotFoundError", @@ -180,13 +211,25 @@ def __dir__(): "PublicApiErrorCode", "PublicApiErrorDetails", "PublicApiValidationIssue", + "SkillFile", + "SkillFileContentResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", "TooManyRequestsError", "UnauthorizedError", "UpdateDashboardPlacementRequest", "UpdateDashboardRequest", "UpdateDashboardWidgetRequest", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", "WidgetPlacement", "dashboard_widgets", "dashboards", "errors", + "skills", ] diff --git a/langfuse/api/unstable/client.py b/langfuse/api/unstable/client.py index 23050bc80..3cc2a9298 100644 --- a/langfuse/api/unstable/client.py +++ b/langfuse/api/unstable/client.py @@ -13,6 +13,7 @@ DashboardWidgetsClient, ) from .dashboards.client import AsyncDashboardsClient, DashboardsClient + from .skills.client import AsyncSkillsClient, SkillsClient class UnstableClient: @@ -21,6 +22,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[DashboardWidgetsClient] = None self._dashboards: typing.Optional[DashboardsClient] = None + self._skills: typing.Optional[SkillsClient] = None @property def with_raw_response(self) -> RawUnstableClient: @@ -51,6 +53,14 @@ def dashboards(self): self._dashboards = DashboardsClient(client_wrapper=self._client_wrapper) return self._dashboards + @property + def skills(self): + if self._skills is None: + from .skills.client import SkillsClient # noqa: E402 + + self._skills = SkillsClient(client_wrapper=self._client_wrapper) + return self._skills + class AsyncUnstableClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -58,6 +68,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[AsyncDashboardWidgetsClient] = None self._dashboards: typing.Optional[AsyncDashboardsClient] = None + self._skills: typing.Optional[AsyncSkillsClient] = None @property def with_raw_response(self) -> AsyncRawUnstableClient: @@ -89,3 +100,11 @@ def dashboards(self): client_wrapper=self._client_wrapper ) return self._dashboards + + @property + def skills(self): + if self._skills is None: + from .skills.client import AsyncSkillsClient # noqa: E402 + + self._skills = AsyncSkillsClient(client_wrapper=self._client_wrapper) + return self._skills diff --git a/langfuse/api/unstable/skills/__init__.py b/langfuse/api/unstable/skills/__init__.py new file mode 100644 index 000000000..07756e8ec --- /dev/null +++ b/langfuse/api/unstable/skills/__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 ( + CreateSkillVersionRequest, + DeleteSkillVersionResponse, + SkillFile, + SkillFileContentResponse, + SkillListPagination, + SkillMeta, + SkillMetaListResponse, + SkillVersion, + SkillVersionFileCreateInput, + SkillVersionFileInput, + SkillVersionFileReference, + UpdateSkillLabelsRequest, + UpdateSkillRequest, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateSkillVersionRequest": ".types", + "DeleteSkillVersionResponse": ".types", + "SkillFile": ".types", + "SkillFileContentResponse": ".types", + "SkillListPagination": ".types", + "SkillMeta": ".types", + "SkillMetaListResponse": ".types", + "SkillVersion": ".types", + "SkillVersionFileCreateInput": ".types", + "SkillVersionFileInput": ".types", + "SkillVersionFileReference": ".types", + "UpdateSkillLabelsRequest": ".types", + "UpdateSkillRequest": ".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__ = [ + "CreateSkillVersionRequest", + "DeleteSkillVersionResponse", + "SkillFile", + "SkillFileContentResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", +] diff --git a/langfuse/api/unstable/skills/client.py b/langfuse/api/unstable/skills/client.py new file mode 100644 index 000000000..5f7d3c263 --- /dev/null +++ b/langfuse/api/unstable/skills/client.py @@ -0,0 +1,800 @@ +# 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 AsyncRawSkillsClient, RawSkillsClient +from .types.delete_skill_version_response import DeleteSkillVersionResponse +from .types.skill_file_content_response import SkillFileContentResponse +from .types.skill_meta_list_response import SkillMetaListResponse +from .types.skill_version import SkillVersion +from .types.skill_version_file_create_input import SkillVersionFileCreateInput + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class SkillsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSkillsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSkillsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSkillsClient + """ + return self._raw_client + + def list( + self, + *, + name: typing.Optional[str] = None, + search: 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, + ) -> SkillMetaListResponse: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillMetaListResponse + + 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.unstable.skills.list() + """ + _response = self._raw_client.list( + name=name, + search=search, + 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_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.unstable.skills import SkillVersionFileInput + + 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.unstable.skills.create_version( + files=[ + SkillVersionFileInput( + path="path", + content="content", + ), + SkillVersionFileInput( + path="path", + content="content", + ), + ], + ) + """ + _response = self._raw_client.create_version( + files=files, commit_message=commit_message, request_options=request_options + ) + return _response.data + + def get( + self, + skill_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's id with getFileContent to read its text content. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + label : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.get( + skill_name="skillName", + ) + """ + _response = self._raw_client.get( + skill_name, version=version, label=label, request_options=request_options + ) + return _response.data + + def update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.update( + skill_name="skillName", + tags=["tags", "tags"], + ) + """ + _response = self._raw_client.update( + skill_name, tags=tags, request_options=request_options + ) + return _response.data + + def get_file_content( + self, file_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SkillFileContentResponse: + """ + Read one text file from a persisted skill version using its file id, not its blob id. Returns JSON containing the text content with Cache-Control no-store. + + Parameters + ---------- + file_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillFileContentResponse + + 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.unstable.skills.get_file_content( + file_id="fileId", + ) + """ + _response = self._raw_client.get_file_content( + file_id, request_options=request_options + ) + return _response.data + + def set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.set_labels( + skill_name="skillName", + skill_version=1, + labels=["labels", "labels"], + ) + """ + _response = self._raw_client.set_labels( + skill_name, skill_version, labels=labels, request_options=request_options + ) + return _response.data + + def delete_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteSkillVersionResponse: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSkillVersionResponse + + 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.unstable.skills.delete_version( + skill_name="skillName", + skill_version=1, + ) + """ + _response = self._raw_client.delete_version( + skill_name, skill_version, request_options=request_options + ) + return _response.data + + +class AsyncSkillsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSkillsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSkillsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSkillsClient + """ + return self._raw_client + + async def list( + self, + *, + name: typing.Optional[str] = None, + search: 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, + ) -> SkillMetaListResponse: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillMetaListResponse + + 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.unstable.skills.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + name=name, + search=search, + 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_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.unstable.skills import SkillVersionFileInput + + 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.unstable.skills.create_version( + files=[ + SkillVersionFileInput( + path="path", + content="content", + ), + SkillVersionFileInput( + path="path", + content="content", + ), + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_version( + files=files, commit_message=commit_message, request_options=request_options + ) + return _response.data + + async def get( + self, + skill_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's id with getFileContent to read its text content. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + label : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.get( + skill_name="skillName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + skill_name, version=version, label=label, request_options=request_options + ) + return _response.data + + async def update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.update( + skill_name="skillName", + tags=["tags", "tags"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + skill_name, tags=tags, request_options=request_options + ) + return _response.data + + async def get_file_content( + self, file_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SkillFileContentResponse: + """ + Read one text file from a persisted skill version using its file id, not its blob id. Returns JSON containing the text content with Cache-Control no-store. + + Parameters + ---------- + file_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillFileContentResponse + + 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.unstable.skills.get_file_content( + file_id="fileId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_file_content( + file_id, request_options=request_options + ) + return _response.data + + async def set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.unstable.skills.set_labels( + skill_name="skillName", + skill_version=1, + labels=["labels", "labels"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.set_labels( + skill_name, skill_version, labels=labels, request_options=request_options + ) + return _response.data + + async def delete_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteSkillVersionResponse: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSkillVersionResponse + + 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.unstable.skills.delete_version( + skill_name="skillName", + skill_version=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_version( + skill_name, skill_version, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/unstable/skills/raw_client.py b/langfuse/api/unstable/skills/raw_client.py new file mode 100644 index 000000000..1990413ad --- /dev/null +++ b/langfuse/api/unstable/skills/raw_client.py @@ -0,0 +1,1631 @@ +# 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.delete_skill_version_response import DeleteSkillVersionResponse +from .types.skill_file_content_response import SkillFileContentResponse +from .types.skill_meta_list_response import SkillMetaListResponse +from .types.skill_version import SkillVersion +from .types.skill_version_file_create_input import SkillVersionFileCreateInput + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawSkillsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + name: typing.Optional[str] = None, + search: 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[SkillMetaListResponse]: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillMetaListResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/skills", + method="GET", + params={ + "name": name, + "search": search, + "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( + SkillMetaListResponse, + parse_obj_as( + type_=SkillMetaListResponse, # 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_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/skills", + method="POST", + json={ + "files": convert_and_respect_annotation_metadata( + object_=files, + annotation=typing.Sequence[SkillVersionFileCreateInput], + direction="write", + ), + "commitMessage": commit_message, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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, + skill_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's id with getFileContent to read its text content. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + label : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + method="GET", + params={ + "version": version, + "label": label, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + method="PATCH", + json={ + "tags": tags, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_file_content( + self, file_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SkillFileContentResponse]: + """ + Read one text file from a persisted skill version using its file id, not its blob id. Returns JSON containing the text content with Cache-Control no-store. + + Parameters + ---------- + file_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillFileContentResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/files/{jsonable_encoder(file_id)}/content", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillFileContentResponse, + parse_obj_as( + type_=SkillFileContentResponse, # 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 set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="PATCH", + json={ + "labels": labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteSkillVersionResponse]: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteSkillVersionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSkillVersionResponse, + parse_obj_as( + type_=DeleteSkillVersionResponse, # 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 AsyncRawSkillsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + name: typing.Optional[str] = None, + search: 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[SkillMetaListResponse]: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillMetaListResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/skills", + method="GET", + params={ + "name": name, + "search": search, + "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( + SkillMetaListResponse, + parse_obj_as( + type_=SkillMetaListResponse, # 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_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/skills", + method="POST", + json={ + "files": convert_and_respect_annotation_metadata( + object_=files, + annotation=typing.Sequence[SkillVersionFileCreateInput], + direction="write", + ), + "commitMessage": commit_message, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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, + skill_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's id with getFileContent to read its text content. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + label : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + method="GET", + params={ + "version": version, + "label": label, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + method="PATCH", + json={ + "tags": tags, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_file_content( + self, file_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SkillFileContentResponse]: + """ + Read one text file from a persisted skill version using its file id, not its blob id. Returns JSON containing the text content with Cache-Control no-store. + + Parameters + ---------- + file_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillFileContentResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/files/{jsonable_encoder(file_id)}/content", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillFileContentResponse, + parse_obj_as( + type_=SkillFileContentResponse, # 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 set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="PATCH", + json={ + "labels": labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteSkillVersionResponse]: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteSkillVersionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSkillVersionResponse, + parse_obj_as( + type_=DeleteSkillVersionResponse, # 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/unstable/skills/types/__init__.py b/langfuse/api/unstable/skills/types/__init__.py new file mode 100644 index 000000000..3171bd1be --- /dev/null +++ b/langfuse/api/unstable/skills/types/__init__.py @@ -0,0 +1,80 @@ +# 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_skill_version_request import CreateSkillVersionRequest + from .delete_skill_version_response import DeleteSkillVersionResponse + from .skill_file import SkillFile + from .skill_file_content_response import SkillFileContentResponse + from .skill_list_pagination import SkillListPagination + from .skill_meta import SkillMeta + from .skill_meta_list_response import SkillMetaListResponse + from .skill_version import SkillVersion + from .skill_version_file_create_input import SkillVersionFileCreateInput + from .skill_version_file_input import SkillVersionFileInput + from .skill_version_file_reference import SkillVersionFileReference + from .update_skill_labels_request import UpdateSkillLabelsRequest + from .update_skill_request import UpdateSkillRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateSkillVersionRequest": ".create_skill_version_request", + "DeleteSkillVersionResponse": ".delete_skill_version_response", + "SkillFile": ".skill_file", + "SkillFileContentResponse": ".skill_file_content_response", + "SkillListPagination": ".skill_list_pagination", + "SkillMeta": ".skill_meta", + "SkillMetaListResponse": ".skill_meta_list_response", + "SkillVersion": ".skill_version", + "SkillVersionFileCreateInput": ".skill_version_file_create_input", + "SkillVersionFileInput": ".skill_version_file_input", + "SkillVersionFileReference": ".skill_version_file_reference", + "UpdateSkillLabelsRequest": ".update_skill_labels_request", + "UpdateSkillRequest": ".update_skill_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__ = [ + "CreateSkillVersionRequest", + "DeleteSkillVersionResponse", + "SkillFile", + "SkillFileContentResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", +] diff --git a/langfuse/api/unstable/skills/types/create_skill_version_request.py b/langfuse/api/unstable/skills/types/create_skill_version_request.py new file mode 100644 index 000000000..cf7c073ca --- /dev/null +++ b/langfuse/api/unstable/skills/types/create_skill_version_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 +from .skill_version_file_create_input import SkillVersionFileCreateInput + + +class CreateSkillVersionRequest(UniversalBaseModel): + files: typing.List[SkillVersionFileCreateInput] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + + 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/unstable/skills/types/delete_skill_version_response.py similarity index 62% rename from langfuse/api/ingestion/types/sdk_log_event.py rename to langfuse/api/unstable/skills/types/delete_skill_version_response.py index ca303af55..cf92c2867 100644 --- a/langfuse/api/ingestion/types/sdk_log_event.py +++ b/langfuse/api/unstable/skills/types/delete_skill_version_response.py @@ -3,12 +3,11 @@ import typing import pydantic -from .base_event import BaseEvent -from .sdk_log_body import SdkLogBody +from ....core.pydantic_utilities import UniversalBaseModel -class SdkLogEvent(BaseEvent): - body: SdkLogBody +class DeleteSkillVersionResponse(UniversalBaseModel): + deleted: bool model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/skills/types/skill_file.py b/langfuse/api/unstable/skills/types/skill_file.py new file mode 100644 index 000000000..683b13679 --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_file.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 + + +class SkillFile(UniversalBaseModel): + id: str + path: str + blob_id: typing_extensions.Annotated[str, FieldMetadata(alias="blobId")] + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] + content_type: typing_extensions.Annotated[str, FieldMetadata(alias="contentType")] + content_length: typing_extensions.Annotated[ + int, FieldMetadata(alias="contentLength") + ] + + 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/unstable/skills/types/skill_file_content_response.py similarity index 63% rename from langfuse/api/ingestion/types/sdk_log_body.py rename to langfuse/api/unstable/skills/types/skill_file_content_response.py index d5b46f118..d8836821d 100644 --- a/langfuse/api/ingestion/types/sdk_log_body.py +++ b/langfuse/api/unstable/skills/types/skill_file_content_response.py @@ -3,11 +3,11 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ....core.pydantic_utilities import UniversalBaseModel -class SdkLogBody(UniversalBaseModel): - log: typing.Any +class SkillFileContentResponse(UniversalBaseModel): + content: str model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/skills/types/skill_list_pagination.py b/langfuse/api/unstable/skills/types/skill_list_pagination.py new file mode 100644 index 000000000..f7ae797de --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_list_pagination.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.serialization import FieldMetadata +from ....utils.pagination.types.meta_response import MetaResponse + + +class SkillListPagination(MetaResponse): + has_next_page: typing_extensions.Annotated[bool, FieldMetadata(alias="hasNextPage")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/skill_meta.py b/langfuse/api/unstable/skills/types/skill_meta.py new file mode 100644 index 000000000..a391972df --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_meta.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 + + +class SkillMeta(UniversalBaseModel): + name: str + description: str + tags: typing.List[str] + latest_version: typing_extensions.Annotated[ + int, FieldMetadata(alias="latestVersion") + ] + latest_version_created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="latestVersionCreatedAt") + ] + latest_version_last_updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="latestVersionLastUpdatedAt") + ] + production_version: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="productionVersion") + ] = pydantic.Field(default=None) + """ + Version assigned the production label, or null when no version is assigned. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/skill_meta_list_response.py b/langfuse/api/unstable/skills/types/skill_meta_list_response.py new file mode 100644 index 000000000..a097e1bc0 --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_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 .skill_list_pagination import SkillListPagination +from .skill_meta import SkillMeta + + +class SkillMetaListResponse(UniversalBaseModel): + data: typing.List[SkillMeta] + meta: SkillListPagination + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/skill_version.py b/langfuse/api/unstable/skills/types/skill_version.py new file mode 100644 index 000000000..c3486c23d --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_version.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 +from .skill_file import SkillFile + + +class SkillVersion(UniversalBaseModel): + id: 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")] + created_by: typing_extensions.Annotated[str, FieldMetadata(alias="createdBy")] + name: str + description: str + frontmatter: typing.Any + version: int + tags: typing.List[str] + labels: typing.List[str] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + files: typing.List[SkillFile] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/skill_version_file_create_input.py b/langfuse/api/unstable/skills/types/skill_version_file_create_input.py new file mode 100644 index 000000000..495002d8c --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_version_file_create_input.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .skill_version_file_input import SkillVersionFileInput +from .skill_version_file_reference import SkillVersionFileReference + +SkillVersionFileCreateInput = typing.Union[ + SkillVersionFileInput, SkillVersionFileReference +] diff --git a/langfuse/api/unstable/skills/types/skill_version_file_input.py b/langfuse/api/unstable/skills/types/skill_version_file_input.py new file mode 100644 index 000000000..c6f20d51e --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_version_file_input.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 SkillVersionFileInput(UniversalBaseModel): + path: str + content: str = pydantic.Field() + """ + UTF-8 text content. The server computes the hash and byte length. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/skill_version_file_reference.py b/langfuse/api/unstable/skills/types/skill_version_file_reference.py new file mode 100644 index 000000000..4960d9e90 --- /dev/null +++ b/langfuse/api/unstable/skills/types/skill_version_file_reference.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 SkillVersionFileReference(UniversalBaseModel): + path: str + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] = ( + pydantic.Field() + ) + """ + Canonical base64-encoded SHA-256 from a skill file manifest. The content must already exist in this project. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/update_skill_labels_request.py b/langfuse/api/unstable/skills/types/update_skill_labels_request.py new file mode 100644 index 000000000..c1931a1e4 --- /dev/null +++ b/langfuse/api/unstable/skills/types/update_skill_labels_request.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 UpdateSkillLabelsRequest(UniversalBaseModel): + labels: typing.List[str] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/skills/types/update_skill_request.py b/langfuse/api/unstable/skills/types/update_skill_request.py new file mode 100644 index 000000000..3c211b921 --- /dev/null +++ b/langfuse/api/unstable/skills/types/update_skill_request.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 UpdateSkillRequest(UniversalBaseModel): + tags: typing.List[str] = pydantic.Field() + """ + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + """ + + 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 index b272f64b5..1690ee475 100644 --- a/langfuse/api/utils/__init__.py +++ b/langfuse/api/utils/__init__.py @@ -7,8 +7,9 @@ if typing.TYPE_CHECKING: from . import pagination - from .pagination import MetaResponse + from .pagination import CursorMetaResponse, MetaResponse _dynamic_imports: typing.Dict[str, str] = { + "CursorMetaResponse": ".pagination", "MetaResponse": ".pagination", "pagination": ".pagination", } @@ -41,4 +42,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["MetaResponse", "pagination"] +__all__ = ["CursorMetaResponse", "MetaResponse", "pagination"] diff --git a/langfuse/api/utils/pagination/__init__.py b/langfuse/api/utils/pagination/__init__.py index 50821832d..59cdf0321 100644 --- a/langfuse/api/utils/pagination/__init__.py +++ b/langfuse/api/utils/pagination/__init__.py @@ -6,8 +6,11 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import MetaResponse -_dynamic_imports: typing.Dict[str, str] = {"MetaResponse": ".types"} + from .types import CursorMetaResponse, MetaResponse +_dynamic_imports: typing.Dict[str, str] = { + "CursorMetaResponse": ".types", + "MetaResponse": ".types", +} def __getattr__(attr_name: str) -> typing.Any: @@ -37,4 +40,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["MetaResponse"] +__all__ = ["CursorMetaResponse", "MetaResponse"] diff --git a/langfuse/api/utils/pagination/types/__init__.py b/langfuse/api/utils/pagination/types/__init__.py index 5c0d83028..85d13aee0 100644 --- a/langfuse/api/utils/pagination/types/__init__.py +++ b/langfuse/api/utils/pagination/types/__init__.py @@ -6,8 +6,12 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .cursor_meta_response import CursorMetaResponse from .meta_response import MetaResponse -_dynamic_imports: typing.Dict[str, str] = {"MetaResponse": ".meta_response"} +_dynamic_imports: typing.Dict[str, str] = { + "CursorMetaResponse": ".cursor_meta_response", + "MetaResponse": ".meta_response", +} def __getattr__(attr_name: str) -> typing.Any: @@ -37,4 +41,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["MetaResponse"] +__all__ = ["CursorMetaResponse", "MetaResponse"] diff --git a/langfuse/api/utils/pagination/types/cursor_meta_response.py b/langfuse/api/utils/pagination/types/cursor_meta_response.py new file mode 100644 index 000000000..eb30740ac --- /dev/null +++ b/langfuse/api/utils/pagination/types/cursor_meta_response.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 CursorMetaResponse(UniversalBaseModel): + """ + Metadata for cursor-based pagination. + """ + + cursor: typing.Optional[str] = pydantic.Field(default=None) + """ + Opaque cursor for the next page. Omitted when there is no next page. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + )