mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
feat(mcp): support saved metrics from datasets in chart generation (#38955)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit 15bab227bb)
This commit is contained in:
committed by
Michael S. Molina
parent
bda02a3fdc
commit
c853d4df63
@@ -82,25 +82,19 @@ class DatasetValidator:
|
||||
# Collect all column references
|
||||
column_refs = DatasetValidator._extract_column_references(config)
|
||||
|
||||
# Validate each column exists
|
||||
invalid_columns = []
|
||||
for col_ref in column_refs:
|
||||
if not DatasetValidator._column_exists(col_ref.name, dataset_context):
|
||||
invalid_columns.append(col_ref)
|
||||
# Validate saved metrics exist in dataset metrics specifically
|
||||
invalid_saved = DatasetValidator._validate_saved_metrics(
|
||||
column_refs, dataset_context
|
||||
)
|
||||
if invalid_saved:
|
||||
return False, invalid_saved
|
||||
|
||||
if invalid_columns:
|
||||
# Generate suggestions for invalid columns
|
||||
suggestions_map = {}
|
||||
for col_ref in invalid_columns:
|
||||
suggestions = DatasetValidator._get_column_suggestions(
|
||||
col_ref.name, dataset_context
|
||||
)
|
||||
suggestions_map[col_ref.name] = suggestions
|
||||
|
||||
# Build error with suggestions
|
||||
return False, DatasetValidator._build_column_error(
|
||||
invalid_columns, suggestions_map, dataset_context
|
||||
)
|
||||
# Validate columns exist (skip saved metrics — already validated above)
|
||||
column_error = DatasetValidator._validate_columns_exist(
|
||||
column_refs, dataset_context
|
||||
)
|
||||
if column_error:
|
||||
return False, column_error
|
||||
|
||||
# Validate aggregation compatibility
|
||||
if isinstance(config, (TableChartConfig, XYChartConfig)):
|
||||
@@ -112,6 +106,32 @@ class DatasetValidator:
|
||||
|
||||
return True, None
|
||||
|
||||
@staticmethod
|
||||
def _validate_columns_exist(
|
||||
column_refs: List[ColumnRef], dataset_context: DatasetContext
|
||||
) -> ChartGenerationError | None:
|
||||
"""Validate that non-saved-metric column refs exist in the dataset."""
|
||||
invalid_columns = []
|
||||
for col_ref in column_refs:
|
||||
if col_ref.saved_metric:
|
||||
continue
|
||||
if not DatasetValidator._column_exists(col_ref.name, dataset_context):
|
||||
invalid_columns.append(col_ref)
|
||||
|
||||
if not invalid_columns:
|
||||
return None
|
||||
|
||||
suggestions_map = {}
|
||||
for col_ref in invalid_columns:
|
||||
suggestions = DatasetValidator._get_column_suggestions(
|
||||
col_ref.name, dataset_context
|
||||
)
|
||||
suggestions_map[col_ref.name] = suggestions
|
||||
|
||||
return DatasetValidator._build_column_error(
|
||||
invalid_columns, suggestions_map, dataset_context
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_dataset_context(dataset_id: int | str) -> DatasetContext | None:
|
||||
"""Get dataset context with column information."""
|
||||
@@ -418,6 +438,49 @@ class DatasetValidator:
|
||||
error_code="MULTIPLE_INVALID_COLUMNS",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_saved_metrics(
|
||||
column_refs: List[ColumnRef], dataset_context: DatasetContext
|
||||
) -> ChartGenerationError | None:
|
||||
"""Validate that saved_metric refs exist in dataset metrics.
|
||||
|
||||
A ColumnRef with saved_metric=True must match an entry in
|
||||
available_metrics, not just available_columns. Without this check
|
||||
a regular column name marked as saved_metric would pass
|
||||
_column_exists (which checks both lists) but fail at query time.
|
||||
"""
|
||||
metric_names = {m["name"].lower() for m in dataset_context.available_metrics}
|
||||
invalid = [
|
||||
col_ref.name
|
||||
for col_ref in column_refs
|
||||
if col_ref.saved_metric and col_ref.name.lower() not in metric_names
|
||||
]
|
||||
if not invalid:
|
||||
return None
|
||||
|
||||
from superset.mcp_service.utils.error_builder import ChartErrorBuilder
|
||||
|
||||
available = [m["name"] for m in dataset_context.available_metrics]
|
||||
return ChartErrorBuilder.build_error(
|
||||
error_type="invalid_saved_metric",
|
||||
template_key="column_not_found",
|
||||
template_vars={
|
||||
"column": ", ".join(invalid),
|
||||
"suggestions": (
|
||||
f"Available saved metrics: {', '.join(available[:10])}"
|
||||
if available
|
||||
else "This dataset has no saved metrics"
|
||||
),
|
||||
},
|
||||
custom_suggestions=[
|
||||
f"'{name}' is not a saved metric in this dataset. "
|
||||
"Remove saved_metric=True to use it as a column with an aggregate, "
|
||||
"or use get_dataset_info to see available saved metrics."
|
||||
for name in invalid
|
||||
],
|
||||
error_code="INVALID_SAVED_METRIC",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_aggregations(
|
||||
column_refs: List[ColumnRef], dataset_context: DatasetContext
|
||||
@@ -426,6 +489,8 @@ class DatasetValidator:
|
||||
errors = []
|
||||
|
||||
for col_ref in column_refs:
|
||||
if col_ref.saved_metric:
|
||||
continue # Saved metrics have built-in aggregation
|
||||
if not col_ref.aggregate:
|
||||
continue
|
||||
|
||||
|
||||
Reference in New Issue
Block a user