Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c3aa06b
feat(pkg-r): add dynamic card dashboard tool
gadenbuie Jun 16, 2026
71ab99c
fix(pkg-r): correct reactive handling and rendering for card tool
gadenbuie Jun 17, 2026
09f0a7b
feat(pkg-r): split card update into replace and patch actions
gadenbuie Jun 17, 2026
9dae2f0
refactor(pkg-r): unify card schema on a single display axis
gadenbuie Jun 17, 2026
38d4f9a
feat(pkg-r): group value boxes into their own card rows
gadenbuie Jun 17, 2026
281bcc1
refactor(pkg-r): split render_card into per-display helpers
gadenbuie Jun 17, 2026
823d441
refactor(pkg-r): use standalone purrr helpers over base vapply/Filter
gadenbuie Jun 17, 2026
9e33da5
feat(pkg-r): action-specific card tool title and patch-first prompts
gadenbuie Jun 17, 2026
df5074f
feat(pkg-r): add card tool "get" action; return structured JSON
gadenbuie Jun 17, 2026
d9b9b38
feat(pkg-r): use short 4-char collision-safe card ids
gadenbuie Jun 17, 2026
57ba327
feat(pkg-r): add Insights tab to the bundled app for cards
gadenbuie Jun 17, 2026
ef3fb96
feat(pkg-r): enable cards and viz by default in querychat_app()
gadenbuie Jun 17, 2026
a6480d4
feat(pkg-r): prompt model to proactively offer to save insights
gadenbuie Jun 17, 2026
02a7f72
refactor(pkg-r): use page_navbar with Data/Insights tabs in bundled app
gadenbuie Jun 17, 2026
ddfc5b7
feat(pkg-r): support selective card bookmarking via enable_bookmarking
gadenbuie Jun 17, 2026
ee848fb
fix(pkg-r): correct card bookmark restore over URL state
gadenbuie Jun 17, 2026
9ce3964
refactor(pkg-r): tighten querychat_card prompting and move param docs…
gadenbuie Jun 18, 2026
acdd899
chore(pkg-py): pre-stage tool-card.md prompt for future card port
gadenbuie Jun 18, 2026
4b8a8b5
feat(pkg-r): teach the model proactive-pinning signals for cards
gadenbuie Jun 18, 2026
e556d90
refactor(pkg-r)!: rename enable_bookmarking to bookmark_enable, ortho…
gadenbuie Jun 22, 2026
65a027f
fix(pkg-r): error when both bookmark_enable and enable_bookmarking ar…
gadenbuie Jun 22, 2026
3ba4dcb
fix(pkg-r): validate table cards with a fast validate_query() executo…
gadenbuie Jun 23, 2026
ec14e96
fix(pkg-r): restore an executor type-check in tool_card()
gadenbuie Jun 23, 2026
7b80063
test(pkg-r): use bookmark_enable in mod_server() testServer calls
gadenbuie Jun 23, 2026
fe24c15
feat(pkg-r): default bookmark_store to NULL with smart resolution
gadenbuie Jun 23, 2026
d3455b8
refactor(pkg-r): extract validate_and_build_card() from tool_card_impl()
gadenbuie Jun 23, 2026
009bfec
feat(pkg-r): shareable cards URL (Feature A)
gadenbuie Jun 23, 2026
d79cb43
feat(pkg-r): author-seeded initial cards (Feature B)
gadenbuie Jun 23, 2026
1ce2880
feat(pkg-r): default share link in querychat_app() Insights panel (Fe…
gadenbuie Jun 23, 2026
6e7784b
docs(pkg-r): document shareable + author-seeded cards
gadenbuie Jun 23, 2026
777714c
chore: small follow ups
gadenbuie Jun 23, 2026
ab7a79b
feat(pkg-r): open Insights tab when seeded with cards
gadenbuie Jun 23, 2026
018c802
feat(pkg-r): show SQL query in value box full-screen mode
gadenbuie Jun 24, 2026
edc5eba
refactor(pkg-r): split card `value` field into `query` + `text`
gadenbuie Jun 24, 2026
561a24d
feat(pkg-r): value_box column-mapping from query results
gadenbuie Jun 24, 2026
c7255df
feat(pkg-r): add mustache interpolation for markdown cards
gadenbuie Jun 24, 2026
561b423
refactor(pkg-r): merge `caption` into `text`, drop theme validation, …
gadenbuie Jun 26, 2026
ab5d1f4
fix(pkg-r): migrate legacy card fields and update theme prompt
gadenbuie Jun 26, 2026
b96fc54
`air format` (GitHub Actions)
gadenbuie Jun 26, 2026
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
57 changes: 57 additions & 0 deletions pkg-py/src/querychat/prompts/tool-card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Add, replace, patch, or remove a persistent card in the dashboard cards area

Cards live in a developer-placed dashboard area and stay visible across queries. Use them to surface insights the user wants to keep in view (a key metric, a notable ranking, a trend, or a written takeaway), not to echo every query result. Add a card when the user asks to "pin", "save", or "add to the dashboard", or when you have answered a question and a persistent summary would clearly add lasting value.

Match the display to the finding:

- **value_box**: a single key metric. The SQL query must return exactly 1 row. The displayed number comes from the `value` column (or the first column if no `value` column). Columns named `title`, `text`, `theme`, or `icon` override the static card fields, enabling dynamic theming (e.g. `CASE WHEN ... THEN 'danger' ELSE 'success' END AS theme`).
- **table**: a ranked or comparative result set the user wants to see at a glance.
- **visualization**: a trend, distribution, or comparison that reads better as a chart.
- **markdown**: a written takeaway or note. Use the `text` field for the markdown body. Optionally supply a `query` (SQL returning exactly 1 row) whose columns become `{{var}}` placeholders in `text` for live interpolation (e.g. `Revenue grew {{pct}}% to {{total}}`).

For a small set of related metrics (roughly 3-4 or fewer), add a separate value_box for each one; a row of value boxes reads better than one table of headline numbers.

Query-backed cards (table, visualization, value_box) are validated by running the query before the card is added, replaced, or patched. If a query fails you receive the error message; fix the query and retry at least once before reporting failure to the user.

Parameters
----------
action :
The operation to perform.
- `"add"`: create a new card. Requires `display`, `title`, and `query` (or `text` for markdown).
- `"patch"`: the preferred way to edit a card. Send the `id` and only the fields you are changing; omitted fields keep their current values. Cannot clear an optional field; use `"replace"` for that.
- `"replace"`: fully overwrite a card. Send the `id` and every field for the new version (same requirements as `"add"`; changing `display` is allowed). Omitted optional fields are cleared.
- `"remove"`: delete a card. Requires only `id`.
- `"get"`: read existing cards. Omit `id` for all cards, or pass an `id` for one. Use it to discover card `id`s and their current contents before a patch, replace, or remove.
id :
The short card identifier. Required for `"replace"`, `"patch"`, and `"remove"`; optional for `"get"` (omit to return all cards); omit for `"add"`.
display :
Which renderer to use; required for `"add"` and `"replace"`. One of `"table"`, `"visualization"`, `"markdown"`, or `"value_box"`, as described above.
title :
A brief card heading shown in the card header. Required for `"add"` and `"replace"`.
query :
The data query; required for table, visualization, and value_box displays; optional for markdown (interpolation). Its meaning depends on `display`:
- `"table"`: a valid {{db_type}} SQL SELECT query.
- `"visualization"`: a full ggsql query including a VISUALISE clause. Do NOT include `LABEL title => ...`; use the `title` parameter instead.
- `"value_box"`: a {{db_type}} SQL SELECT query returning exactly 1 row. The displayed number comes from the `value` column (or the first column). Additional columns named `title`, `text`, `theme`, or `icon` override the static card fields. Format the displayed value as a human-readable string in SQL (thousands separators, currency, rounding, a `%` suffix, etc.).
- `"markdown"` (optional): a {{db_type}} SQL SELECT query returning exactly 1 row. Its columns become `{{var}}` placeholders in the `text` body.
text :
Supplementary text; its role depends on `display`:
- `"markdown"` (required): the body content, rendered as HTML via markdown. If a `query` is also supplied, its single-row columns are interpolated as `{{var}}` placeholders.
- `"table"` / `"visualization"`: a brief footer shown below the content.
- `"value_box"`: the subtitle shown under the main value.
theme :
Optional Bootstrap theme name for a value_box background (e.g. `primary`, `secondary`, `success`, `danger`, `warning`, `info`). Any valid Bootstrap theme class is accepted. Applies to value_box only; ignored for other displays.
icon :
Optional Bootstrap icon name (e.g., `"bar-chart"`, `"currency-dollar"`, `"people-fill"`). Honored by every display: the showcase icon for value_box, and shown beside the title for table/visualization/markdown.

Returns
-------
:
For `"add"`, `"replace"`, `"patch"`, and `"remove"`: a JSON object with the
affected card's `id` and a `status` (e.g. `{"id": "a3f7", "status": "added"}`).
For `"get"`: a single card object when an `id` is given, otherwise a JSON array
of all cards. Each card object holds the card's full definition (`id`, `display`,
`title`, `query`/`text`, and any optional fields), e.g.
`{"id": "a3f7", "display": "value_box", "title": "Total Revenue", "query": "SELECT ..."}`.
If a query-backed card fails validation, an error message is returned instead and
no card is created or changed.
6 changes: 6 additions & 0 deletions pkg-r/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@

* 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)

* Card dashboards can now be **shared and author-seeded** without the LLM. `$cards_url()` encodes the current cards into a compact URL, and `$cards_set_url()` updates the address bar to that link; opening such a URL seeds the dashboard with exactly those cards and a fresh conversation. The new `cards` argument to `QueryChat$new()` (and `querychat()`) seeds an initial dashboard from a list of cards, a JSON string, or a path to a `.json` file. The bundled `querychat_app()` Insights panel shows an "open in new tab" link for the current cards.

## 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()` has also been removed; call `$add_table()` before `$server()` instead. (#195)

## Improvements

* The `$server()` argument `enable_bookmarking` has been renamed to `bookmark_enable` (the old name is deprecated but still works). It now also selects *which* categories of state to bookmark, accepting `TRUE`/`FALSE` or a subset of `c("conversation", "cards")`. `bookmark_enable` is also available on `$app()`, `$app_obj()`, and `querychat_app()`, where `bookmark_store` now solely controls *where* state is stored.

* The `bookmark_store` argument of `$app()`, `$app_obj()`, and `querychat_app()` now defaults to `NULL` instead of `"url"`. With `NULL`, querychat defers to a store you set yourself via `shiny::enableBookmarking()`, and otherwise picks a sensible default: `"server"` when the conversation is bookmarked or when running on a hosting platform (detected via `R_CONFIG_ACTIVE`), and `"url"` otherwise. Pass `bookmark_store` explicitly to override.

* 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$new()` 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 `data_dict` is provided, the tool skips columns that already have descriptions, so the LLM only pays for what isn't already documented. (#195)
Expand Down
Loading