"""Tools for interacting with LangSmith experiments."""
from typing import Any, Dict, Optional
from langsmith import Client
from langsmith_mcp_server.common.helpers import find_in_dict
def list_experiments_tool(
client: Client,
reference_dataset_id: Optional[str] = None,
reference_dataset_name: Optional[str] = None,
limit: int = 5,
project_name: Optional[str] = None,
) -> Dict[str, Any]:
"""
List LangSmith experiment projects (reference projects) with mandatory dataset filtering.
Args:
client: LangSmith client instance
reference_dataset_id: The ID of the reference dataset to filter experiments by
reference_dataset_name: The name of the reference dataset to filter experiments by
limit: Maximum number of experiments to return (default: 5)
project_name: Filter projects by name using partial matching
Returns:
Dictionary containing an "experiments" key with a list of simplified experiment project dictionaries
"""
# Validate that exactly one of reference_dataset_id or reference_dataset_name is provided
if reference_dataset_id is None and reference_dataset_name is None:
raise ValueError(
"Either 'reference_dataset_id' or 'reference_dataset_name' must be provided"
)
if reference_dataset_id is not None and reference_dataset_name is not None:
raise ValueError(
"Cannot provide both 'reference_dataset_id' and 'reference_dataset_name'. "
"Please provide only one."
)
projects = []
for project in client.list_projects(
reference_free=False,
reference_dataset_id=reference_dataset_id,
reference_dataset_name=reference_dataset_name,
name_contains=project_name,
limit=limit,
include_stats=True,
):
projects.append(project.dict())
simple_projects = []
for project in projects:
deployment_id = find_in_dict(project, "deployment_id")
project_id = project.get("id", None)
# Extract and format latency (p50 and p99)
latency_p50 = project.get("latency_p50", None)
latency_p99 = project.get("latency_p99", None)
# Extract and format cost (convert Decimal to float for JSON serialization)
total_cost = project.get("total_cost", None)
prompt_cost = project.get("prompt_cost", None)
completion_cost = project.get("completion_cost", None)
project_dict = {
"name": project.get("name", None),
"experiment_id": str(project_id) if project_id is not None else None,
"feedback_stats": project.get("feedback_stats", None),
}
# Add latency metrics if available
if latency_p50 is not None:
# Convert timedelta to total seconds (float)
project_dict["latency_p50_seconds"] = latency_p50.total_seconds()
if latency_p99 is not None:
project_dict["latency_p99_seconds"] = latency_p99.total_seconds()
# Add cost metrics if available
if total_cost is not None:
project_dict["total_cost"] = float(total_cost)
if prompt_cost is not None:
project_dict["prompt_cost"] = float(prompt_cost)
if completion_cost is not None:
project_dict["completion_cost"] = float(completion_cost)
if deployment_id:
project_dict["agent_deployment_id"] = deployment_id
simple_projects.append(project_dict)
return {"experiments": simple_projects}