feat(pkg-py): Add ggsql visualization integration#201
Draft
feat(pkg-py): Add ggsql visualization integration#201
Conversation
c5c8f6e to
34a41da
Compare
34a41da to
a21712c
Compare
cpsievert
commented
Mar 3, 2026
Add core support for LLM-generated visualizations using ggsql syntax: - Add ggsql and altair dependencies to pyproject.toml - Create _ggsql.py with helpers for parsing and rendering visualizations - Extend AppState with visualization state fields (filter_viz_*, query_viz_*) - Implement visualize_dashboard and visualize_query tools in tools.py - Add prompt templates for visualization tools with ggsql syntax reference - Update system prompt with ggsql grammar documentation - Add visualization accessor methods to QueryChatBase - Export visualization data types (VisualizeDashboardData, VisualizeQueryData) The ggsql DSL allows the LLM to generate chart specifications that are rendered to Altair/Vega-Lite charts, supporting bar, line, point, area, and boxplot marks with various encodings. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrate ggsql visualization capabilities into all supported frameworks: Shiny: - Add visualization state to ServerValues dataclass - Implement ggvis(), ggsql(), ggtitle() reactive accessors - Add filter visualization re-rendering on data change - Create tabbed UI with Data/Filter Plot/Query Plot tabs Streamlit: - Add ggvis(), ggsql(), ggtitle() methods reading from session state - Create tabbed app layout with visualization tabs - Render Altair charts with expandable ggsql specs Gradio: - Add ggvis(), ggsql(), ggtitle() methods taking state dict - Create tabbed Blocks layout with visualization displays - Wire state changes to update all visualization outputs Dash: - Add visualization callbacks and state management - Create tabbed layout with dcc.Graph for Altair charts - Add ggsql spec display in collapsible sections All frameworks enable visualization tools by default and support both filter (dashboard) and query visualizations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive test coverage for ggsql visualization features: - test_ggsql.py: Unit tests for ggsql parsing and rendering - test_ggsql_integration.py: Integration tests for end-to-end visualization - test_viz_tools.py: Tests for visualize_dashboard and visualize_query tools - test_visualization_tabs.py: Playwright tests for UI tab interactions - Update test_state.py with visualization state field tests - Update test_tools.py and test_base.py for new tool configurations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
a21712c to
05687d5
Compare
05687d5 to
8f33d70
Compare
8f33d70 to
a3bdcbd
Compare
a3bdcbd to
3e15610
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3e15610 to
ac299be
Compare
1450755 to
a711326
Compare
a711326 to
8696fca
Compare
8696fca to
db264dd
Compare
db264dd to
5a7e9de
Compare
…ge ggsql API Remove the visualize_dashboard tool to focus on visualize_query. Switch from the one-shot ggsql.render_altair() API to the two-stage API (reader.execute → writer.render), gaining access to ggsql's writer- independent Spec object for structured metadata access. The new execute_ggsql() helper uses a hybrid approach: DataSource handles SQL execution (preserving database pushdown), then a ggsql DuckDBReader processes the VISUALISE portion to produce a Spec. Title extraction now reads from the rendered Vega-Lite JSON rather than regex-parsing the VISUALISE string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5a7e9de to
9160e24
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a footer bar to inline chart tool results with a Show/Hide Query toggle (backed by a read-only bslib code editor), a Save dropdown for PNG/SVG export via the Vega view API, and a clipboard copy button. Includes a ggsql Prism grammar extension (ggsql-grammar.js) that adds VISUALISE/DRAW/LABEL/FACET/SCALE/THEME keyword highlighting at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- htmltools doesn't have `tags.path`, use `Tag("path", ...)` instead
- Extend SQL grammar in-place rather than creating separate ggsql
language, since input_code_editor uses language="sql"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f1aef08 to
1c3c8e3
Compare
Replace hardcoded 300px chart height with CSS aspect-ratio (4/3) on the widget container, so charts scale proportionally to chat width. Uses height="container" + width="container" in Altair so Vega-Lite fills the aspect-ratio box. Full-screen mode unsets the aspect-ratio to allow flexbox-based filling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hide Vega's built-in save/export dropdown since the viz card already has its own save button. Also change chart aspect ratio from 4/3 to 4/2 to reduce chart height. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Translate the TextMate grammar from posit-dev/ggsql into Prism format. Fix import bug (was importing languageMap, not languages registry). Add token types: geom, aesthetic, scale-type, theme, project, functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Translate the TextMate grammar from posit-dev/ggsql into Prism format. Fix import bug (was importing languageMap from index.js, not languages from the internal grammar registry). Add token types: geom, aesthetic, scale-type, theme, project, functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
input_code_editor has a built-in copy button extension, so the custom one is redundant. Removes the button HTML, its JS click handler, and its CSS rules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Custom token names (ggsql-keyword, ggsql-geom, etc.) have no CSS in the Prism theme. Use Prism's alias feature to map each to a standard token class (keyword, builtin, attr-name, class-name, operator). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous polling approach extended the grammar AFTER the code editor had already tokenized, so ggsql tokens never rendered. Now uses Object.defineProperty to intercept the moment sql.js assigns languages.sql, extending it before the editor ever reads it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates ggsql with querychat's Shiny integration to enable LLM-generated data visualizations via a new
visualize_querytool that extends the existing tool system.Uses ggsql's two-stage API (
reader.execute()→writer.render()) to gain access to the writer-independentSpecobject, enabling structured metadata access (row/column counts, VISUALISE clause, etc.) rather than regex-parsing raw strings.New Features
visualize_querytool (opt-in): Creates charts from SQL queries with aVISUALISEclause. The LLM writes a full ggsql query (SQL + VISUALISE), which is executed and rendered as an Altair chart.Two-stage ggsql pipeline: A hybrid
execute_ggsql()helper uses DataSource for SQL execution (preserving database pushdown for SQLAlchemy/ibis backends), then feeds the result into aggsql.DuckDBReaderfor VISUALISE processing →Spec→VegaLiteWriter→ Altair chart.Accessor methods on
QueryChatExpress:ggvis()— Returns rendered Altair chart objectggsql()— Returns ggsql specification stringggtitle()— Returns visualization titleTabbed UI: Shiny
app()displays Data / Query Plot tabs when viz tool is enabledSystem prompt: ggsql syntax reference included when the visualization tool is enabled
Installation
The visualization tool requires the
vizextra:This installs
ggsql,altair, andshinywidgetsas optional dependencies.Usage
The visualization tool is opt-in via the
toolsparameter:The default remains
("update", "query")— no visualization unless explicitly requested.Key Files
pkg-py/src/querychat/_ggsql.py—execute_ggsql(),spec_to_altair(),to_polars(),extract_title()pkg-py/src/querychat/tools.py—tool_visualize_queryimplementationpkg-py/src/querychat/prompts/tool-visualize-query.md— Tool prompt templatepkg-py/src/querychat/prompts/prompt.md— System prompt with ggsql syntax sectionpkg-py/src/querychat/_shiny.py— Shiny UI (Query Plot tab, Express accessors)pkg-py/src/querychat/_shiny_module.py— Reactive viz state and chart renderingpyproject.toml—vizoptional extraTest Coverage
test_ggsql.py—execute_ggsqlpipeline,spec_to_altair,extract_title,ggsql.validate()teststest_viz_tools.py— Visualization tool unit tests (dependency checks, tool creation, rendering, error handling)test_visualization_tabs.py— Playwright tests for Shiny viz tabsBefore Merging
🤖 Generated with Claude Code