feat(artifact): turn a querychat session into a standalone, runnable artifact (Python)#245
Draft
cpsievert wants to merge 3 commits into
Draft
feat(artifact): turn a querychat session into a standalone, runnable artifact (Python)#245cpsievert wants to merge 3 commits into
cpsievert wants to merge 3 commits into
Conversation
cpsievert
commented
Jun 11, 2026
Turn the work done during a querychat session — the SQL queries and ggsql visualizations produced by asking questions — into a standalone, runnable, downloadable artifact: a Quarto dashboard, Shiny app, Marimo or Jupyter notebook, or a freeform format the LLM describes on demand. Python-only (pkg-py); no R counterpart yet. What the user sees: - Open a "Create Artifact" modal via the `/artifact` slash command or when the LLM calls the `querychat_request_artifact` tool. Both paths converge on a single modal-opening flow. - Pick from a gallery of the session's queries/visualizations plus an output format, language (R/Python), and directions; an LLM recommendation pre-selects sensible defaults. - On Generate, a side panel opens and the artifact source streams into a read-only editor while a pill is appended to the chat. - Revise with AI (pushing new versions), step through versions, and download a zip (source + README.md + bundled data). Artifacts survive bookmarking. Architecture (see memory-bank/artifact-feature.md): - artifact_server (_artifact_server.py) owns all reactivity; active_artifact_id is the single source of truth for panel visibility. - ArtifactOrchestrator (_artifact_orchestrator.py) is non-reactive business logic for every flow, testable with plain fakes. - One owner per concern: ArtifactView (server→client wire protocol, modal, pill), ArtifactChat (chatlas transport; forks the chat so generation never pollutes the visible history), ArtifactStore (LRU container + bookmark serialization). - ArtifactState/ArtifactVersion and the request/type models are pydantic; bundled data and data instructions are excluded from bookmarks and regenerated from the live data source on restore. - Tool names are centralized in _tool_names.py so tool registration and gallery extraction can't drift. - Front-end artifact-core.ts / artifact.css are built to static/ (mirroring the viz feature); the Py↔JS message contract is hand-duplicated on both sides. Includes unit tests per module (orchestrator suite uses plain fakes) and Playwright integration tests, plus a memory-bank entry documenting the design.
a18c0a7 to
cdbed9f
Compare
shinychat switched from a <textarea> to a TipTap contenteditable div on @main, breaking two categories of e2e tests: 1. `test_chat_input_visible` expected a native `placeholder` attribute, but TipTap uses `data-placeholder` instead. 2. Artifact gallery/generation tests relied on `to_be_editable()` to detect stream completion, but the contenteditable div is always editable (its attribute never flips to "false"). The correct signal is the container's `disabled` CSS class, which shinychat adds/removes when streaming starts/ends.
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.
What this adds
querychat is great for exploring data conversationally — but today that exploration is trapped in the session. Close the tab and the queries and charts you built are gone. This PR lets a user turn the work they did during a querychat session into a standalone, runnable, downloadable artifact: a Quarto dashboard, a Shiny app, a Marimo or Jupyter notebook, or a freeform format they describe in words.
In short: chat your way to the right queries and visualizations, give the session a prompt, and walk away with a real file you can run, share, version-control, or drop into a report — no copy-pasting SQL out of the UI.
Why it's useful
See it in action
Upon requesting a new artifact:
After generating the artifact:
How it works (user's-eye view)
/artifact, or the assistant offers it when it senses you're ready.Approach & architecture (for reviewers)
Built around a few deliberate constraints (full write-up in
memory-bank/artifact-feature.md):ArtifactOrchestratoris plain async business logic — reads noreactive.Value, defines no effects, never touchesinput.*. All reactivity lives inartifact_server(). That's what lets every flow be unit-tested with plain fakes.ArtifactView— the only place server→client output happens (custom messages, modal, chat pill).ArtifactChat— the only place chatlas is touched; deep-copies the live chat before each call, so generate/revise/recommend never pollute the visible conversation.ArtifactStore— the only place artifacts are held (LRU) and serialized.active_artifact_id— a single reactive value, the sole source of truth for panel visibility.ArtifactState/ArtifactVersionare pydantic models; bundled data + data instructions areexclude=Trueand regenerated from the live data source on restore (mirroring how viz widgets re-run ggsql)._tool_names.py, so tool registration and the gallery that mines chat turns can't silently drift./artifactcommand and thequerychat_request_artifacttool converge on the same flow (the tool fires mid-stream where no reactive context exists, so it relays through a status-gated effect).Limitations & things to know
input_submit_textarea/input_code_editorand a recentshiny;pyproject.tomlcurrently pins git refs for both (shinychat →worktree-feat+slash-commands). These must move to released versions before any release./artifactcommand register regardless of thetools=config, and the panel UI is always injected. Deliberate by omission — worth revisiting.static/js/artifact.js/static/css/artifact.cssare build outputs ofjs/src/*— edit the source and rebuild, never the generated files.Testing
tests/test_artifact_*.py); the orchestrator suite runs on plain fakes thanks to the no-reactive-state rule.test_13_artifact*,test_14_artifact_bookmark) covering the modal, generation, panel, pill navigation, and bookmark restore.