diff --git a/langfuse/api/README.md b/langfuse/api/README.md index 93549c0d6..e1a87ffff 100644 --- a/langfuse/api/README.md +++ b/langfuse/api/README.md @@ -29,11 +29,10 @@ client = FernLangfuse( ) client.comments.create( request=CreateCommentRequest( - project_id="string", - object_type="string", - object_id="string", - content="string", - author_user_id="string", + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", ), ) ``` @@ -61,11 +60,10 @@ client = AsyncFernLangfuse( async def main() -> None: await client.comments.create( request=CreateCommentRequest( - project_id="string", - object_type="string", - object_id="string", - content="string", - author_user_id="string", + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", ), ) diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index ec2bed17e..71adf3728 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -42,6 +42,9 @@ DatasetRunItem, DatasetRunWithItems, DatasetStatus, + DeleteDatasetItemResponse, + DeleteDatasetRunResponse, + DeleteTraceResponse, Error, GetCommentsResponse, GetMediaResponse, @@ -86,8 +89,9 @@ Observations, ObservationsView, ObservationsViews, + OpenAiCompletionUsageSchema, + OpenAiResponseUsageSchema, OpenAiUsage, - OpenAiUsageSchema, OptionalObservationBody, PaginatedDatasetItems, PaginatedDatasetRuns, @@ -198,6 +202,9 @@ "DatasetRunItem", "DatasetRunWithItems", "DatasetStatus", + "DeleteDatasetItemResponse", + "DeleteDatasetRunResponse", + "DeleteTraceResponse", "Error", "GetCommentsResponse", "GetMediaResponse", @@ -242,8 +249,9 @@ "Observations", "ObservationsView", "ObservationsViews", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", "OpenAiUsage", - "OpenAiUsageSchema", "OptionalObservationBody", "PaginatedDatasetItems", "PaginatedDatasetRuns", diff --git a/langfuse/api/reference.md b/langfuse/api/reference.md index 0b6bf964a..77f2b2a58 100644 --- a/langfuse/api/reference.md +++ b/langfuse/api/reference.md @@ -40,11 +40,10 @@ client = FernLangfuse( ) client.comments.create( request=CreateCommentRequest( - project_id="string", - object_type="string", - object_id="string", - content="string", - author_user_id="string", + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", ), ) @@ -119,13 +118,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.comments.get( - page=1, - limit=1, - object_type="string", - object_id="string", - author_user_id="string", -) +client.comments.get() ``` @@ -231,7 +224,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.comments.get_by_id( - comment_id="string", + comment_id="commentId", ) ``` @@ -296,7 +289,7 @@ Create a dataset item
```python -from langfuse import CreateDatasetItemRequest, DatasetStatus +from langfuse import CreateDatasetItemRequest from langfuse.client import FernLangfuse client = FernLangfuse( @@ -309,14 +302,7 @@ client = FernLangfuse( ) client.dataset_items.create( request=CreateDatasetItemRequest( - dataset_name="string", - input={"key": "value"}, - expected_output={"key": "value"}, - metadata={"key": "value"}, - source_trace_id="string", - source_observation_id="string", - id="string", - status=DatasetStatus.ACTIVE, + dataset_name="datasetName", ), ) @@ -392,7 +378,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.dataset_items.get( - id="string", + id="id", ) ``` @@ -466,13 +452,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.dataset_items.list( - dataset_name="string", - source_trace_id="string", - source_observation_id="string", - page=1, - limit=1, -) +client.dataset_items.list() ```
@@ -536,6 +516,81 @@ client.dataset_items.list( + + + + +
client.dataset_items.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a dataset item and all its run items. This action is irreversible. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", +) +client.dataset_items.delete( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -581,12 +636,8 @@ client = FernLangfuse( ) client.dataset_run_items.create( request=CreateDatasetRunItemRequest( - run_name="string", - run_description="string", - metadata={"key": "value"}, - dataset_item_id="string", - observation_id="string", - trace_id="string", + run_name="runName", + dataset_item_id="datasetItemId", ), ) @@ -662,10 +713,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.datasets.list( - page=1, - limit=1, -) +client.datasets.list() ``` @@ -747,7 +795,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.datasets.get( - dataset_name="string", + dataset_name="datasetName", ) ``` @@ -824,9 +872,7 @@ client = FernLangfuse( ) client.datasets.create( request=CreateDatasetRequest( - name="string", - description="string", - metadata={"key": "value"}, + name="name", ), ) @@ -902,8 +948,92 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.datasets.get_run( - dataset_name="string", - run_name="string", + dataset_name="datasetName", + run_name="runName", +) + +``` + + + + + +#### ⚙️ Parameters + +
+
+ +
+
+ +**dataset_name:** `str` + +
+
+ +
+
+ +**run_name:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + + + + + +
client.datasets.delete_run(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a dataset run and all its run items. This action is irreversible. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", +) +client.datasets.delete_run( + dataset_name="datasetName", + run_name="runName", ) ``` @@ -986,9 +1116,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.datasets.get_runs( - dataset_name="string", - page=1, - limit=1, + dataset_name="datasetName", ) ``` @@ -1131,7 +1259,6 @@ The event.body.id is the ID of the actual trace and will be used for updates and I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: - - Introduction to data model: https://langfuse.com/docs/tracing-data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors. @@ -1170,6 +1297,7 @@ client.ingestion.batch( trace_id="1234-5678-90ab-cdef", name="My Score", value=0.9, + environment="default", ), ) ], @@ -1256,7 +1384,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.media.get( - media_id="string", + media_id="mediaId", ) ``` @@ -1334,14 +1462,12 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.media.patch( - media_id="string", + media_id="mediaId", request=PatchMediaBody( uploaded_at=datetime.datetime.fromisoformat( "2024-01-15 09:30:00+00:00", ), upload_http_status=1, - upload_http_error="string", - upload_time_ms=1, ), ) @@ -1427,12 +1553,11 @@ client = FernLangfuse( ) client.media.get_upload_url( request=GetMediaUploadUrlRequest( - trace_id="string", - observation_id="string", + trace_id="traceId", content_type=MediaContentType.IMAGE_PNG, content_length=1, - sha_256_hash="string", - field="string", + sha_256_hash="sha256Hash", + field="field", ), ) @@ -1498,8 +1623,6 @@ Get daily metrics of the Langfuse project
```python -import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -1510,19 +1633,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.metrics.daily( - page=1, - limit=1, - trace_name="string", - user_id="string", - tags="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), -) +client.metrics.daily() ```
@@ -1578,6 +1689,14 @@ client.metrics.daily(
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for metrics where events include any of these environments + +
+
+ +
+
+ **from_timestamp:** `typing.Optional[dt.datetime]` — Optional filter to only include traces and observations on or after a certain datetime (ISO 8601)
@@ -1634,9 +1753,7 @@ Create a model
```python -import datetime - -from langfuse import CreateModelRequest, ModelUsageUnit +from langfuse import CreateModelRequest from langfuse.client import FernLangfuse client = FernLangfuse( @@ -1649,17 +1766,8 @@ client = FernLangfuse( ) client.models.create( request=CreateModelRequest( - model_name="string", - match_pattern="string", - start_date=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - unit=ModelUsageUnit.CHARACTERS, - input_price=1.1, - output_price=1.1, - total_price=1.1, - tokenizer_id="string", - tokenizer_config={"key": "value"}, + model_name="modelName", + match_pattern="matchPattern", ), ) @@ -1734,10 +1842,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.models.list( - page=1, - limit=1, -) +client.models.list() ```
@@ -1819,7 +1924,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.models.get( - id="string", + id="id", ) ``` @@ -1894,7 +1999,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.models.delete( - id="string", + id="id", ) ``` @@ -1970,7 +2075,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.observations.get( - observation_id="string", + observation_id="observationId", ) ``` @@ -2034,8 +2139,6 @@ Get a list of observations
```python -import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -2046,22 +2149,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.observations.get_many( - page=1, - limit=1, - name="string", - user_id="string", - type="string", - trace_id="string", - parent_observation_id="string", - from_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - version="string", -) +client.observations.get_many() ```
@@ -2133,6 +2221,14 @@ client.observations.get_many(
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for observations where the environment is one of the provided values. + +
+
+ +
+
+ **from_start_time:** `typing.Optional[dt.datetime]` — Retrieve only observations with a start_time or or after this datetime (ISO 8601).
@@ -2274,9 +2370,9 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.prompt_version.update( - name="string", + name="name", version=1, - new_labels=["string"], + new_labels=["newLabels", "newLabels"], ) ``` @@ -2368,9 +2464,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.prompts.get( - prompt_name="string", - version=1, - label="string", + prompt_name="promptName", ) ``` @@ -2450,8 +2544,6 @@ Get a list of prompt names with versions and labels
```python -import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -2462,19 +2554,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.prompts.list( - name="string", - label="string", - tag="string", - page=1, - limit=1, - from_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), -) +client.prompts.list() ```
@@ -2598,17 +2678,17 @@ client = FernLangfuse( ) client.prompts.create( request=CreatePromptRequest_Chat( - name="string", + name="name", prompt=[ ChatMessage( - role="string", - content="string", - ) + role="role", + content="content", + ), + ChatMessage( + role="role", + content="content", + ), ], - config={"key": "value"}, - labels=["string"], - tags=["string"], - commit_message="string", ), ) @@ -2674,7 +2754,7 @@ Create a score configuration (config). Score configs are used to define the stru
```python -from langfuse import ConfigCategory, CreateScoreConfigRequest, ScoreDataType +from langfuse import CreateScoreConfigRequest, ScoreDataType from langfuse.client import FernLangfuse client = FernLangfuse( @@ -2687,17 +2767,8 @@ client = FernLangfuse( ) client.score_configs.create( request=CreateScoreConfigRequest( - name="string", + name="name", data_type=ScoreDataType.NUMERIC, - categories=[ - ConfigCategory( - value=1.1, - label="string", - ) - ], - min_value=1.1, - max_value=1.1, - description="string", ), ) @@ -2772,10 +2843,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.score_configs.get( - page=1, - limit=1, -) +client.score_configs.get() ```
@@ -2857,7 +2925,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.score_configs.get_by_id( - config_id="string", + config_id="configId", ) ``` @@ -2935,9 +3003,9 @@ client = FernLangfuse( ) client.score.create( request=CreateScoreRequest( - name="novelty", - value=0.9, - trace_id="cdef-1234-5678-90ab", + trace_id="traceId", + name="name", + value=1.1, ), ) @@ -3002,9 +3070,6 @@ Get a list of scores
```python -import datetime - -from langfuse import ScoreDataType, ScoreSource from langfuse.client import FernLangfuse client = FernLangfuse( @@ -3015,26 +3080,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.score.get( - page=1, - limit=1, - user_id="string", - name="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - source=ScoreSource.ANNOTATION, - operator="string", - value=1.1, - score_ids="string", - config_id="string", - queue_id="string", - data_type=ScoreDataType.NUMERIC, - trace_tags="string", -) +client.score.get() ```
@@ -3098,6 +3144,14 @@ client.score.get(
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for scores where the environment is one of the provided values. + +
+
+ +
+
+ **source:** `typing.Optional[ScoreSource]` — Retrieve only scores from a specific source.
@@ -3212,7 +3266,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.score.get_by_id( - score_id="string", + score_id="scoreId", ) ``` @@ -3287,7 +3341,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.score.delete( - score_id="string", + score_id="scoreId", ) ``` @@ -3352,8 +3406,6 @@ Get sessions
```python -import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -3364,16 +3416,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.sessions.list( - page=1, - limit=1, - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), -) +client.sessions.list() ```
@@ -3421,6 +3464,14 @@ client.sessions.list(
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for sessions where the environment is one of the provided values. + +
+
+ +
+
+ **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -3471,7 +3522,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.sessions.get( - session_id="string", + session_id="sessionId", ) ``` @@ -3547,7 +3598,7 @@ client = FernLangfuse( base_url="https://yourhost.com/path/to/api", ) client.trace.get( - trace_id="string", + trace_id="traceId", ) ``` @@ -3580,6 +3631,81 @@ client.trace.get(
+
+
+
+ +
client.trace.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a specific trace +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", +) +client.trace.delete( + trace_id="traceId", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**trace_id:** `str` — The unique langfuse identifier of the trace to delete + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -3611,8 +3737,6 @@ Get list of traces
```python -import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -3623,23 +3747,7 @@ client = FernLangfuse( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) -client.trace.list( - page=1, - limit=1, - user_id="string", - name="string", - session_id="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - order_by="string", - tags="string", - version="string", - release="string", -) +client.trace.list() ```
@@ -3743,6 +3851,89 @@ client.trace.list(
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for traces where the environment is one of the provided values. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + + + + + + + +
client.trace.delete_multiple(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete multiple traces +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", +) +client.trace.delete_multiple( + trace_ids=["traceIds", "traceIds"], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**trace_ids:** `typing.Sequence[str]` — List of trace IDs to delete + +
+
+ +
+
+ **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
diff --git a/langfuse/api/resources/__init__.py b/langfuse/api/resources/__init__.py index 6ebdcbe69..bc0dfbfef 100644 --- a/langfuse/api/resources/__init__.py +++ b/langfuse/api/resources/__init__.py @@ -62,9 +62,18 @@ UnauthorizedError, Usage, ) -from .dataset_items import CreateDatasetItemRequest, PaginatedDatasetItems +from .dataset_items import ( + CreateDatasetItemRequest, + DeleteDatasetItemResponse, + PaginatedDatasetItems, +) from .dataset_run_items import CreateDatasetRunItemRequest -from .datasets import CreateDatasetRequest, PaginatedDatasetRuns, PaginatedDatasets +from .datasets import ( + CreateDatasetRequest, + DeleteDatasetRunResponse, + PaginatedDatasetRuns, + PaginatedDatasets, +) from .health import HealthResponse, ServiceUnavailableError from .ingestion import ( BaseEvent, @@ -92,8 +101,9 @@ IngestionUsage, ObservationBody, ObservationType, + OpenAiCompletionUsageSchema, + OpenAiResponseUsageSchema, OpenAiUsage, - OpenAiUsageSchema, OptionalObservationBody, ScoreBody, ScoreEvent, @@ -151,7 +161,7 @@ ) from .score_configs import CreateScoreConfigRequest, ScoreConfigs from .sessions import PaginatedSessions -from .trace import Sort, Traces +from .trace import DeleteTraceResponse, Sort, Traces __all__ = [ "AccessDeniedError", @@ -195,6 +205,9 @@ "DatasetRunItem", "DatasetRunWithItems", "DatasetStatus", + "DeleteDatasetItemResponse", + "DeleteDatasetRunResponse", + "DeleteTraceResponse", "Error", "GetCommentsResponse", "GetMediaResponse", @@ -239,8 +252,9 @@ "Observations", "ObservationsView", "ObservationsViews", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", "OpenAiUsage", - "OpenAiUsageSchema", "OptionalObservationBody", "PaginatedDatasetItems", "PaginatedDatasetRuns", diff --git a/langfuse/api/resources/comments/client.py b/langfuse/api/resources/comments/client.py index 9d84948a3..9c78ca23f 100644 --- a/langfuse/api/resources/comments/client.py +++ b/langfuse/api/resources/comments/client.py @@ -61,11 +61,10 @@ def create( ) client.comments.create( request=CreateCommentRequest( - project_id="string", - object_type="string", - object_id="string", - content="string", - author_user_id="string", + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", ), ) """ @@ -151,13 +150,7 @@ def get( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.comments.get( - page=1, - limit=1, - object_type="string", - object_id="string", - author_user_id="string", - ) + client.comments.get() """ _response = self._client_wrapper.httpx_client.request( "api/public/comments", @@ -231,7 +224,7 @@ def get_by_id( base_url="https://yourhost.com/path/to/api", ) client.comments.get_by_id( - comment_id="string", + comment_id="commentId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -310,11 +303,10 @@ async def create( async def main() -> None: await client.comments.create( request=CreateCommentRequest( - project_id="string", - object_type="string", - object_id="string", - content="string", - author_user_id="string", + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", ), ) @@ -408,13 +400,7 @@ async def get( async def main() -> None: - await client.comments.get( - page=1, - limit=1, - object_type="string", - object_id="string", - author_user_id="string", - ) + await client.comments.get() asyncio.run(main()) @@ -496,7 +482,7 @@ async def get_by_id( async def main() -> None: await client.comments.get_by_id( - comment_id="string", + comment_id="commentId", ) diff --git a/langfuse/api/resources/commons/types/session.py b/langfuse/api/resources/commons/types/session.py index 230bf004b..46a0a6b96 100644 --- a/langfuse/api/resources/commons/types/session.py +++ b/langfuse/api/resources/commons/types/session.py @@ -11,6 +11,10 @@ class Session(pydantic_v1.BaseModel): id: str created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") project_id: str = pydantic_v1.Field(alias="projectId") + environment: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + The environment from which this session originated. + """ def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/langfuse/api/resources/dataset_items/__init__.py b/langfuse/api/resources/dataset_items/__init__.py index 2a0693046..06d2ae527 100644 --- a/langfuse/api/resources/dataset_items/__init__.py +++ b/langfuse/api/resources/dataset_items/__init__.py @@ -1,5 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -from .types import CreateDatasetItemRequest, PaginatedDatasetItems +from .types import ( + CreateDatasetItemRequest, + DeleteDatasetItemResponse, + PaginatedDatasetItems, +) -__all__ = ["CreateDatasetItemRequest", "PaginatedDatasetItems"] +__all__ = [ + "CreateDatasetItemRequest", + "DeleteDatasetItemResponse", + "PaginatedDatasetItems", +] diff --git a/langfuse/api/resources/dataset_items/client.py b/langfuse/api/resources/dataset_items/client.py index 7cab6f51c..8ece3a790 100644 --- a/langfuse/api/resources/dataset_items/client.py +++ b/langfuse/api/resources/dataset_items/client.py @@ -15,6 +15,7 @@ from ..commons.errors.unauthorized_error import UnauthorizedError from ..commons.types.dataset_item import DatasetItem from .types.create_dataset_item_request import CreateDatasetItemRequest +from .types.delete_dataset_item_response import DeleteDatasetItemResponse from .types.paginated_dataset_items import PaginatedDatasetItems # this is used as the default value for optional parameters @@ -47,7 +48,7 @@ def create( Examples -------- - from langfuse import CreateDatasetItemRequest, DatasetStatus + from langfuse import CreateDatasetItemRequest from langfuse.client import FernLangfuse client = FernLangfuse( @@ -60,14 +61,7 @@ def create( ) client.dataset_items.create( request=CreateDatasetItemRequest( - dataset_name="string", - input={"key": "value"}, - expected_output={"key": "value"}, - metadata={"key": "value"}, - source_trace_id="string", - source_observation_id="string", - id="string", - status=DatasetStatus.ACTIVE, + dataset_name="datasetName", ), ) """ @@ -134,7 +128,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.dataset_items.get( - id="string", + id="id", ) """ _response = self._client_wrapper.httpx_client.request( @@ -214,13 +208,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.dataset_items.list( - dataset_name="string", - source_trace_id="string", - source_observation_id="string", - page=1, - limit=1, - ) + client.dataset_items.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/dataset-items", @@ -260,6 +248,72 @@ def list( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDatasetItemResponse: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetItemResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.dataset_items.delete( + id="id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + DeleteDatasetItemResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncDatasetItemsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -289,7 +343,7 @@ async def create( -------- import asyncio - from langfuse import CreateDatasetItemRequest, DatasetStatus + from langfuse import CreateDatasetItemRequest from langfuse.client import AsyncFernLangfuse client = AsyncFernLangfuse( @@ -305,14 +359,7 @@ async def create( async def main() -> None: await client.dataset_items.create( request=CreateDatasetItemRequest( - dataset_name="string", - input={"key": "value"}, - expected_output={"key": "value"}, - metadata={"key": "value"}, - source_trace_id="string", - source_observation_id="string", - id="string", - status=DatasetStatus.ACTIVE, + dataset_name="datasetName", ), ) @@ -387,7 +434,7 @@ async def get( async def main() -> None: await client.dataset_items.get( - id="string", + id="id", ) @@ -475,13 +522,7 @@ async def list( async def main() -> None: - await client.dataset_items.list( - dataset_name="string", - source_trace_id="string", - source_observation_id="string", - page=1, - limit=1, - ) + await client.dataset_items.list() asyncio.run(main()) @@ -523,3 +564,77 @@ async def main() -> None: except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDatasetItemResponse: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetItemResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.dataset_items.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + DeleteDatasetItemResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/langfuse/api/resources/dataset_items/types/__init__.py b/langfuse/api/resources/dataset_items/types/__init__.py index 24023de7b..214adce0e 100644 --- a/langfuse/api/resources/dataset_items/types/__init__.py +++ b/langfuse/api/resources/dataset_items/types/__init__.py @@ -1,6 +1,11 @@ # This file was auto-generated by Fern from our API Definition. from .create_dataset_item_request import CreateDatasetItemRequest +from .delete_dataset_item_response import DeleteDatasetItemResponse from .paginated_dataset_items import PaginatedDatasetItems -__all__ = ["CreateDatasetItemRequest", "PaginatedDatasetItems"] +__all__ = [ + "CreateDatasetItemRequest", + "DeleteDatasetItemResponse", + "PaginatedDatasetItems", +] diff --git a/langfuse/api/resources/dataset_items/types/delete_dataset_item_response.py b/langfuse/api/resources/dataset_items/types/delete_dataset_item_response.py new file mode 100644 index 000000000..4d700ff75 --- /dev/null +++ b/langfuse/api/resources/dataset_items/types/delete_dataset_item_response.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class DeleteDatasetItemResponse(pydantic_v1.BaseModel): + message: str = pydantic_v1.Field() + """ + Success message after deletion + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/dataset_run_items/client.py b/langfuse/api/resources/dataset_run_items/client.py index c4c923a9b..4f5426568 100644 --- a/langfuse/api/resources/dataset_run_items/client.py +++ b/langfuse/api/resources/dataset_run_items/client.py @@ -58,12 +58,8 @@ def create( ) client.dataset_run_items.create( request=CreateDatasetRunItemRequest( - run_name="string", - run_description="string", - metadata={"key": "value"}, - dataset_item_id="string", - observation_id="string", - trace_id="string", + run_name="runName", + dataset_item_id="datasetItemId", ), ) """ @@ -145,12 +141,8 @@ async def create( async def main() -> None: await client.dataset_run_items.create( request=CreateDatasetRunItemRequest( - run_name="string", - run_description="string", - metadata={"key": "value"}, - dataset_item_id="string", - observation_id="string", - trace_id="string", + run_name="runName", + dataset_item_id="datasetItemId", ), ) diff --git a/langfuse/api/resources/datasets/__init__.py b/langfuse/api/resources/datasets/__init__.py index d8a7fab0e..dd30a359d 100644 --- a/langfuse/api/resources/datasets/__init__.py +++ b/langfuse/api/resources/datasets/__init__.py @@ -1,5 +1,15 @@ # This file was auto-generated by Fern from our API Definition. -from .types import CreateDatasetRequest, PaginatedDatasetRuns, PaginatedDatasets +from .types import ( + CreateDatasetRequest, + DeleteDatasetRunResponse, + PaginatedDatasetRuns, + PaginatedDatasets, +) -__all__ = ["CreateDatasetRequest", "PaginatedDatasetRuns", "PaginatedDatasets"] +__all__ = [ + "CreateDatasetRequest", + "DeleteDatasetRunResponse", + "PaginatedDatasetRuns", + "PaginatedDatasets", +] diff --git a/langfuse/api/resources/datasets/client.py b/langfuse/api/resources/datasets/client.py index 5d535b949..aff7293a0 100644 --- a/langfuse/api/resources/datasets/client.py +++ b/langfuse/api/resources/datasets/client.py @@ -16,6 +16,7 @@ from ..commons.types.dataset import Dataset from ..commons.types.dataset_run_with_items import DatasetRunWithItems from .types.create_dataset_request import CreateDatasetRequest +from .types.delete_dataset_run_response import DeleteDatasetRunResponse from .types.paginated_dataset_runs import PaginatedDatasetRuns from .types.paginated_datasets import PaginatedDatasets @@ -64,10 +65,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.datasets.list( - page=1, - limit=1, - ) + client.datasets.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/v2/datasets", @@ -134,7 +132,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.datasets.get( - dataset_name="string", + dataset_name="datasetName", ) """ _response = self._client_wrapper.httpx_client.request( @@ -203,9 +201,7 @@ def create( ) client.datasets.create( request=CreateDatasetRequest( - name="string", - description="string", - metadata={"key": "value"}, + name="name", ), ) """ @@ -278,8 +274,8 @@ def get_run( base_url="https://yourhost.com/path/to/api", ) client.datasets.get_run( - dataset_name="string", - run_name="string", + dataset_name="datasetName", + run_name="runName", ) """ _response = self._client_wrapper.httpx_client.request( @@ -313,6 +309,79 @@ def get_run( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDatasetRunResponse: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetRunResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.datasets.delete_run( + dataset_name="datasetName", + run_name="runName", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + DeleteDatasetRunResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def get_runs( self, dataset_name: str, @@ -354,9 +423,7 @@ def get_runs( base_url="https://yourhost.com/path/to/api", ) client.datasets.get_runs( - dataset_name="string", - page=1, - limit=1, + dataset_name="datasetName", ) """ _response = self._client_wrapper.httpx_client.request( @@ -438,10 +505,7 @@ async def list( async def main() -> None: - await client.datasets.list( - page=1, - limit=1, - ) + await client.datasets.list() asyncio.run(main()) @@ -516,7 +580,7 @@ async def get( async def main() -> None: await client.datasets.get( - dataset_name="string", + dataset_name="datasetName", ) @@ -593,9 +657,7 @@ async def create( async def main() -> None: await client.datasets.create( request=CreateDatasetRequest( - name="string", - description="string", - metadata={"key": "value"}, + name="name", ), ) @@ -676,8 +738,8 @@ async def get_run( async def main() -> None: await client.datasets.get_run( - dataset_name="string", - run_name="string", + dataset_name="datasetName", + run_name="runName", ) @@ -714,6 +776,87 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDatasetRunResponse: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetRunResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.datasets.delete_run( + dataset_name="datasetName", + run_name="runName", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + DeleteDatasetRunResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def get_runs( self, dataset_name: str, @@ -760,9 +903,7 @@ async def get_runs( async def main() -> None: await client.datasets.get_runs( - dataset_name="string", - page=1, - limit=1, + dataset_name="datasetName", ) diff --git a/langfuse/api/resources/datasets/types/__init__.py b/langfuse/api/resources/datasets/types/__init__.py index befecc4c0..f3304a59f 100644 --- a/langfuse/api/resources/datasets/types/__init__.py +++ b/langfuse/api/resources/datasets/types/__init__.py @@ -1,7 +1,13 @@ # This file was auto-generated by Fern from our API Definition. from .create_dataset_request import CreateDatasetRequest +from .delete_dataset_run_response import DeleteDatasetRunResponse from .paginated_dataset_runs import PaginatedDatasetRuns from .paginated_datasets import PaginatedDatasets -__all__ = ["CreateDatasetRequest", "PaginatedDatasetRuns", "PaginatedDatasets"] +__all__ = [ + "CreateDatasetRequest", + "DeleteDatasetRunResponse", + "PaginatedDatasetRuns", + "PaginatedDatasets", +] diff --git a/langfuse/api/resources/datasets/types/delete_dataset_run_response.py b/langfuse/api/resources/datasets/types/delete_dataset_run_response.py new file mode 100644 index 000000000..cf52eca14 --- /dev/null +++ b/langfuse/api/resources/datasets/types/delete_dataset_run_response.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class DeleteDatasetRunResponse(pydantic_v1.BaseModel): + message: str + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/ingestion/__init__.py b/langfuse/api/resources/ingestion/__init__.py index 6bd1373be..9e072dc17 100644 --- a/langfuse/api/resources/ingestion/__init__.py +++ b/langfuse/api/resources/ingestion/__init__.py @@ -26,8 +26,9 @@ IngestionUsage, ObservationBody, ObservationType, + OpenAiCompletionUsageSchema, + OpenAiResponseUsageSchema, OpenAiUsage, - OpenAiUsageSchema, OptionalObservationBody, ScoreBody, ScoreEvent, @@ -70,8 +71,9 @@ "IngestionUsage", "ObservationBody", "ObservationType", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", "OpenAiUsage", - "OpenAiUsageSchema", "OptionalObservationBody", "ScoreBody", "ScoreEvent", diff --git a/langfuse/api/resources/ingestion/client.py b/langfuse/api/resources/ingestion/client.py index 7fd19ea06..9d6784856 100644 --- a/langfuse/api/resources/ingestion/client.py +++ b/langfuse/api/resources/ingestion/client.py @@ -42,7 +42,6 @@ def batch( I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: - - Introduction to data model: https://langfuse.com/docs/tracing-data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors. @@ -87,6 +86,7 @@ def batch( timestamp=datetime.datetime.fromisoformat( "2022-01-01 00:00:00+00:00", ), + environment="production", name="My Trace", user_id="1234-5678-90ab-cdef", input="My input", @@ -159,7 +159,6 @@ async def batch( I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: - - Introduction to data model: https://langfuse.com/docs/tracing-data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors. @@ -208,6 +207,7 @@ async def main() -> None: timestamp=datetime.datetime.fromisoformat( "2022-01-01 00:00:00+00:00", ), + environment="production", name="My Trace", user_id="1234-5678-90ab-cdef", input="My input", diff --git a/langfuse/api/resources/ingestion/types/__init__.py b/langfuse/api/resources/ingestion/types/__init__.py index 95fa2559e..a3490e4dc 100644 --- a/langfuse/api/resources/ingestion/types/__init__.py +++ b/langfuse/api/resources/ingestion/types/__init__.py @@ -27,8 +27,9 @@ from .ingestion_usage import IngestionUsage from .observation_body import ObservationBody from .observation_type import ObservationType +from .open_ai_completion_usage_schema import OpenAiCompletionUsageSchema +from .open_ai_response_usage_schema import OpenAiResponseUsageSchema from .open_ai_usage import OpenAiUsage -from .open_ai_usage_schema import OpenAiUsageSchema from .optional_observation_body import OptionalObservationBody from .score_body import ScoreBody from .score_event import ScoreEvent @@ -70,8 +71,9 @@ "IngestionUsage", "ObservationBody", "ObservationType", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", "OpenAiUsage", - "OpenAiUsageSchema", "OptionalObservationBody", "ScoreBody", "ScoreEvent", diff --git a/langfuse/api/resources/ingestion/types/open_ai_completion_usage_schema.py b/langfuse/api/resources/ingestion/types/open_ai_completion_usage_schema.py new file mode 100644 index 000000000..461c8eee4 --- /dev/null +++ b/langfuse/api/resources/ingestion/types/open_ai_completion_usage_schema.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class OpenAiCompletionUsageSchema(pydantic_v1.BaseModel): + """ + OpenAI Usage schema from (Chat-)Completion APIs + """ + + prompt_tokens: int + completion_tokens: int + total_tokens: int + prompt_tokens_details: typing.Optional[typing.Dict[str, int]] = None + completion_tokens_details: typing.Optional[typing.Dict[str, int]] = None + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/ingestion/types/open_ai_response_usage_schema.py b/langfuse/api/resources/ingestion/types/open_ai_response_usage_schema.py new file mode 100644 index 000000000..99b0bbfa0 --- /dev/null +++ b/langfuse/api/resources/ingestion/types/open_ai_response_usage_schema.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class OpenAiResponseUsageSchema(pydantic_v1.BaseModel): + """ + OpenAI Usage schema from Response API + """ + + input_tokens: int + output_tokens: int + total_tokens: int + input_tokens_details: typing.Optional[typing.Dict[str, int]] = None + output_tokens_details: typing.Optional[typing.Dict[str, int]] = None + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/ingestion/types/usage_details.py b/langfuse/api/resources/ingestion/types/usage_details.py index 89c0fc2e9..beb98cbd0 100644 --- a/langfuse/api/resources/ingestion/types/usage_details.py +++ b/langfuse/api/resources/ingestion/types/usage_details.py @@ -2,6 +2,9 @@ import typing -from .open_ai_usage_schema import OpenAiUsageSchema +from .open_ai_completion_usage_schema import OpenAiCompletionUsageSchema +from .open_ai_response_usage_schema import OpenAiResponseUsageSchema -UsageDetails = typing.Union[typing.Dict[str, int], OpenAiUsageSchema] +UsageDetails = typing.Union[ + typing.Dict[str, int], OpenAiCompletionUsageSchema, OpenAiResponseUsageSchema +] diff --git a/langfuse/api/resources/media/client.py b/langfuse/api/resources/media/client.py index 0dcd75e8e..bb8e4b149 100644 --- a/langfuse/api/resources/media/client.py +++ b/langfuse/api/resources/media/client.py @@ -57,7 +57,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.media.get( - media_id="string", + media_id="mediaId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -131,14 +131,12 @@ def patch( base_url="https://yourhost.com/path/to/api", ) client.media.patch( - media_id="string", + media_id="mediaId", request=PatchMediaBody( uploaded_at=datetime.datetime.fromisoformat( "2024-01-15 09:30:00+00:00", ), upload_http_status=1, - upload_http_error="string", - upload_time_ms=1, ), ) """ @@ -210,12 +208,11 @@ def get_upload_url( ) client.media.get_upload_url( request=GetMediaUploadUrlRequest( - trace_id="string", - observation_id="string", + trace_id="traceId", content_type=MediaContentType.IMAGE_PNG, content_length=1, - sha_256_hash="string", - field="string", + sha_256_hash="sha256Hash", + field="field", ), ) """ @@ -295,7 +292,7 @@ async def get( async def main() -> None: await client.media.get( - media_id="string", + media_id="mediaId", ) @@ -376,14 +373,12 @@ async def patch( async def main() -> None: await client.media.patch( - media_id="string", + media_id="mediaId", request=PatchMediaBody( uploaded_at=datetime.datetime.fromisoformat( "2024-01-15 09:30:00+00:00", ), upload_http_status=1, - upload_http_error="string", - upload_time_ms=1, ), ) @@ -463,12 +458,11 @@ async def get_upload_url( async def main() -> None: await client.media.get_upload_url( request=GetMediaUploadUrlRequest( - trace_id="string", - observation_id="string", + trace_id="traceId", content_type=MediaContentType.IMAGE_PNG, content_length=1, - sha_256_hash="string", - field="string", + sha_256_hash="sha256Hash", + field="field", ), ) diff --git a/langfuse/api/resources/metrics/client.py b/langfuse/api/resources/metrics/client.py index 6447b1133..9ae8666c9 100644 --- a/langfuse/api/resources/metrics/client.py +++ b/langfuse/api/resources/metrics/client.py @@ -29,6 +29,7 @@ def daily( trace_name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, @@ -53,6 +54,9 @@ def daily( tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] Optional filter for metrics where traces include all of these tags + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for metrics where events include any of these environments + from_timestamp : typing.Optional[dt.datetime] Optional filter to only include traces and observations on or after a certain datetime (ISO 8601) @@ -68,8 +72,6 @@ def daily( Examples -------- - import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -80,19 +82,7 @@ def daily( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.metrics.daily( - page=1, - limit=1, - trace_name="string", - user_id="string", - tags="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + client.metrics.daily() """ _response = self._client_wrapper.httpx_client.request( "api/public/metrics/daily", @@ -103,6 +93,7 @@ def daily( "traceName": trace_name, "userId": user_id, "tags": tags, + "environment": environment, "fromTimestamp": serialize_datetime(from_timestamp) if from_timestamp is not None else None, @@ -151,6 +142,7 @@ async def daily( trace_name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, request_options: typing.Optional[RequestOptions] = None, @@ -175,6 +167,9 @@ async def daily( tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] Optional filter for metrics where traces include all of these tags + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for metrics where events include any of these environments + from_timestamp : typing.Optional[dt.datetime] Optional filter to only include traces and observations on or after a certain datetime (ISO 8601) @@ -191,7 +186,6 @@ async def daily( Examples -------- import asyncio - import datetime from langfuse.client import AsyncFernLangfuse @@ -206,19 +200,7 @@ async def daily( async def main() -> None: - await client.metrics.daily( - page=1, - limit=1, - trace_name="string", - user_id="string", - tags="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + await client.metrics.daily() asyncio.run(main()) @@ -232,6 +214,7 @@ async def main() -> None: "traceName": trace_name, "userId": user_id, "tags": tags, + "environment": environment, "fromTimestamp": serialize_datetime(from_timestamp) if from_timestamp is not None else None, diff --git a/langfuse/api/resources/models/client.py b/langfuse/api/resources/models/client.py index 1ebac9d07..4f4b727fa 100644 --- a/langfuse/api/resources/models/client.py +++ b/langfuse/api/resources/models/client.py @@ -47,9 +47,7 @@ def create( Examples -------- - import datetime - - from langfuse import CreateModelRequest, ModelUsageUnit + from langfuse import CreateModelRequest from langfuse.client import FernLangfuse client = FernLangfuse( @@ -62,17 +60,8 @@ def create( ) client.models.create( request=CreateModelRequest( - model_name="string", - match_pattern="string", - start_date=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - unit=ModelUsageUnit.CHARACTERS, - input_price=1.1, - output_price=1.1, - total_price=1.1, - tokenizer_id="string", - tokenizer_config={"key": "value"}, + model_name="modelName", + match_pattern="matchPattern", ), ) """ @@ -146,10 +135,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.models.list( - page=1, - limit=1, - ) + client.models.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/models", @@ -213,7 +199,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.models.get( - id="string", + id="id", ) """ _response = self._client_wrapper.httpx_client.request( @@ -277,7 +263,7 @@ def delete( base_url="https://yourhost.com/path/to/api", ) client.models.delete( - id="string", + id="id", ) """ _response = self._client_wrapper.httpx_client.request( @@ -339,9 +325,8 @@ async def create( Examples -------- import asyncio - import datetime - from langfuse import CreateModelRequest, ModelUsageUnit + from langfuse import CreateModelRequest from langfuse.client import AsyncFernLangfuse client = AsyncFernLangfuse( @@ -357,17 +342,8 @@ async def create( async def main() -> None: await client.models.create( request=CreateModelRequest( - model_name="string", - match_pattern="string", - start_date=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - unit=ModelUsageUnit.CHARACTERS, - input_price=1.1, - output_price=1.1, - total_price=1.1, - tokenizer_id="string", - tokenizer_config={"key": "value"}, + model_name="modelName", + match_pattern="matchPattern", ), ) @@ -449,10 +425,7 @@ async def list( async def main() -> None: - await client.models.list( - page=1, - limit=1, - ) + await client.models.list() asyncio.run(main()) @@ -524,7 +497,7 @@ async def get( async def main() -> None: await client.models.get( - id="string", + id="id", ) @@ -596,7 +569,7 @@ async def delete( async def main() -> None: await client.models.delete( - id="string", + id="id", ) diff --git a/langfuse/api/resources/observations/client.py b/langfuse/api/resources/observations/client.py index d937841b9..180a48949 100644 --- a/langfuse/api/resources/observations/client.py +++ b/langfuse/api/resources/observations/client.py @@ -57,7 +57,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.observations.get( - observation_id="string", + observation_id="observationId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -101,6 +101,7 @@ def get_many( type: typing.Optional[str] = None, trace_id: typing.Optional[str] = None, parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, from_start_time: typing.Optional[dt.datetime] = None, to_start_time: typing.Optional[dt.datetime] = None, version: typing.Optional[str] = None, @@ -127,6 +128,9 @@ def get_many( parent_observation_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + from_start_time : typing.Optional[dt.datetime] Retrieve only observations with a start_time or or after this datetime (ISO 8601). @@ -145,8 +149,6 @@ def get_many( Examples -------- - import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -157,22 +159,7 @@ def get_many( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.observations.get_many( - page=1, - limit=1, - name="string", - user_id="string", - type="string", - trace_id="string", - parent_observation_id="string", - from_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - version="string", - ) + client.observations.get_many() """ _response = self._client_wrapper.httpx_client.request( "api/public/observations", @@ -185,6 +172,7 @@ def get_many( "type": type, "traceId": trace_id, "parentObservationId": parent_observation_id, + "environment": environment, "fromStartTime": serialize_datetime(from_start_time) if from_start_time is not None else None, @@ -265,7 +253,7 @@ async def get( async def main() -> None: await client.observations.get( - observation_id="string", + observation_id="observationId", ) @@ -312,6 +300,7 @@ async def get_many( type: typing.Optional[str] = None, trace_id: typing.Optional[str] = None, parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, from_start_time: typing.Optional[dt.datetime] = None, to_start_time: typing.Optional[dt.datetime] = None, version: typing.Optional[str] = None, @@ -338,6 +327,9 @@ async def get_many( parent_observation_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + from_start_time : typing.Optional[dt.datetime] Retrieve only observations with a start_time or or after this datetime (ISO 8601). @@ -357,7 +349,6 @@ async def get_many( Examples -------- import asyncio - import datetime from langfuse.client import AsyncFernLangfuse @@ -372,22 +363,7 @@ async def get_many( async def main() -> None: - await client.observations.get_many( - page=1, - limit=1, - name="string", - user_id="string", - type="string", - trace_id="string", - parent_observation_id="string", - from_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_start_time=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - version="string", - ) + await client.observations.get_many() asyncio.run(main()) @@ -403,6 +379,7 @@ async def main() -> None: "type": type, "traceId": trace_id, "parentObservationId": parent_observation_id, + "environment": environment, "fromStartTime": serialize_datetime(from_start_time) if from_start_time is not None else None, diff --git a/langfuse/api/resources/prompt_version/client.py b/langfuse/api/resources/prompt_version/client.py index 638871082..89140941d 100644 --- a/langfuse/api/resources/prompt_version/client.py +++ b/langfuse/api/resources/prompt_version/client.py @@ -65,9 +65,9 @@ def update( base_url="https://yourhost.com/path/to/api", ) client.prompt_version.update( - name="string", + name="name", version=1, - new_labels=["string"], + new_labels=["newLabels", "newLabels"], ) """ _response = self._client_wrapper.httpx_client.request( @@ -155,9 +155,9 @@ async def update( async def main() -> None: await client.prompt_version.update( - name="string", + name="name", version=1, - new_labels=["string"], + new_labels=["newLabels", "newLabels"], ) diff --git a/langfuse/api/resources/prompts/client.py b/langfuse/api/resources/prompts/client.py index 5ca4c4aa5..b41ab5642 100644 --- a/langfuse/api/resources/prompts/client.py +++ b/langfuse/api/resources/prompts/client.py @@ -69,9 +69,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.prompts.get( - prompt_name="string", - version=1, - label="string", + prompt_name="promptName", ) """ _response = self._client_wrapper.httpx_client.request( @@ -150,8 +148,6 @@ def list( Examples -------- - import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -162,19 +158,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.prompts.list( - name="string", - label="string", - tag="string", - page=1, - limit=1, - from_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + client.prompts.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/v2/prompts", @@ -257,17 +241,17 @@ def create( ) client.prompts.create( request=CreatePromptRequest_Chat( - name="string", + name="name", prompt=[ ChatMessage( - role="string", - content="string", - ) + role="role", + content="content", + ), + ChatMessage( + role="role", + content="content", + ), ], - config={"key": "value"}, - labels=["string"], - tags=["string"], - commit_message="string", ), ) """ @@ -356,9 +340,7 @@ async def get( async def main() -> None: await client.prompts.get( - prompt_name="string", - version=1, - label="string", + prompt_name="promptName", ) @@ -441,7 +423,6 @@ async def list( Examples -------- import asyncio - import datetime from langfuse.client import AsyncFernLangfuse @@ -456,19 +437,7 @@ async def list( async def main() -> None: - await client.prompts.list( - name="string", - label="string", - tag="string", - page=1, - limit=1, - from_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_updated_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + await client.prompts.list() asyncio.run(main()) @@ -559,17 +528,17 @@ async def create( async def main() -> None: await client.prompts.create( request=CreatePromptRequest_Chat( - name="string", + name="name", prompt=[ ChatMessage( - role="string", - content="string", - ) + role="role", + content="content", + ), + ChatMessage( + role="role", + content="content", + ), ], - config={"key": "value"}, - labels=["string"], - tags=["string"], - commit_message="string", ), ) diff --git a/langfuse/api/resources/prompts/types/base_prompt.py b/langfuse/api/resources/prompts/types/base_prompt.py index 576110b75..eff295cc5 100644 --- a/langfuse/api/resources/prompts/types/base_prompt.py +++ b/langfuse/api/resources/prompts/types/base_prompt.py @@ -28,6 +28,13 @@ class BasePrompt(pydantic_v1.BaseModel): Commit message for this prompt version. """ + resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( + alias="resolutionGraph", default=None + ) + """ + The dependency resolution graph for the current prompt. Null if prompt has no dependencies. + """ + def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { "by_alias": True, diff --git a/langfuse/api/resources/prompts/types/prompt.py b/langfuse/api/resources/prompts/types/prompt.py index 9c91b85d8..2fee54b70 100644 --- a/langfuse/api/resources/prompts/types/prompt.py +++ b/langfuse/api/resources/prompts/types/prompt.py @@ -20,6 +20,9 @@ class Prompt_Chat(pydantic_v1.BaseModel): commit_message: typing.Optional[str] = pydantic_v1.Field( alias="commitMessage", default=None ) + resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( + alias="resolutionGraph", default=None + ) type: typing.Literal["chat"] = "chat" def json(self, **kwargs: typing.Any) -> str: @@ -66,6 +69,9 @@ class Prompt_Text(pydantic_v1.BaseModel): commit_message: typing.Optional[str] = pydantic_v1.Field( alias="commitMessage", default=None ) + resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( + alias="resolutionGraph", default=None + ) type: typing.Literal["text"] = "text" def json(self, **kwargs: typing.Any) -> str: diff --git a/langfuse/api/resources/score/client.py b/langfuse/api/resources/score/client.py index 9c40a48f7..774f4f218 100644 --- a/langfuse/api/resources/score/client.py +++ b/langfuse/api/resources/score/client.py @@ -65,9 +65,9 @@ def create( ) client.score.create( request=CreateScoreRequest( - name="novelty", - value=0.9, - trace_id="cdef-1234-5678-90ab", + trace_id="traceId", + name="name", + value=1.1, ), ) """ @@ -113,6 +113,7 @@ def get( name: typing.Optional[str] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, source: typing.Optional[ScoreSource] = None, operator: typing.Optional[str] = None, value: typing.Optional[float] = None, @@ -146,6 +147,9 @@ def get( to_timestamp : typing.Optional[dt.datetime] Optional filter to only include scores created before a certain datetime (ISO 8601) + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + source : typing.Optional[ScoreSource] Retrieve only scores from a specific source. @@ -179,9 +183,6 @@ def get( Examples -------- - import datetime - - from langfuse import ScoreDataType, ScoreSource from langfuse.client import FernLangfuse client = FernLangfuse( @@ -192,26 +193,7 @@ def get( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.score.get( - page=1, - limit=1, - user_id="string", - name="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - source=ScoreSource.ANNOTATION, - operator="string", - value=1.1, - score_ids="string", - config_id="string", - queue_id="string", - data_type=ScoreDataType.NUMERIC, - trace_tags="string", - ) + client.score.get() """ _response = self._client_wrapper.httpx_client.request( "api/public/scores", @@ -227,6 +209,7 @@ def get( "toTimestamp": serialize_datetime(to_timestamp) if to_timestamp is not None else None, + "environment": environment, "source": source, "operator": operator, "value": value, @@ -295,7 +278,7 @@ def get_by_id( base_url="https://yourhost.com/path/to/api", ) client.score.get_by_id( - score_id="string", + score_id="scoreId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -360,7 +343,7 @@ def delete( base_url="https://yourhost.com/path/to/api", ) client.score.delete( - score_id="string", + score_id="scoreId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -439,9 +422,9 @@ async def create( async def main() -> None: await client.score.create( request=CreateScoreRequest( - name="novelty", - value=0.9, - trace_id="cdef-1234-5678-90ab", + trace_id="traceId", + name="name", + value=1.1, ), ) @@ -490,6 +473,7 @@ async def get( name: typing.Optional[str] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, source: typing.Optional[ScoreSource] = None, operator: typing.Optional[str] = None, value: typing.Optional[float] = None, @@ -523,6 +507,9 @@ async def get( to_timestamp : typing.Optional[dt.datetime] Optional filter to only include scores created before a certain datetime (ISO 8601) + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + source : typing.Optional[ScoreSource] Retrieve only scores from a specific source. @@ -557,9 +544,7 @@ async def get( Examples -------- import asyncio - import datetime - from langfuse import ScoreDataType, ScoreSource from langfuse.client import AsyncFernLangfuse client = AsyncFernLangfuse( @@ -573,26 +558,7 @@ async def get( async def main() -> None: - await client.score.get( - page=1, - limit=1, - user_id="string", - name="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - source=ScoreSource.ANNOTATION, - operator="string", - value=1.1, - score_ids="string", - config_id="string", - queue_id="string", - data_type=ScoreDataType.NUMERIC, - trace_tags="string", - ) + await client.score.get() asyncio.run(main()) @@ -611,6 +577,7 @@ async def main() -> None: "toTimestamp": serialize_datetime(to_timestamp) if to_timestamp is not None else None, + "environment": environment, "source": source, "operator": operator, "value": value, @@ -684,7 +651,7 @@ async def get_by_id( async def main() -> None: await client.score.get_by_id( - score_id="string", + score_id="scoreId", ) @@ -757,7 +724,7 @@ async def delete( async def main() -> None: await client.score.delete( - score_id="string", + score_id="scoreId", ) diff --git a/langfuse/api/resources/score/types/create_score_request.py b/langfuse/api/resources/score/types/create_score_request.py index 8a76f464e..c11030f4f 100644 --- a/langfuse/api/resources/score/types/create_score_request.py +++ b/langfuse/api/resources/score/types/create_score_request.py @@ -34,6 +34,11 @@ class CreateScoreRequest(pydantic_v1.BaseModel): alias="observationId", default=None ) comment: typing.Optional[str] = None + environment: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + data_type: typing.Optional[ScoreDataType] = pydantic_v1.Field( alias="dataType", default=None ) diff --git a/langfuse/api/resources/score/types/get_scores_response_trace_data.py b/langfuse/api/resources/score/types/get_scores_response_trace_data.py index efbafadf4..6e5539e35 100644 --- a/langfuse/api/resources/score/types/get_scores_response_trace_data.py +++ b/langfuse/api/resources/score/types/get_scores_response_trace_data.py @@ -18,6 +18,11 @@ class GetScoresResponseTraceData(pydantic_v1.BaseModel): A list of tags associated with the trace referenced by score """ + environment: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + The environment of the trace referenced by score + """ + def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { "by_alias": True, diff --git a/langfuse/api/resources/score_configs/client.py b/langfuse/api/resources/score_configs/client.py index 5f8463612..7bd2a72df 100644 --- a/langfuse/api/resources/score_configs/client.py +++ b/langfuse/api/resources/score_configs/client.py @@ -47,7 +47,7 @@ def create( Examples -------- - from langfuse import ConfigCategory, CreateScoreConfigRequest, ScoreDataType + from langfuse import CreateScoreConfigRequest, ScoreDataType from langfuse.client import FernLangfuse client = FernLangfuse( @@ -60,17 +60,8 @@ def create( ) client.score_configs.create( request=CreateScoreConfigRequest( - name="string", + name="name", data_type=ScoreDataType.NUMERIC, - categories=[ - ConfigCategory( - value=1.1, - label="string", - ) - ], - min_value=1.1, - max_value=1.1, - description="string", ), ) """ @@ -144,10 +135,7 @@ def get( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.score_configs.get( - page=1, - limit=1, - ) + client.score_configs.get() """ _response = self._client_wrapper.httpx_client.request( "api/public/score-configs", @@ -212,7 +200,7 @@ def get_by_id( base_url="https://yourhost.com/path/to/api", ) client.score_configs.get_by_id( - config_id="string", + config_id="configId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -275,7 +263,7 @@ async def create( -------- import asyncio - from langfuse import ConfigCategory, CreateScoreConfigRequest, ScoreDataType + from langfuse import CreateScoreConfigRequest, ScoreDataType from langfuse.client import AsyncFernLangfuse client = AsyncFernLangfuse( @@ -291,17 +279,8 @@ async def create( async def main() -> None: await client.score_configs.create( request=CreateScoreConfigRequest( - name="string", + name="name", data_type=ScoreDataType.NUMERIC, - categories=[ - ConfigCategory( - value=1.1, - label="string", - ) - ], - min_value=1.1, - max_value=1.1, - description="string", ), ) @@ -383,10 +362,7 @@ async def get( async def main() -> None: - await client.score_configs.get( - page=1, - limit=1, - ) + await client.score_configs.get() asyncio.run(main()) @@ -459,7 +435,7 @@ async def get_by_id( async def main() -> None: await client.score_configs.get_by_id( - config_id="string", + config_id="configId", ) diff --git a/langfuse/api/resources/sessions/client.py b/langfuse/api/resources/sessions/client.py index 98c56e0fc..d5ae779c3 100644 --- a/langfuse/api/resources/sessions/client.py +++ b/langfuse/api/resources/sessions/client.py @@ -30,6 +30,7 @@ def list( limit: typing.Optional[int] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, ) -> PaginatedSessions: """ @@ -49,6 +50,9 @@ def list( to_timestamp : typing.Optional[dt.datetime] Optional filter to only include sessions created before a certain datetime (ISO 8601) + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -58,8 +62,6 @@ def list( Examples -------- - import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -70,16 +72,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.sessions.list( - page=1, - limit=1, - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + client.sessions.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/sessions", @@ -93,6 +86,7 @@ def list( "toTimestamp": serialize_datetime(to_timestamp) if to_timestamp is not None else None, + "environment": environment, }, request_options=request_options, ) @@ -156,7 +150,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.sessions.get( - session_id="string", + session_id="sessionId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -202,6 +196,7 @@ async def list( limit: typing.Optional[int] = None, from_timestamp: typing.Optional[dt.datetime] = None, to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, ) -> PaginatedSessions: """ @@ -221,6 +216,9 @@ async def list( to_timestamp : typing.Optional[dt.datetime] Optional filter to only include sessions created before a certain datetime (ISO 8601) + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -231,7 +229,6 @@ async def list( Examples -------- import asyncio - import datetime from langfuse.client import AsyncFernLangfuse @@ -246,16 +243,7 @@ async def list( async def main() -> None: - await client.sessions.list( - page=1, - limit=1, - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - ) + await client.sessions.list() asyncio.run(main()) @@ -272,6 +260,7 @@ async def main() -> None: "toTimestamp": serialize_datetime(to_timestamp) if to_timestamp is not None else None, + "environment": environment, }, request_options=request_options, ) @@ -340,7 +329,7 @@ async def get( async def main() -> None: await client.sessions.get( - session_id="string", + session_id="sessionId", ) diff --git a/langfuse/api/resources/trace/__init__.py b/langfuse/api/resources/trace/__init__.py index a2135eb9e..17855e971 100644 --- a/langfuse/api/resources/trace/__init__.py +++ b/langfuse/api/resources/trace/__init__.py @@ -1,5 +1,5 @@ # This file was auto-generated by Fern from our API Definition. -from .types import Sort, Traces +from .types import DeleteTraceResponse, Sort, Traces -__all__ = ["Sort", "Traces"] +__all__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/resources/trace/client.py b/langfuse/api/resources/trace/client.py index 0c33e90d5..ece75f3a6 100644 --- a/langfuse/api/resources/trace/client.py +++ b/langfuse/api/resources/trace/client.py @@ -16,8 +16,12 @@ from ..commons.errors.not_found_error import NotFoundError from ..commons.errors.unauthorized_error import UnauthorizedError from ..commons.types.trace_with_full_details import TraceWithFullDetails +from .types.delete_trace_response import DeleteTraceResponse from .types.traces import Traces +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + class TraceClient: def __init__(self, *, client_wrapper: SyncClientWrapper): @@ -54,7 +58,7 @@ def get( base_url="https://yourhost.com/path/to/api", ) client.trace.get( - trace_id="string", + trace_id="traceId", ) """ _response = self._client_wrapper.httpx_client.request( @@ -88,6 +92,71 @@ def get( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTraceResponse: + """ + Delete a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of the trace to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTraceResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.trace.delete( + trace_id="traceId", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(DeleteTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def list( self, *, @@ -102,6 +171,7 @@ def list( tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, version: typing.Optional[str] = None, release: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Traces: """ @@ -139,6 +209,9 @@ def list( release : typing.Optional[str] Optional filter to only include traces with a certain release. + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for traces where the environment is one of the provided values. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -148,8 +221,6 @@ def list( Examples -------- - import datetime - from langfuse.client import FernLangfuse client = FernLangfuse( @@ -160,23 +231,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.trace.list( - page=1, - limit=1, - user_id="string", - name="string", - session_id="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - order_by="string", - tags="string", - version="string", - release="string", - ) + client.trace.list() """ _response = self._client_wrapper.httpx_client.request( "api/public/traces", @@ -197,6 +252,7 @@ def list( "tags": tags, "version": version, "release": release, + "environment": environment, }, request_options=request_options, ) @@ -226,6 +282,76 @@ def list( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete_multiple( + self, + *, + trace_ids: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteTraceResponse: + """ + Delete multiple traces + + Parameters + ---------- + trace_ids : typing.Sequence[str] + List of trace IDs to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTraceResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.trace.delete_multiple( + trace_ids=["traceIds", "traceIds"], + ) + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/traces", + method="DELETE", + json={"traceIds": trace_ids}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(DeleteTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncTraceClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -267,7 +393,7 @@ async def get( async def main() -> None: await client.trace.get( - trace_id="string", + trace_id="traceId", ) @@ -304,6 +430,79 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def delete( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTraceResponse: + """ + Delete a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of the trace to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTraceResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.trace.delete( + trace_id="traceId", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(DeleteTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def list( self, *, @@ -318,6 +517,7 @@ async def list( tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, version: typing.Optional[str] = None, release: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Traces: """ @@ -355,6 +555,9 @@ async def list( release : typing.Optional[str] Optional filter to only include traces with a certain release. + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for traces where the environment is one of the provided values. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -365,7 +568,6 @@ async def list( Examples -------- import asyncio - import datetime from langfuse.client import AsyncFernLangfuse @@ -380,23 +582,7 @@ async def list( async def main() -> None: - await client.trace.list( - page=1, - limit=1, - user_id="string", - name="string", - session_id="string", - from_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - to_timestamp=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - order_by="string", - tags="string", - version="string", - release="string", - ) + await client.trace.list() asyncio.run(main()) @@ -420,6 +606,7 @@ async def main() -> None: "tags": tags, "version": version, "release": release, + "environment": environment, }, request_options=request_options, ) @@ -448,3 +635,81 @@ async def main() -> None: except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + + async def delete_multiple( + self, + *, + trace_ids: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteTraceResponse: + """ + Delete multiple traces + + Parameters + ---------- + trace_ids : typing.Sequence[str] + List of trace IDs to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTraceResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.trace.delete_multiple( + trace_ids=["traceIds", "traceIds"], + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/traces", + method="DELETE", + json={"traceIds": trace_ids}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(DeleteTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/langfuse/api/resources/trace/types/__init__.py b/langfuse/api/resources/trace/types/__init__.py index 7907a8b54..929a1e047 100644 --- a/langfuse/api/resources/trace/types/__init__.py +++ b/langfuse/api/resources/trace/types/__init__.py @@ -1,6 +1,7 @@ # This file was auto-generated by Fern from our API Definition. +from .delete_trace_response import DeleteTraceResponse from .sort import Sort from .traces import Traces -__all__ = ["Sort", "Traces"] +__all__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/resources/trace/types/delete_trace_response.py b/langfuse/api/resources/trace/types/delete_trace_response.py new file mode 100644 index 000000000..450c894e2 --- /dev/null +++ b/langfuse/api/resources/trace/types/delete_trace_response.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class DeleteTraceResponse(pydantic_v1.BaseModel): + message: str + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/openai.py b/langfuse/openai.py index 03d2585cd..37e3d1e07 100644 --- a/langfuse/openai.py +++ b/langfuse/openai.py @@ -128,6 +128,22 @@ class OpenAiDefinition: sync=False, min_version="1.50.0", ), + OpenAiDefinition( + module="openai.resources.responses", + object="Responses", + method="create", + type="chat", + sync=True, + min_version="1.66.0", + ), + OpenAiDefinition( + module="openai.resources.responses", + object="AsyncResponses", + method="create", + type="chat", + sync=False, + min_version="1.66.0", + ), ] @@ -350,6 +366,10 @@ def _get_langfuse_data_from_kwargs( if resource.type == "completion": prompt = kwargs.get("prompt", None) + + elif resource.object == "Responses": + prompt = kwargs.get("input", None) + elif resource.type == "chat": prompt = _extract_chat_prompt(kwargs) @@ -443,6 +463,7 @@ def _create_langfuse_update( completion_start_time, model=None, usage=None, + metadata=None, ): update = { "end_time": _get_timestamp(), @@ -452,6 +473,9 @@ def _create_langfuse_update( if model is not None: update["model"] = model + if metadata is not None: + update["metadata"] = metadata + if usage is not None: update["usage"] = _parse_usage(usage) @@ -464,7 +488,12 @@ def _parse_usage(usage=None): usage_dict = usage.copy() if isinstance(usage, dict) else usage.__dict__.copy() - for tokens_details in ["prompt_tokens_details", "completion_tokens_details"]: + for tokens_details in [ + "prompt_tokens_details", + "completion_tokens_details", + "input_token_details", + "output_token_details", + ]: if tokens_details in usage_dict and usage_dict[tokens_details] is not None: tokens_details_dict = ( usage_dict[tokens_details] @@ -478,6 +507,33 @@ def _parse_usage(usage=None): return usage_dict +def _extract_streamed_response_api_response(chunks): + completion, model, usage = None, None, None + metadata = {} + + for raw_chunk in chunks: + chunk = raw_chunk.__dict__ + if raw_response := chunk.get("response", None): + usage = chunk.get("usage", None) + response = raw_response.__dict__ + model = response.get("model") + + for key, val in response.items(): + if key not in ["created_at", "model", "output", "usage", "text"]: + metadata[key] = val + + if key == "output": + output = val + if not isinstance(output, list): + completion = output + elif len(output) > 1: + completion = output + elif len(output) == 1: + completion = output[0] + + return (model, completion, usage, metadata) + + def _extract_streamed_openai_response(resource, chunks): completion = defaultdict(str) if resource.type == "chat" else "" model, usage = None, None @@ -587,6 +643,7 @@ def get_response_for_chat(): model, get_response_for_chat() if resource.type == "chat" else completion, usage, + None, ) @@ -597,12 +654,24 @@ def _get_langfuse_data_from_default_response(resource: OpenAiDefinition, respons model = response.get("model", None) or None completion = None + if resource.type == "completion": choices = response.get("choices", []) if len(choices) > 0: choice = choices[-1] completion = choice.text if _is_openai_v1() else choice.get("text", None) + + elif resource.object == "Responses": + output = response.get("output", {}) + + if not isinstance(output, list): + completion = output + elif len(output) > 1: + completion = output + elif len(output) == 1: + completion = output[0] + elif resource.type == "chat": choices = response.get("choices", []) if len(choices) > 0: @@ -890,8 +959,10 @@ def __exit__(self, exc_type, exc_value, traceback): pass def _finalize(self): - model, completion, usage = _extract_streamed_openai_response( - self.resource, self.items + model, completion, usage, metadata = ( + _extract_streamed_response_api_response(self.items) + if self.resource.object == "Responses" + else _extract_streamed_openai_response(self.resource, self.items) ) # Avoiding the trace-update if trace-id is provided by user. @@ -904,6 +975,7 @@ def _finalize(self): self.completion_start_time, model=model, usage=usage, + metadata=metadata, ) @@ -960,8 +1032,10 @@ async def __aexit__(self, exc_type, exc_value, traceback): pass async def _finalize(self): - model, completion, usage = _extract_streamed_openai_response( - self.resource, self.items + model, completion, usage, metadata = ( + _extract_streamed_response_api_response(self.items) + if self.resource.object == "Responses" + else _extract_streamed_openai_response(self.resource, self.items) ) # Avoiding the trace-update if trace-id is provided by user. @@ -974,6 +1048,7 @@ async def _finalize(self): self.completion_start_time, model=model, usage=usage, + metadata=metadata, ) async def close(self) -> None: diff --git a/poetry.lock b/poetry.lock index ea86959f7..52744668f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3235,13 +3235,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.54.3" +version = "1.66.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" files = [ - {file = "openai-1.54.3-py3-none-any.whl", hash = "sha256:f18dbaf09c50d70c4185b892a2a553f80681d1d866323a2da7f7be2f688615d5"}, - {file = "openai-1.54.3.tar.gz", hash = "sha256:7511b74eeb894ac0b0253dc71f087a15d2e4d71d22d0088767205143d880cca6"}, + {file = "openai-1.66.3-py3-none-any.whl", hash = "sha256:a427c920f727711877ab17c11b95f1230b27767ba7a01e5b66102945141ceca9"}, + {file = "openai-1.66.3.tar.gz", hash = "sha256:8dde3aebe2d081258d4159c4cb27bdc13b5bb3f7ea2201d9bd940b9a89faf0c9"}, ] [package.dependencies] @@ -3256,6 +3256,7 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +realtime = ["websockets (>=13,<15)"] [[package]] name = "opentelemetry-api" diff --git a/tests/test_openai.py b/tests/test_openai.py index 31176bfae..ddaec0447 100644 --- a/tests/test_openai.py +++ b/tests/test_openai.py @@ -60,6 +60,7 @@ def test_openai_chat_completion(): assert len(completion.choices) != 0 assert generation.data[0].input == [ { + "annotations": None, "content": "You are an expert mathematician", "audio": None, "function_call": None, @@ -90,6 +91,7 @@ def test_openai_chat_completion(): trace = get_api().trace.get(generation.data[0].trace_id) assert trace.input == [ { + "annotations": None, "content": "You are an expert mathematician", "audio": None, "function_call": None, @@ -1612,3 +1614,234 @@ def test_audio_input_and_output(): "@@@langfuseMedia:type=audio/wav|id=" in generation.data[0].output["audio"]["data"] ) + + +def test_response_api_text_input(): + client = openai.OpenAI() + generation_name = "test_response_api_text_input" + create_uuid()[:8] + + client.responses.create( + name=generation_name, + model="gpt-4o", + input="Tell me a three sentence bedtime story about a unicorn.", + ) + + openai.flush_langfuse() + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert ( + generation.data[0].input + == "Tell me a three sentence bedtime story about a unicorn." + ) + assert generationData.type == "GENERATION" + assert "gpt-4o" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + + +def test_response_api_image_input(): + client = openai.OpenAI() + generation_name = "test_response_api_image_input" + create_uuid()[:8] + + client.responses.create( + name=generation_name, + model="gpt-4o", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": "what is in this image?"}, + { + "type": "input_image", + "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", + }, + ], + } + ], + ) + + openai.flush_langfuse() + + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert generation.data[0].input[0]["content"][0]["text"] == "what is in this image?" + assert generationData.type == "GENERATION" + assert "gpt-4o" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + + +def test_response_api_web_search(): + client = openai.OpenAI() + generation_name = "test_response_api_web_search" + create_uuid()[:8] + + client.responses.create( + name=generation_name, + model="gpt-4o", + tools=[{"type": "web_search_preview"}], + input="What was a positive news story from today?", + ) + + openai.flush_langfuse() + + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert generationData.input == "What was a positive news story from today?" + assert generationData.type == "GENERATION" + assert "gpt-4o" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + assert generationData.metadata is not None + + +def test_response_api_streaming(): + client = openai.OpenAI() + generation_name = "test_response_api_streaming" + create_uuid()[:8] + + response = client.responses.create( + name=generation_name, + model="gpt-4o", + instructions="You are a helpful assistant.", + input="Hello!", + stream=True, + ) + + for _ in response: + continue + + openai.flush_langfuse() + + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert generation.data[0].input == "Hello!" + assert generationData.type == "GENERATION" + assert "gpt-4o" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + assert generationData.metadata is not None + assert generationData.metadata["instructions"] == "You are a helpful assistant." + + +def test_response_api_functions(): + client = openai.OpenAI() + generation_name = "test_response_api_functions" + create_uuid()[:8] + + tools = [ + { + "type": "function", + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + }, + "required": ["location", "unit"], + }, + } + ] + + client.responses.create( + name=generation_name, + model="gpt-4o", + tools=tools, + input="What is the weather like in Boston today?", + tool_choice="auto", + ) + + openai.flush_langfuse() + + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert generation.data[0].input == "What is the weather like in Boston today?" + assert generationData.type == "GENERATION" + assert "gpt-4o" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + assert generationData.metadata is not None + + +def test_response_api_reasoning(): + client = openai.OpenAI() + generation_name = "test_response_api_reasoning" + create_uuid()[:8] + + client.responses.create( + name=generation_name, + model="o3-mini", + input="How much wood would a woodchuck chuck?", + reasoning={"effort": "high"}, + ) + openai.flush_langfuse() + + generation = get_api().observations.get_many( + name=generation_name, type="GENERATION" + ) + + assert len(generation.data) != 0 + generationData = generation.data[0] + assert generationData.name == generation_name + assert generation.data[0].input == "How much wood would a woodchuck chuck?" + assert generationData.type == "GENERATION" + assert "o3-mini" in generationData.model + assert generationData.start_time is not None + assert generationData.end_time is not None + assert generationData.start_time < generationData.end_time + assert generationData.usage.input is not None + assert generationData.usage.output is not None + assert generationData.usage.total is not None + assert generationData.output is not None + assert generationData.metadata is not None