Skip to content

Commit ea5658f

Browse files
feat(client): add methods to langfuse client to fetch and delete dataset runs (#1453)
1 parent f4148bc commit ea5658f

File tree

2 files changed

+186
-1
lines changed

2 files changed

+186
-1
lines changed

langfuse/_client/client.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@
7979
from langfuse._utils.parse_error import handle_fern_exception
8080
from langfuse._utils.prompt_cache import PromptCache
8181
from langfuse.api.resources.commons.errors.error import Error
82+
from langfuse.api.resources.commons.types import DatasetRunWithItems
83+
from langfuse.api.resources.datasets.types import (
84+
DeleteDatasetRunResponse,
85+
PaginatedDatasetRuns,
86+
)
8287
from langfuse.api.resources.commons.errors.not_found_error import NotFoundError
8388
from langfuse.api.resources.ingestion.types.score_body import ScoreBody
8489
from langfuse.api.resources.prompts.types import (
@@ -2461,6 +2466,78 @@ def get_dataset(
24612466
handle_fern_exception(e)
24622467
raise e
24632468

2469+
def get_dataset_run(
2470+
self, *, dataset_name: str, run_name: str
2471+
) -> DatasetRunWithItems:
2472+
"""Fetch a dataset run by dataset name and run name.
2473+
2474+
Args:
2475+
dataset_name (str): The name of the dataset.
2476+
run_name (str): The name of the run.
2477+
2478+
Returns:
2479+
DatasetRunWithItems: The dataset run with its items.
2480+
"""
2481+
try:
2482+
return self.api.datasets.get_run(
2483+
dataset_name=self._url_encode(dataset_name),
2484+
run_name=self._url_encode(run_name),
2485+
request_options=None,
2486+
)
2487+
except Error as e:
2488+
handle_fern_exception(e)
2489+
raise e
2490+
2491+
def get_dataset_runs(
2492+
self,
2493+
*,
2494+
dataset_name: str,
2495+
page: Optional[int] = None,
2496+
limit: Optional[int] = None,
2497+
) -> PaginatedDatasetRuns:
2498+
"""Fetch all runs for a dataset.
2499+
2500+
Args:
2501+
dataset_name (str): The name of the dataset.
2502+
page (Optional[int]): Page number, starts at 1.
2503+
limit (Optional[int]): Limit of items per page.
2504+
2505+
Returns:
2506+
PaginatedDatasetRuns: Paginated list of dataset runs.
2507+
"""
2508+
try:
2509+
return self.api.datasets.get_runs(
2510+
dataset_name=self._url_encode(dataset_name),
2511+
page=page,
2512+
limit=limit,
2513+
request_options=None,
2514+
)
2515+
except Error as e:
2516+
handle_fern_exception(e)
2517+
raise e
2518+
2519+
def delete_dataset_run(
2520+
self, *, dataset_name: str, run_name: str
2521+
) -> DeleteDatasetRunResponse:
2522+
"""Delete a dataset run and all its run items. This action is irreversible.
2523+
2524+
Args:
2525+
dataset_name (str): The name of the dataset.
2526+
run_name (str): The name of the run.
2527+
2528+
Returns:
2529+
DeleteDatasetRunResponse: Confirmation of deletion.
2530+
"""
2531+
try:
2532+
return self.api.datasets.delete_run(
2533+
dataset_name=self._url_encode(dataset_name),
2534+
run_name=self._url_encode(run_name),
2535+
request_options=None,
2536+
)
2537+
except Error as e:
2538+
handle_fern_exception(e)
2539+
raise e
2540+
24642541
def run_experiment(
24652542
self,
24662543
*,

tests/test_datasets.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def test_get_dataset_runs():
220220

221221
langfuse.flush()
222222
time.sleep(1) # Give API time to process
223-
runs = langfuse.api.datasets.get_runs(dataset_name)
223+
runs = langfuse.get_dataset_runs(dataset_name=dataset_name)
224224

225225
assert len(runs.data) == 2
226226
assert runs.data[0].name == run_name_2
@@ -419,3 +419,111 @@ def execute_dataset_item(item, run_name):
419419
assert "args" in trace.input
420420
assert trace.input["args"][0] == expected_input
421421
assert trace.output == expected_input
422+
423+
424+
def test_get_dataset_with_folder_name():
425+
"""Test that get_dataset works with folder-format names containing slashes."""
426+
langfuse = Langfuse(debug=False)
427+
428+
# Create a dataset with slashes in the name (folder format)
429+
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
430+
langfuse.create_dataset(name=folder_name)
431+
432+
# Fetch the dataset using the wrapper method
433+
dataset = langfuse.get_dataset(folder_name)
434+
assert dataset.name == folder_name
435+
assert "/" in dataset.name # Verify slashes are preserved
436+
437+
438+
def test_get_dataset_runs_with_folder_name():
439+
"""Test that get_dataset_runs works with folder-format dataset names."""
440+
langfuse = Langfuse(debug=False)
441+
442+
# Create a dataset with slashes in the name
443+
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
444+
langfuse.create_dataset(name=folder_name)
445+
446+
# Create a dataset item
447+
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})
448+
dataset = langfuse.get_dataset(folder_name)
449+
assert len(dataset.items) == 1
450+
451+
# Create a run
452+
run_name = f"run-{create_uuid()[:8]}"
453+
for item in dataset.items:
454+
with item.run(run_name=run_name):
455+
pass
456+
457+
langfuse.flush()
458+
time.sleep(1) # Give API time to process
459+
460+
# Fetch runs using the new wrapper method
461+
runs = langfuse.get_dataset_runs(dataset_name=folder_name)
462+
assert len(runs.data) == 1
463+
assert runs.data[0].name == run_name
464+
465+
466+
def test_get_dataset_run_with_folder_names():
467+
"""Test that get_dataset_run works with folder-format dataset and run names."""
468+
langfuse = Langfuse(debug=False)
469+
470+
# Create a dataset with slashes in the name
471+
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
472+
langfuse.create_dataset(name=folder_name)
473+
474+
# Create a dataset item
475+
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})
476+
dataset = langfuse.get_dataset(folder_name)
477+
assert len(dataset.items) == 1
478+
479+
# Create a run with slashes in the name
480+
run_name = f"run/nested/{create_uuid()[:8]}"
481+
for item in dataset.items:
482+
with item.run(run_name=run_name, run_metadata={"key": "value"}):
483+
pass
484+
485+
langfuse.flush()
486+
time.sleep(1) # Give API time to process
487+
488+
# Fetch the specific run using the new wrapper method
489+
run = langfuse.get_dataset_run(dataset_name=folder_name, run_name=run_name)
490+
assert run.name == run_name
491+
assert run.dataset_name == folder_name
492+
assert run.metadata == {"key": "value"}
493+
assert "/" in run_name # Verify slashes are preserved in run name
494+
495+
496+
def test_delete_dataset_run_with_folder_names():
497+
"""Test that delete_dataset_run works with folder-format dataset and run names."""
498+
langfuse = Langfuse(debug=False)
499+
500+
# Create a dataset with slashes in the name
501+
folder_name = f"folder/subfolder/dataset-{create_uuid()[:8]}"
502+
langfuse.create_dataset(name=folder_name)
503+
504+
# Create a dataset item
505+
langfuse.create_dataset_item(dataset_name=folder_name, input={"test": "data"})
506+
dataset = langfuse.get_dataset(folder_name)
507+
508+
# Create a run with slashes in the name
509+
run_name = f"run/to/delete/{create_uuid()[:8]}"
510+
for item in dataset.items:
511+
with item.run(run_name=run_name):
512+
pass
513+
514+
langfuse.flush()
515+
time.sleep(1) # Give API time to process
516+
517+
# Verify the run exists
518+
runs_before = langfuse.get_dataset_runs(dataset_name=folder_name)
519+
assert len(runs_before.data) == 1
520+
521+
# Delete the run using the new wrapper method
522+
result = langfuse.delete_dataset_run(dataset_name=folder_name, run_name=run_name)
523+
assert result.message is not None
524+
525+
time.sleep(1) # Give API time to process deletion
526+
527+
# Verify the run is deleted
528+
runs_after = langfuse.get_dataset_runs(dataset_name=folder_name)
529+
assert len(runs_after.data) == 0

0 commit comments

Comments
 (0)