Repository navigation
feat(mcp): add typed Bullet chart support - #43770
aminghadersohi wants to merge 108 commits into
Conversation
|
The issue described is a logic error in the Bullet chart configuration mapping where range rectangles lack a category To resolve this, you need to ensure that the |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #43770 +/- ##
==========================================
- Coverage 82.85% 82.84% -0.02%
==========================================
Files 3017 3018 +1
Lines 192909 194576 +1667
Branches 44974 45431 +457
==========================================
+ Hits 159836 161191 +1355
- Misses 30034 30206 +172
- Partials 3039 3179 +140
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review Agent Run #658aec
Actionable Suggestions - 1
-
superset/mcp_service/chart/chart_utils.py - 1
- Float precision loss · Line 1072-1072
Additional Suggestions - 7
-
superset/mcp_service/chart/preview_utils.py - 2
-
SQL metric label gap · Line 328-341`_form_metric_label` reimplements `superset.utils.core.get_metric_name` but drops its SQL branch (`expressionType == "SQL"` returns `sqlExpression`). For a SQL adhoc metric without a `label`, this returns `None`, so `_bullet_result_roles` falls back to the first numeric column — which may be a dimension or a different metric, producing a wrong Bullet preview. Consider reusing `get_metric_name` or adding the SQL branch.
-
Empty ranges band divergence · Line 620-620When `ranges` is empty, `_bullet_numeric_tokens` returns `[]` and no range band is drawn. The native Bullet plugin (`transformProps.ts`) falls back to `[0, measure * 1.1]` when ranges are empty, so the preview diverges from the real chart. Consider deriving a default band from the metric values to stay faithful.
-
-
superset/mcp_service/chart/tool/get_chart_preview.py - 1
-
Duplicated bullet spec logic · Line 704-822`_bullet_chart_spec` reimplements metric/dimension resolution (lines 729-745) and the full layer-building already provided by `_bullet_result_roles`/`_canonical_result_field` and `_generate_bullet_vega_lite_preview` in preview_utils.py. The metric fallback also diverges: existing code picks the first quantitative field from the data row, this picks the last from `reversed(fields)`. Reuse the shared helpers to avoid divergent bullet rendering.
-
-
superset/mcp_service/chart/chart_utils.py - 1
-
Unhandled KeyError · Line 1122-1122`dimension_targets[order.column.casefold()]` raises `KeyError` when an `order_by` column matches neither the metric nor any dimension. The schema's `_adapt_native_order_by` accepts arbitrary bare column-name strings, so this is reachable from user input and crashes the update/preview tool. Use `.get(...)` and surface a validation error.
-
-
superset/mcp_service/chart/schemas.py - 2
-
Incomplete operator mapping · Line 2231-2232`_adapt_native_filters` maps `operator` using `{"==": "=", "IS_NOT_NULL": "IS NOT NULL"}`, but saved `adhoc_filters` store the `Operators` enum value (e.g. `'EQUALS'`, `'LESS_THAN'`, `'GREATER_THAN'`, `'IN'`, `'IS_NULL'`). Only `IS_NOT_NULL` maps correctly; `'=='` never occurs, and all other operators pass through unmapped and fail `FilterConfig.op` Literal validation, so saved bullet charts with filters won't load. Map the full enum set.
-
order_by validation/mapper divergence · Line 2371-2379`valid_order_targets` adds a dimension's `label` even when the dimension has no `name`, but `map_bullet_config`'s `dimension_targets` dict only maps label→name when `dimension.name` is present (`if candidate and dimension.name`). A dimension `{"label": "Region"}` passes this validation yet crashes the mapper with `KeyError` on `dimension_targets[order.column.casefold()]`. Gate the label on `dimension.name` to match.
-
-
tests/unit_tests/mcp_service/chart/test_bullet_chart.py - 1
-
Vacuous compile test · Line 347-348Within the provided diff, `test_bullet_compile_path_uses_groupby_metric_orderby_and_empty_results` ends at line 348 with `query` assigned but never asserted; `result` (line 347) is also unused. If the function is complete, the test is vacuous and would pass even if compiled queries were wrong. Add assertions on `query`/`result`, or remove the dead assignments. (If assertions follow line 348 and the diff was truncated, please disregard.)
-
Review Details
-
Files reviewed - 14 · Commit Range:
6411173..6411173- superset/mcp_service/chart/chart_helpers.py
- superset/mcp_service/chart/chart_utils.py
- superset/mcp_service/chart/plugins/__init__.py
- superset/mcp_service/chart/plugins/bullet.py
- superset/mcp_service/chart/preview_utils.py
- superset/mcp_service/chart/resources/chart_configs.py
- superset/mcp_service/chart/schemas.py
- superset/mcp_service/chart/tool/generate_chart.py
- superset/mcp_service/chart/tool/get_chart_data.py
- superset/mcp_service/chart/tool/get_chart_preview.py
- superset/mcp_service/chart/tool/get_chart_type_schema.py
- superset/mcp_service/chart/tool/update_chart.py
- superset/mcp_service/chart/tool/update_chart_preview.py
- tests/unit_tests/mcp_service/chart/test_bullet_chart.py
-
Files skipped - 0
-
Tools
- MyPy (Static Code Analysis) - ✔︎ Successful
- Astral Ruff (Static Code Analysis) - ✔︎ Successful
- Whispers (Secret Scanner) - ✔︎ Successful
- Detect-secrets (Secret Scanner) - ✔︎ Successful
Bito Usage Guide
Commands
Type the following command in the pull request comment and save the comment.
-
/review- Manually triggers an incremental AI Review. -
/review full- Manually triggers a full AI Review. -
/pause- Pauses automatic reviews on this pull request. -
/resume- Resumes automatic reviews. -
/resolve- Marks all Bito-posted review comments as resolved. -
/abort- Cancels all in-progress reviews.
Refer to the documentation for additional commands.
Configuration
This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.
Documentation & Help
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Code Review Agent Run #d1f367Actionable Suggestions - 0Additional Suggestions - 6
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Code Review Agent Run #76a9c8Actionable Suggestions - 0Additional Suggestions - 6
Filtered by Review RulesBito filtered these suggestions based on rules created automatically for your feedback. Manage rules.
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Code Review Agent Run #0e799fActionable Suggestions - 0Additional Suggestions - 2
Filtered by Review RulesBito filtered these suggestions based on rules created automatically for your feedback. Manage rules.
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Code Review Agent Run #400d68Actionable Suggestions - 0Additional Suggestions - 6
Filtered by Review RulesBito filtered these suggestions based on rules created automatically for your feedback. Manage rules.
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Code Review Agent Run #48577fActionable Suggestions - 0Additional Suggestions - 6
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
|
AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
A saved grouped XY chart with truncate_metric strips the metric prefix from the pivoted columns, so field discovery found no series and the preview fell back to a single unlabeled series. With one metric and truncate_metric set, every non-x-axis column is a category series.
The envelope validation in compile and the preview paths ran before the plugin result normalizer and rejected any non-finite cell, so a grouped Gauge with one Infinity dial failed instead of rendering its finite dials. Honor the plugin's preserve_nonfinite_floats contract there, and render the normalized rows in the saved Vega-Lite preview.
Bullet creation without row_limit saved no limit, unlike the advertised schema default. The mapper always emits the limit; update merging keeps the saved value when the caller omits it.
No MCP tool exports Parquet: get_chart_data accepts json, csv, and excel, and query_dataset and get_table have no export option.
build_mixed_timeseries_secondary and with_x_axis_column have no callers since the Mixed Timeseries plugin builds both layers through the shared timeseries query builder.
Chart-data results unescape flattened column names, so a metric label containing the separator never matched its escaped prefix and the preview lost its category series. Match the unescaped label as well as the escaped spelling from raw post-processing output.
Table buildQuery drops a cached duration grain from semantic-view aggregate queries when no selected column is temporal, because the semantic layer rejects a grain without a time column. The MCP Table builder copied the grain into extras, so get_chart_data failed for a chart that renders in Explore. Apply the same rule to every final query.
Rows that fit the source result budget can still exceed the response budget once column profiling repeats their cells in sample_values. Exercise that guard through the registered tool.
Restating the saved dimensions in a Bullet update made the merged validation copy treat an inherited independent sort, such as a saved ranking metric, as newly authored and reject it. Exclude the inherited ordering unless the update supplies order_by, as for groupby and filters; the native query contract still validates it.
A single compared metric renames its shifted series to the bare offset, so the metric-prefix filter dropped the comparison series from grouped previews. Also escape the x-axis field in the encoding and tooltip, as for folded series, so a dotted column name is not read as a nested path.
The rebind prune only checked list-valued column roles, so a saved Bullet with a scalar groupby kept a hierarchy the replacement dataset cannot resolve and the query failed on a missing column. Treat a scalar value as a one-item list, as the frontend ensureIsArray does.
- Treat a saved chart's viz_type column as authoritative when params omit it, and treat a missing saved viz_type as a visualization boundary when merging filters. - Drop a null dashboard time-subject marker from the Bullet validation copy regardless of filters. - Infer the XY pivot preview x type from parseable dates instead of a character scan, so labels such as "New York" stay nominal. - Null infinities nested inside array/object cells like NaN instead of rejecting the whole result. - Remove the unused Deck.gl column/metric/null-filter resolvers and tests.
…le response guard
Master landed the typed Sunburst chart (apache#43771), whose shared MCP chart infrastructure (native query builders in form_data_query_context, result envelope validation, preview/update hooks, response preflight) overlaps the shared code this branch had grown independently. Resolve onto master's shared implementation and port the Bullet-specific behavior onto it: - Bullet plugin: build_query_dicts mirrors Bullet/buildQuery, table previews are rejected through unsupported_preview, null data is malformed, and same-viz updates keep unmodeled native controls like the shared overlay. - Result validation gains an opt-in Chart Data temporal mode (epoch-ms numbers and duration text) used by plugins that set temporal_json_numbers. - get_chart_data honors allows_empty_data_result and sanitize_data_rows, and empty results still export their header row. - Compile checks optionally validate native QueryObject references for plugins that set validates_native_references. - Dataset rebinds follow master's contract (saved query roles are not inherited), so Bullet rebind tests that expected inherited hierarchies were removed. Shared-behavior tests from this branch that targeted the superseded implementation were dropped in favor of master's suites; Bullet tests that lived in shared test modules moved to test_bullet_tool_paths.py.
…iew regressions Folded grouped XY Vega-Lite previews encoded a quantitative y with a nominal color and no stack, so Vega-Lite stacked unstacked grouped bars and areas. The y encoding now follows the saved stack control (null when unstacked, zero for Stack, normalize for Expand). Add regression tests for the other review findings, which the shared master implementation resolves: - the compile sample skips the Prophet step for grouped series whose two sample rows share one timestamp; - get_table reports grain-variant temporal columns as temporal for empty results too; - update_chart_preview's access check runs against a real dataset lookup and blocks compile and cache writes.
…order aliases, keep native provenance through request normalization
# Conflicts: # superset/mcp_service/chart/schemas.py # superset/mcp_service/chart/tool/generate_chart.py
… bar previews faithful - Do not restore the dashboard time-binding marker once the merge decided the adhoc filter sequence, so a filters=[] update validates. - Export every validated ungrouped Bullet source row instead of only the rendered first row. - Offset unstacked folded bars by series so they render side by side. - Remove unreachable Timeseries/Mixed/Deck raw-control checks from the Bullet-only native reference validator.
SUMMARY
Adds typed MCP support for the ECharts Bullet plugin (
chart_type: bullet, nativeviz_type: bullet), including schema discovery, metric/dimension normalization, query compilation, saved/cached updates, and ASCII/Vega-Lite previews.Preserves the reviewed shared contracts: query-result validation and bounded serialization; pandas/NumPy temporal and numeric conversion; exact-result identity; final-response size preflight; filter/time-binding provenance and explicit-clear behavior; and existing BASE_AXIS, Deck.gl, Pivot, Jinja, and non-Bullet paths.
Finite maintainer pass:
e22ce197into Amin's branch using normal merge commits, with no rebase, force-push, or history rewrite. Conflict resolution retains both Bullet and upstream Gauge registration/schema/discovery, preview renderers, result normalization, and metric sorting.Regionandregionremain distinct through role validation, dataset normalization, sort-target resolution, query columns, and preview result keys. Exact duplicates and ambiguous non-exact lookups remain errors.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
Screenshots
Bullet chart created by the MCP
generate_charttool (chart_typebullet), opened in Explore on thecleaned_sales_dataexamples dataset: SUM(sales) by product_line.The rendered chart: qualitative range bands (Low / Target / Stretch), the Forecast marker line at 2.5M, and the Goal markers at 3.5M, all from the MCP config.
Captured at
4bec3812. Captured in a local docker-compose-light dev environment with the bundled examples dataset, driving this PR's MCP generate_chart tool.N/A: MCP contract/query/preview changes; no frontend UI change. The frontend addition is a parity regression test. No live Superset server was available (
localhost:8088/healthfailed), so live/manual UI verification is not claimed.TESTING INSTRUCTIONS
Run from the repository root:
Results for this pass:
d753e0b0647db86223f60be4acb05d2ab09c476e. A focused post-merge Bullet/Gauge/Sunburst-fallback/update-path rerun passed 614 tests (1 skipped). Branch-wide pre-commit also passed. All required exact-head GitHub checks completed successfully.ADDITIONAL INFORMATION
Work is confined to Amin's
sc-119157-mcp-bullet. No Sunburst or community branch was modified, and no independent reviewer or further review loop was started.All live reviews, inline comments, issue comments, and review threads were fetched again for this finite pass. No new actionable feedback or unresolved thread was present. Earlier addressed human threads were short labels and case-distinct dimensions. Both received individual fix/evidence replies after the normal push and are resolved. The newer bot's ambiguous temporal-binding coverage suggestion is already covered by
test_bullet_rejects_ambiguous_provenance_subject_operator_matches, which exercises the livemerge_update_form_datapath. Optional deduplication refactors are deferred rather than broadening this finite pass.