Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions langfuse/_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@
from langfuse._utils.prompt_cache import PromptCache
from langfuse.api.resources.commons.errors.error import Error
from langfuse.api.resources.commons.errors.not_found_error import NotFoundError
from langfuse.api.resources.commons.types.dataset_run_with_items import (
DatasetRunWithItems,
)
from langfuse.api.resources.datasets.types.delete_dataset_run_response import (
DeleteDatasetRunResponse,
)
from langfuse.api.resources.datasets.types.paginated_dataset_runs import (
PaginatedDatasetRuns,
)
from langfuse.api.resources.ingestion.types.score_body import ScoreBody
from langfuse.api.resources.prompts.types import (
CreatePromptRequest_Chat,
Expand Down Expand Up @@ -2461,6 +2470,128 @@ def get_dataset(
handle_fern_exception(e)
raise e

def get_dataset_run(
self,
dataset_name: str,
run_name: str,
) -> DatasetRunWithItems:
"""Fetch a dataset run by dataset name and run name.

This method properly URL-encodes the dataset and run names, supporting
folder-format names that contain slashes (e.g., "folder/subfolder/dataset").

Args:
dataset_name: The name of the dataset (can include slashes for folder structure).
run_name: The name of the run to fetch (can include slashes).

Returns:
DatasetRunWithItems: The dataset run with its items.

Raises:
NotFoundError: If the dataset or run is not found.

Example:
```python
# Fetch a run from a dataset in a folder
run = langfuse.get_dataset_run(
dataset_name="evaluation/qa-dataset",
run_name="experiment-1"
)
print(f"Run has {len(run.dataset_run_items)} items")
```
"""
try:
langfuse_logger.debug(f"Getting dataset run {dataset_name}/{run_name}")
return self.api.datasets.get_run(
dataset_name=self._url_encode(dataset_name),
run_name=self._url_encode(run_name),
)
except Error as e:
handle_fern_exception(e)
raise e

def get_dataset_runs(
self,
dataset_name: str,
*,
page: Optional[int] = None,
limit: Optional[int] = None,
) -> PaginatedDatasetRuns:
"""Fetch all runs for a dataset.

This method properly URL-encodes the dataset name, supporting
folder-format names that contain slashes (e.g., "folder/subfolder/dataset").

Args:
dataset_name: The name of the dataset (can include slashes for folder structure).
page: Optional page number for pagination (starts at 1).
limit: Optional limit of items per page.

Returns:
PaginatedDatasetRuns: Paginated list of dataset runs.

Raises:
NotFoundError: If the dataset is not found.

Example:
```python
# Get all runs for a dataset in a folder
runs = langfuse.get_dataset_runs("evaluation/qa-dataset")
for run in runs.data:
print(f"Run: {run.name}")
```
"""
try:
langfuse_logger.debug(f"Getting dataset runs for {dataset_name}")
return self.api.datasets.get_runs(
dataset_name=self._url_encode(dataset_name),
page=page,
limit=limit,
)
except Error as e:
handle_fern_exception(e)
raise e

def delete_dataset_run(
self,
dataset_name: str,
run_name: str,
) -> DeleteDatasetRunResponse:
"""Delete a dataset run and all its run items.

This action is irreversible. This method properly URL-encodes the dataset
and run names, supporting folder-format names that contain slashes.

Args:
dataset_name: The name of the dataset (can include slashes for folder structure).
run_name: The name of the run to delete (can include slashes).

Returns:
DeleteDatasetRunResponse: Confirmation of the deletion.

Raises:
NotFoundError: If the dataset or run is not found.

Example:
```python
# Delete a run from a dataset in a folder
result = langfuse.delete_dataset_run(
dataset_name="evaluation/qa-dataset",
run_name="old-experiment"
)
print(result.message) # "Dataset run deleted successfully"
```
"""
try:
langfuse_logger.debug(f"Deleting dataset run {dataset_name}/{run_name}")
return self.api.datasets.delete_run(
dataset_name=self._url_encode(dataset_name),
run_name=self._url_encode(run_name),
)
except Error as e:
handle_fern_exception(e)
raise e

def run_experiment(
self,
*,
Expand Down
116 changes: 115 additions & 1 deletion tests/test_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def test_get_dataset_runs():

langfuse.flush()
time.sleep(1) # Give API time to process
runs = langfuse.api.datasets.get_runs(dataset_name)
runs = langfuse.get_dataset_runs(dataset_name)

assert len(runs.data) == 2
assert runs.data[0].name == run_name_2
Expand Down Expand Up @@ -419,3 +419,117 @@ def execute_dataset_item(item, run_name):
assert "args" in trace.input
assert trace.input["args"][0] == expected_input
assert trace.output == expected_input


def test_get_dataset_with_folder_name():
"""Test that get_dataset works with folder-format names containing slashes."""
langfuse = Langfuse(debug=False)

# Create a dataset with slashes in the name (folder format)
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
langfuse.create_dataset(name=folder_name)

# Fetch the dataset using the wrapper method
dataset = langfuse.get_dataset(folder_name)

assert dataset.name == folder_name
assert "/" in dataset.name # Verify slashes are preserved


def test_get_dataset_runs_with_folder_name():
"""Test that get_dataset_runs works with folder-format dataset names."""
langfuse = Langfuse(debug=False)

# Create a dataset with slashes in the name
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
langfuse.create_dataset(name=folder_name)

# Create a dataset item
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})

dataset = langfuse.get_dataset(folder_name)
assert len(dataset.items) == 1

# Create a run
run_name = f"run-{create_uuid()[:8]}"
for item in dataset.items:
with item.run(run_name=run_name):
pass

langfuse.flush()
time.sleep(1) # Give API time to process

# Fetch runs using the new wrapper method
runs = langfuse.get_dataset_runs(folder_name)

assert len(runs.data) == 1
assert runs.data[0].name == run_name


def test_get_dataset_run_with_folder_names():
"""Test that get_dataset_run works with folder-format dataset and run names."""
langfuse = Langfuse(debug=False)

# Create a dataset with slashes in the name
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
langfuse.create_dataset(name=folder_name)

# Create a dataset item
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})

dataset = langfuse.get_dataset(folder_name)
assert len(dataset.items) == 1

# Create a run with slashes in the name
run_name = f"run/nested/{create_uuid()[:8]}"
for item in dataset.items:
with item.run(run_name=run_name, run_metadata={"key": "value"}):
pass

langfuse.flush()
time.sleep(1) # Give API time to process

# Fetch the specific run using the new wrapper method
run = langfuse.get_dataset_run(folder_name, run_name)

assert run.name == run_name
assert run.metadata == {"key": "value"}
assert "/" in folder_name # Verify slashes are preserved


def test_delete_dataset_run_with_folder_names():
"""Test that delete_dataset_run works with folder-format dataset and run names."""
langfuse = Langfuse(debug=False)

# Create a dataset with slashes in the name
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
langfuse.create_dataset(name=folder_name)

# Create a dataset item
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})

dataset = langfuse.get_dataset(folder_name)

# Create a run with slashes in the name
run_name = f"run/to/delete/{create_uuid()[:8]}"
for item in dataset.items:
with item.run(run_name=run_name):
pass

langfuse.flush()
time.sleep(1) # Give API time to process

# Verify the run exists
runs_before = langfuse.get_dataset_runs(folder_name)
assert len(runs_before.data) == 1

# Delete the run using the new wrapper method
result = langfuse.delete_dataset_run(folder_name, run_name)

assert result.message is not None

time.sleep(1) # Give API time to process deletion

# Verify the run is deleted
runs_after = langfuse.get_dataset_runs(folder_name)
assert len(runs_after.data) == 0