Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ const SqlEditor: FC<Props> = ({
`}
>
{' '}
{t(`You are edting a query from the virtual dataset `) +
{t(`You are editing a query from the virtual dataset `) +
queryEditor.name}
</p>
<p
Expand Down
3 changes: 2 additions & 1 deletion superset-frontend/src/pages/SqlLab/LocationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ export const LocationProvider: FC = ({ children }: { children: ReactNode }) => {
const permalink = location.pathname.match(/\/p\/\w+/)?.[0].slice(3);
if (queryParams.size > 0 || permalink) {
const autorun = queryParams.get('autorun') === 'true';
const isDataset = queryParams.get('isDataset') === 'true';
const queryParamsState = {
requestedQuery: {
...Object.fromEntries(queryParams),
autorun,
permalink,
},
isDataset: true,
isDataset,
} as LocationState;
return <Provider value={queryParamsState}>{children}</Provider>;
}
Expand Down
7 changes: 7 additions & 0 deletions superset/charts/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,13 @@ class ChartDataExtrasSchema(Schema):
},
allow_none=True,
)
transpile_to_dialect = fields.Boolean(
metadata={
"description": "If true, WHERE/HAVING clauses will be transpiled to the "
"target database dialect using SQLGlot."
},
allow_none=True,
)


class AnnotationLayerSchema(Schema):
Expand Down
16 changes: 12 additions & 4 deletions superset/mcp_service/chart/chart_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ def map_table_config(config: TableChartConfig) -> Dict[str, Any]:
if not raw_columns and not aggregated_metrics:
raise ValueError("Table chart configuration resulted in no displayable columns")

# Use the viz_type from config (defaults to "table", can be "ag-grid-table")
form_data: Dict[str, Any] = {
"viz_type": "table",
"viz_type": config.viz_type,
}

# Handle raw columns (no aggregation)
Expand Down Expand Up @@ -370,7 +371,8 @@ def analyze_chart_capabilities(chart: Any | None, config: Any) -> ChartCapabilit
}
viz_type = viz_type_map.get(kind, "echarts_timeseries_line")
elif chart_type == "table":
viz_type = "table"
# Use the viz_type from config if available (table or ag-grid-table)
viz_type = getattr(config, "viz_type", "table")
else:
viz_type = "unknown"

Expand All @@ -382,10 +384,11 @@ def analyze_chart_capabilities(chart: Any | None, config: Any) -> ChartCapabilit
"echarts_timeseries_scatter",
"deck_scatter",
"deck_hex",
"ag-grid-table", # AG Grid tables are interactive
]

supports_interaction = viz_type in interactive_types
supports_drill_down = viz_type in ["table", "pivot_table_v2"]
supports_drill_down = viz_type in ["table", "pivot_table_v2", "ag-grid-table"]
supports_real_time = viz_type in [
"echarts_timeseries_line",
"echarts_timeseries_bar",
Expand Down Expand Up @@ -433,7 +436,8 @@ def analyze_chart_semantics(chart: Any | None, config: Any) -> ChartSemantics:
}
viz_type = viz_type_map.get(kind, "echarts_timeseries_line")
elif chart_type == "table":
viz_type = "table"
# Use the viz_type from config if available (table or ag-grid-table)
viz_type = getattr(config, "viz_type", "table")
else:
viz_type = "unknown"

Expand All @@ -442,6 +446,10 @@ def analyze_chart_semantics(chart: Any | None, config: Any) -> ChartSemantics:
"echarts_timeseries_line": "Shows trends and changes over time",
"echarts_timeseries_bar": "Compares values across categories or time periods",
"table": "Displays detailed data in tabular format",
"ag-grid-table": (
"Interactive table with advanced features like column resizing, "
"sorting, filtering, and server-side pagination"
),
"pie": "Shows proportional relationships within a dataset",
"echarts_area": "Emphasizes cumulative totals and part-to-whole relationships",
}
Expand Down
18 changes: 13 additions & 5 deletions superset/mcp_service/chart/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,21 +211,21 @@ def serialize_chart_object(chart: ChartLike | None) -> ChartInfo | None:
if not chart:
return None

# Generate MCP service screenshot URL instead of chart's native URL
from superset.mcp_service.utils.url_utils import get_chart_screenshot_url
# Use the chart's native URL (explore URL) instead of screenshot URL
from superset.mcp_service.utils.url_utils import get_superset_base_url

chart_id = getattr(chart, "id", None)
screenshot_url = None
chart_url = None
if chart_id:
screenshot_url = get_chart_screenshot_url(chart_id)
chart_url = f"{get_superset_base_url()}/explore/?slice_id={chart_id}"

return ChartInfo(
id=chart_id,
slice_name=getattr(chart, "slice_name", None),
viz_type=getattr(chart, "viz_type", None),
datasource_name=getattr(chart, "datasource_name", None),
datasource_type=getattr(chart, "datasource_type", None),
url=screenshot_url,
url=chart_url,
description=getattr(chart, "description", None),
cache_timeout=getattr(chart, "cache_timeout", None),
form_data=getattr(chart, "form_data", None),
Expand Down Expand Up @@ -608,6 +608,14 @@ class TableChartConfig(BaseModel):
chart_type: Literal["table"] = Field(
..., description="Chart type (REQUIRED: must be 'table')"
)
viz_type: Literal["table", "ag-grid-table"] = Field(
"table",
description=(
"Visualization type: 'table' for standard table, 'ag-grid-table' for "
"AG Grid Interactive Table with advanced features like column resizing, "
"sorting, filtering, and server-side pagination"
),
)
columns: List[ColumnRef] = Field(
...,
min_length=1,
Expand Down
25 changes: 4 additions & 21 deletions superset/mcp_service/chart/tool/generate_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,9 @@
GenerateChartRequest,
GenerateChartResponse,
PerformanceMetadata,
URLPreview,
)
from superset.mcp_service.utils.schema_utils import parse_request
from superset.mcp_service.utils.url_utils import (
get_chart_screenshot_url,
get_superset_base_url,
)
from superset.mcp_service.utils.url_utils import get_superset_base_url
from superset.utils import json

logger = logging.getLogger(__name__)
Expand All @@ -60,7 +56,6 @@ async def generate_chart( # noqa: C901
- Charts are NOT saved by default (save_chart=False) - preview only
- Set save_chart=True to permanently save the chart
- LLM clients MUST display returned chart URL to users
- Embed preview_url as image: ![Chart Preview](preview_url)
- Use numeric dataset ID or UUID (NOT schema.table_name format)
- MUST include chart_type in config (either 'xy' or 'table')

Expand Down Expand Up @@ -397,20 +392,9 @@ async def generate_chart( # noqa: C901
previews[format_type] = preview_result.content
else:
# For preview-only mode (save_chart=false)
if format_type == "url" and form_data_key:
# Generate screenshot URL using centralized helper
from superset.mcp_service.utils.url_utils import (
get_explore_screenshot_url,
)

preview_url = get_explore_screenshot_url(form_data_key)
previews[format_type] = URLPreview(
preview_url=preview_url,
width=800,
height=600,
supports_interaction=False,
)
elif format_type in ["ascii", "table", "vega_lite"]:
# Note: Screenshot-based URL previews are not supported.
# Use the explore_url to view the chart interactively.
if format_type in ["ascii", "table", "vega_lite"]:
# Generate preview from form data without saved chart
from superset.mcp_service.chart.preview_utils import (
generate_preview_from_form_data,
Expand Down Expand Up @@ -483,7 +467,6 @@ async def generate_chart( # noqa: C901
"data": f"{get_superset_base_url()}/api/v1/chart/{chart.id}/data/"
if chart
else None,
"preview": get_chart_screenshot_url(chart.id) if chart else None,
"export": f"{get_superset_base_url()}/api/v1/chart/{chart.id}/export/"
if chart
else None,
Expand Down
60 changes: 12 additions & 48 deletions superset/mcp_service/chart/tool/get_chart_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,53 +72,17 @@ class URLPreviewStrategy(PreviewFormatStrategy):
"""Generate URL-based image preview."""

def generate(self) -> URLPreview | ChartError:
try:
from flask import g

from superset.mcp_service.screenshot.pooled_screenshot import (
PooledChartScreenshot,
)
from superset.mcp_service.utils.url_utils import get_superset_base_url

# Check if chart.id is None
if self.chart.id is None:
return ChartError(
error="Chart has no ID - cannot generate URL preview",
error_type="InvalidChart",
)

# Use configured Superset base URL instead of Flask's url_for
# which may not respect SUPERSET_WEBSERVER_ADDRESS
base_url = get_superset_base_url()
chart_url = f"{base_url}/superset/slice/{self.chart.id}/"
screenshot = PooledChartScreenshot(chart_url, self.chart.digest)

window_size = (self.request.width or 800, self.request.height or 600)
image_data = screenshot.get_screenshot(user=g.user, window_size=window_size)

if image_data:
# Use the MCP service screenshot URL via centralized helper
from superset.mcp_service.utils.url_utils import (
get_chart_screenshot_url,
)

preview_url = get_chart_screenshot_url(self.chart.id)

return URLPreview(
preview_url=preview_url,
width=self.request.width or 800,
height=self.request.height or 600,
)
else:
return ChartError(
error=f"Could not generate screenshot for chart {self.chart.id}",
error_type="ScreenshotError",
)
except Exception as e:
logger.error("URL preview generation failed: %s", e)
return ChartError(
error=f"Failed to generate URL preview: {str(e)}", error_type="URLError"
)
# Screenshot-based URL previews are not supported.
# Users should use the explore_url to view the chart interactively,
# or use other preview formats like 'ascii', 'table', or 'vega_lite'.
return ChartError(
error=(
"URL-based screenshot previews are not supported. "
"Use the explore_url to view the chart interactively, "
"or try formats: 'ascii', 'table', or 'vega_lite'."
),
error_type="UnsupportedFormat",
)


# Base64 preview support removed - we never return base64 data
Expand Down Expand Up @@ -479,7 +443,7 @@ def _analyze_field_types(
except Exception as e:
logger.warning("Error in field type analysis: %s", e)
# Return nominal types for all fields as fallback
return {field: "nominal" for field in fields}
return dict.fromkeys(fields, "nominal")

return field_types

Expand Down
7 changes: 1 addition & 6 deletions superset/mcp_service/chart/tool/update_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@
UpdateChartRequest,
)
from superset.mcp_service.utils.schema_utils import parse_request
from superset.mcp_service.utils.url_utils import (
get_chart_screenshot_url,
get_superset_base_url,
)
from superset.mcp_service.utils.url_utils import get_superset_base_url
from superset.utils import json

logger = logging.getLogger(__name__)
Expand All @@ -57,7 +54,6 @@ async def update_chart(
IMPORTANT:
- Chart must already be saved (from generate_chart with save_chart=True)
- LLM clients MUST display updated chart URL to users
- Embed preview_url as image: ![Updated Chart](preview_url)
- Use numeric ID or UUID string to identify the chart (NOT chart name)
- MUST include chart_type in config (either 'xy' or 'table')

Expand Down Expand Up @@ -222,7 +218,6 @@ async def update_chart(
"data": (
f"{get_superset_base_url()}/api/v1/chart/{updated_chart.id}/data/"
),
"preview": get_chart_screenshot_url(updated_chart.id),
"export": (
f"{get_superset_base_url()}/api/v1/chart/{updated_chart.id}/export/"
),
Expand Down
30 changes: 3 additions & 27 deletions superset/mcp_service/chart/tool/update_chart_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,8 @@
AccessibilityMetadata,
PerformanceMetadata,
UpdateChartPreviewRequest,
URLPreview,
)
from superset.mcp_service.utils.schema_utils import parse_request
from superset.mcp_service.utils.url_utils import get_mcp_service_url

logger = logging.getLogger(__name__)

Expand All @@ -56,7 +54,6 @@ def update_chart_preview(
- Modifies cached form_data from generate_chart (save_chart=False)
- Original form_data_key is invalidated, new one returned
- LLM clients MUST display explore_url to users
- Embed preview_url as image: ![Chart Preview](preview_url)

Use when:
- Modifying preview before deciding to save
Expand Down Expand Up @@ -99,30 +96,9 @@ def update_chart_preview(
high_contrast_available=False,
)

# Generate previews if requested
previews = {}
if request.generate_preview and new_form_data_key:
try:
for format_type in request.preview_formats:
if format_type == "url":
# Generate screenshot URL using new form_data key
mcp_base = get_mcp_service_url()
preview_url = (
f"{mcp_base}/screenshot/explore/{new_form_data_key}.png"
)

previews[format_type] = URLPreview(
preview_url=preview_url,
width=800,
height=600,
supports_interaction=False,
)
# Other formats would need form_data execution
# which is more complex for preview-only mode

except Exception as e:
# Log warning but don't fail the entire request
logger.warning("Preview generation failed: %s", e)
# Note: Screenshot-based previews are not supported.
# Use the explore_url to view the chart interactively.
previews: Dict[str, Any] = {}

# Return enhanced data
result = {
Expand Down
28 changes: 0 additions & 28 deletions superset/mcp_service/utils/url_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,31 +89,3 @@ def get_mcp_service_url() -> str:

# Development fallback - direct access to MCP service on port 5008
return "http://localhost:5008"


def get_chart_screenshot_url(chart_id: int | str) -> str:
"""
Generate a screenshot URL for a chart using the MCP service.

Args:
chart_id: Chart ID (numeric or string)

Returns:
Complete URL to the chart screenshot endpoint
"""
mcp_base = get_mcp_service_url()
return f"{mcp_base}/screenshot/chart/{chart_id}.png"


def get_explore_screenshot_url(form_data_key: str) -> str:
"""
Generate a screenshot URL for an explore view using the MCP service.

Args:
form_data_key: Form data key for the explore view

Returns:
Complete URL to the explore screenshot endpoint
"""
mcp_base = get_mcp_service_url()
return f"{mcp_base}/screenshot/explore/{form_data_key}.png"
Loading
Loading