Skip to content
Merged
77 changes: 77 additions & 0 deletions langfuse/_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
from langfuse._utils.parse_error import handle_fern_exception
from langfuse._utils.prompt_cache import PromptCache
from langfuse.api.resources.commons.errors.error import Error
from langfuse.api.resources.commons.types import DatasetRunWithItems
from langfuse.api.resources.datasets.types import (
DeleteDatasetRunResponse,
PaginatedDatasetRuns,
)
from langfuse.api.resources.commons.errors.not_found_error import NotFoundError
from langfuse.api.resources.ingestion.types.score_body import ScoreBody
from langfuse.api.resources.prompts.types import (
Expand Down Expand Up @@ -2461,6 +2466,78 @@ 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.

Args:
dataset_name (str): The name of the dataset.
run_name (str): The name of the run.

Returns:
DatasetRunWithItems: The dataset run with its items.
"""
try:
return self.api.datasets.get_run(
dataset_name=self._url_encode(dataset_name),
run_name=self._url_encode(run_name),
request_options=None,
)
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.

Args:
dataset_name (str): The name of the dataset.
page (Optional[int]): Page number, starts at 1.
limit (Optional[int]): Limit of items per page.

Returns:
PaginatedDatasetRuns: Paginated list of dataset runs.
"""
try:
return self.api.datasets.get_runs(
dataset_name=self._url_encode(dataset_name),
page=page,
limit=limit,
request_options=None,
)
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.

Args:
dataset_name (str): The name of the dataset.
run_name (str): The name of the run.

Returns:
DeleteDatasetRunResponse: Confirmation of deletion.
"""
try:
return self.api.datasets.delete_run(
dataset_name=self._url_encode(dataset_name),
run_name=self._url_encode(run_name),
request_options=None,
)
except Error as e:
handle_fern_exception(e)
raise e

def run_experiment(
self,
*,
Expand Down
110 changes: 109 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=dataset_name)

assert len(runs.data) == 2
assert runs.data[0].name == run_name_2
Expand Down Expand Up @@ -419,3 +419,111 @@ 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(dataset_name=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(dataset_name=folder_name, run_name=run_name)
assert run.name == run_name
assert run.dataset_name == folder_name
assert run.metadata == {"key": "value"}
assert "/" in run_name # Verify slashes are preserved in run name


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(dataset_name=folder_name)
assert len(runs_before.data) == 1

# Delete the run using the new wrapper method
result = langfuse.delete_dataset_run(dataset_name=folder_name, run_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(dataset_name=folder_name)
assert len(runs_after.data) == 0
Loading