Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

New features

  • The SQL panel in .app() is now an editable code editor. Users can tweak the generated SQL directly and apply it with Ctrl/Cmd+Enter or by clicking away — no extra button required. The editor stays in sync when the LLM updates the query or the active table changes. (#265)

  • QueryChat() now supports multiple related tables. Register additional tables with add_table() and the LLM can reason across all of them — joins, cross-table filters, aggregations. Per-table reactive state (df(), sql(), title()) is accessible via qc_vals.table("name") on the value returned by server(). For SQLAlchemy engines and Ibis backends, add_tables() registers all tables (or a named subset) in a single call. (#195)

    qc = QueryChat(orders_df, "orders")
    qc.add_table(customers_df, "customers")
    
    # Or, register all tables from a SQLAlchemy engine or Ibis backend at once:
    qc = QueryChat()
    qc.add_tables(engine)       # SQLAlchemy engine
    qc.add_tables(ibis_backend) # Ibis backend
    
    qc_vals = qc.server()
    qc_vals.table("orders").df()
    qc_vals.table("customers").sql()
  • A new DataDict type — integrating with the data-dict spec — lets you annotate tables and columns with plain-English descriptions loaded from a YAML file. This is the preferred way to provide additional context for the data, especially when multiple tables are relevant. The LLM receives these descriptions when it fetches the schema, helping it interpret ambiguous or domain-specific column names without any extra prompting. (#195)

    QueryChat(data_dict="data_dict.yaml")
  • Added PinSource, a data source for chatting with datasets pinned to a pins board. Works with parquet, CSV, JSON, and Arrow pins, and uses the pin’s title, description, and tags as the default data description. Install the optional dependency with pip install querychat[pins]. (#246)

  • File attachments are now enabled by default in the Shiny chat UI. Users can attach images, PDFs, and text files to their messages and the LLM will receive them. Disable with allow_attachments=False in mod_ui() or QueryChat.ui(). (#253)

Breaking Changes

  • The data_source property has been removed. Use qc.table("name").data_source to read a table’s data source, and qc.add_table(df, "name", replace=True) to replace it. The data_source parameter to server() (Shiny) has also been removed; call add_table() before server() instead. (#195)

Improvements

  • Chat greetings now use shinychat’s greeting API (requires shinychat >= 0.4.0). A provided greeting renders instantly when the app loads, and when no greeting is given one is generated on demand — now schema-aware, so it can describe the data it’s about to help you explore — without being added to the conversation history. Generated greetings are preserved across bookmark/restore. Tables passed to QueryChat() are described in the greeting automatically; opt additional tables in with include_in_greeting=True on add_table()/add_tables(), or fine-tune which tables and which template the greeting uses via qc.greeter. (#249, #261)

  • The system prompt is now lighter: full schema is no longer embedded upfront. Instead the LLM fetches per-table schema on demand via the new querychat_get_schema tool — and only when it needs to. When a DataDict is provided, the tool skips columns that already have descriptions, so the LLM only pays for what isn’t already documented. (#195)

  • The query tool result card now starts collapsed by default. Users can still expand it to see the SQL query and results. Set QUERYCHAT_TOOL_DETAILS=expanded to restore the previous behavior. (#239)

  • Fixed data_description and extra_instructions being HTML-escaped in the system prompt. Special characters like <, >, and & in developer-provided descriptions and instructions are now passed to the LLM verbatim. (#258)

[0.6.1] - 2026-05-26

New features

  • Added stream cancellation support. A stop button now appears during LLM streaming, allowing users to cancel in-progress responses by clicking it or pressing Escape. Cancellation is enabled by default and can be disabled via enable_cancel=False in the UI. (#241)

Improvements

  • The tools parameter now uses "filter" as the preferred name (instead of "update") for the dashboard-filtering tool group. The default is now ("filter", "query"). The legacy name "update" is still accepted everywhere. (#222)
  • Suggestion prompts now render more reliably as interactive cards across LLM providers. (#236, #238)
  • Bumped minimum ggsql version to >=0.3.2. (#233)

[0.6.0] - 2026-05-06

New features

  • Added a "visualize" tool that lets the LLM create inline Altair charts from natural language requests using ggsql — a SQL extension for declarative data visualization. Include it via tools=("query", "visualize") (or alongside "filter"; "update" remains a legacy alias). Charts render inline in the chat with fullscreen support, a “Show Query” toggle, and Save as PNG/SVG. Install the optional dependencies with pip install querychat[viz]. (#219)

  • QueryChat() now supports deferred chat client initialization. Pass client= to server() to provide a session-scoped chat client, enabling use cases where API credentials are only available at session time (e.g., Posit Connect managed OAuth tokens). When no client is specified anywhere, querychat resolves a sensible default from the QUERYCHAT_CLIENT environment variable (or "openai"). (#205)

  • Added support for Snowflake Semantic Views. When connected to Snowflake (via SQLAlchemy or Ibis), querychat automatically discovers available Semantic Views and includes their definitions in the system prompt. This helps the LLM generate correct queries using the SEMANTIC_VIEW() table function with certified business metrics and dimensions. (#200)

  • The querychat_query tool now accepts an optional collapsed parameter. When collapsed=True, the result card starts collapsed so preparatory or exploratory queries don’t clutter the conversation. The LLM is guided to use this automatically when running queries before a visualization.

Improvements

  • When a custom prompt_template is provided that doesn’t contain Mustache references to {schema}, the expensive get_schema() call is now skipped entirely. This allows users with large databases to avoid slow startup by providing their own prompt that includes schema information inline (or omits it). (#208)

[0.5.1] - 2026-01-23

New features

  • QueryChat() now supports deferred data source initialization for Shiny Core applications. Pass data_source=None at initialization time, then provide the actual data source via the data_source parameter of server() or by setting the data_source property. This enables use cases where the data source depends on session-specific authentication or per-user database connections. (#202)

[0.5.0] - 2026-01-16

New features

  • Added support for Gradio, Dash, and Streamlit web frameworks in addition to Shiny. Import from the new submodules:
    • from querychat.gradio import QueryChat
    • from querychat.dash import QueryChat
    • from querychat.streamlit import QueryChat

Each framework’s QueryChat provides .app() for quick standalone apps and .ui() for custom layouts. Install framework dependencies with pip extras: pip install querychat[gradio], pip install querychat[dash], or pip install querychat[streamlit]. (#190)

  • QueryChat() gains support for more data sources:
    • polars.LazyFrame: queries execute lazily via polars.SQLContext. In this case, .df() et al. methods will return a polars.LazyFrame. (#191)
    • ibis.Table: queries execute lazily via the Ibis backend’s SQL interface (DuckDB, PostgreSQL, BigQuery, etc.). In this case, .df() et al. methods will return an ibis.Table. (#193)
    • pyarrow.Table: queries execute in-memory via duckdb. In this case, .df() et al. methods will return a pyarrow.Table. (#196)

Improvements

  • Improved typing support for return types on .df() et al. (#196)

Changes

  • DataFrameSource methods now (once again) return the input DataFrame type (e.g., pandas.DataFrame) instead of nw.DataFrame. (#196)

[0.4.0] - 2026-01-14

Breaking Changes

  • Methods like execute_query(), get_data(), and df() now return a narwhals.DataFrame instead of a pandas.DataFrame. This allows querychat to drop its pandas dependency, and for you to use any narwhals-compatible dataframe of your choosing.
    • If this breaks existing code, note you can call .to_native() on the new dataframe value to get your pandas dataframe back.
    • Note that polars or pandas will be needed to realize a sqlalchemy connection query as a dataframe. Install with pip install querychat[pandas] or pip install querychat[polars]

New features

  • Added PolarsLazySource to support Polars LazyFrames as data sources. Data stays lazy until the render boundary, enabling efficient handling of large datasets. Pass a polars.LazyFrame directly to QueryChat() and queries will be executed lazily via Polars’ SQLContext.

  • QueryChat.console() was added to launch interactive console-based chat sessions with your data source, with persistent conversation state across invocations. (#168)

  • QueryChat.client() can now create standalone querychat-enabled chat clients with configurable tools and callbacks, enabling use outside of Shiny applications. (#168)

  • The tools used in a QueryChat chatbot are now configurable. Use the new tools parameter of QueryChat() to select either or both "query" or "update" tools. Choose tools=["update"] if you only want QueryChat to be able to update the dashboard (useful when you want to be 100% certain that the LLM will not see any raw data). (#168)

  • QueryChat.sidebar(), QueryChat.ui(), and QueryChat.server() now support an optional id parameter to create multiple chat instances from a single QueryChat object. (#172)

Improvements

  • The update tool now requires that the SQL query returns all columns from the original data source, ensuring that the dashboard can display the complete data frame after filtering or sorting. If the query does not return all columns, an informative error message will be provided. (#180)

  • Obvious SQL keywords that lead to data modification (e.g., INSERT, UPDATE, DELETE, DROP, etc.) are now prohibited in queries run via the query tool or update tool, to prevent accidental data changes. If such keywords are detected, an informative error message will be provided. (#180)

[0.3.0] - 2025-12-10

Breaking Changes

  • The entire functional API (i.e., init(), sidebar(), server(), etc) has been hard deprecated in favor of a simpler OOP-based API. Namely, the new QueryChat() class is now the main entry point (instead of init()) and has methods to replace old functions (e.g., .sidebar(), .server(), etc). (#101)

  • The .sql() method now returns None instead of "" (empty string) when no query has been set, aligning with the behavior of .title() for consistency. Most code using the or operator or req() for falsy checks will continue working without changes. Code that explicitly checks sql() == "" should be updated to use falsy checks (if not sql()) or explicit null checks (if sql() is None). (#146)

New features

  • New QueryChat.app() method enables quicker/easier chatting with a dataset. (#104)

  • Enabled bookmarking by default in both .app() and .server() methods. In latter case, you’ll need to also specify the bookmark_store (either in shiny.App() or shiny.express.app_opts()) for it to take effect. (#104)

  • The current SQL query and title can now be programmatically set through the .sql() and .title() methods of QueryChat(). (#98, #101)

  • New querychat.data module provides sample datasets (titanic() and tips()) to make it easier to get started without external dependencies. (#118)

  • Added a .generate_greeting() method to help you create a greeting message for your querychat bot. (#87)

  • Added querychat_reset_dashboard() tool for easily resetting the dashboard filters when asked by the user. (#81)

Improvements

  • Added rich tool UI support using shinychat development version and chatlas >= 0.11.1. (#67)

  • querychat’s system prompt and tool descriptions were rewritten for clarity and future extensibility. (#90)

  • Tool detail cards can now be expanded or collapsed by default when querychat runs a query or updates the dashboard via the QUERYCHAT_TOOL_DETAILS environment variable. Valid values are "expanded", "collapsed", or "default". (#137)

[0.2.2] - 2025-09-04

  • Fixed another issue with data sources that aren’t already narwhals DataFrames (#83)

[0.2.1] - 2025-09-04

  • Fixed an issue with the query tool when used with SQLAlchemy data sources. (@npelikan #79)

[0.2.0] - 2025-09-02

  • querychat.init() now accepts a client argument, replacing the previous create_chat_callback argument. (#60)

    The client can be:

    • a chatlas.Chat object,
    • a function that returns a chatlas.Chat object,
    • or a provider-model string, e.g. "openai/gpt-4.1", to be passed to chatlas.ChatAuto().

    If client is not provided, querychat will use the QUERYCHAT_CLIENT environment variable, which should be a provider-model string. If the envvar is not set, querychat uses OpenAI with the default model from chatlas.ChatOpenAI().

  • querychat.ui() now adds a .querychat class to the chat container and querychat.sidebar() adds a .querychat-sidebar class to the sidebar, allowing for easier customization via CSS. (#68)

[0.1.0] - 2025-05-24

This first release of the querychat package.