Add persistent docked ChatGXY panel with context awareness#22096
Draft
dannon wants to merge 26 commits intogalaxyproject:devfrom
Draft
Add persistent docked ChatGXY panel with context awareness#22096dannon wants to merge 26 commits intogalaxyproject:devfrom
dannon wants to merge 26 commits intogalaxyproject:devfrom
Conversation
28cde8e to
5afeef8
Compare
Third modality for ChatGXY alongside center view and scratchbook: a resizable right-side column that pushes center content rather than overlaying it. New chatStore tracks docked panel state, FlexPanel gets a panelId prop to avoid duplicate element IDs, and ChatGXY gains a docked mode with its own slim header. Dock/undock from center view header, close from the panel, or open from the left ChatHistoryPanel. Center /chatgxy route and docked panel are mutually exclusive.
New useActiveContext composable watches the Vue route and detects what the user is working with (tool form, dataset, workflow editor, etc.) and surfaces it as a context chip in the docked panel header. The structured context is JSON-serialized into the existing context string field and sent with each chat message. On the backend, the API handler parses JSON context and passes it through to agents, where it gets formatted as natural language in the prompt so the LLM knows what the user is looking at without them having to say it.
Activity bar click now navigates to the center view instead of toggling
the bottom panel. Sidebar history items also navigate to /chatgxy/{id}.
The bottom panel remains available as a dock-down target.
Remove onPanelClicked wrapper in ActivityBar (was just forwarding to toggleSidebar). Consolidate two route watchers in Analysis.vue into one. Move useChatStore() calls to setup time instead of inline in function bodies.
Rewrites chatStore as the single source of truth for all chat panel state. Previously the chat location (center/right/bottom) was managed across three separate stores and mechanisms. Now chatStore owns chatLocation (persisted), chatVisible (persisted), and activeChatId, with computed properties for each panel position. All consumers (ActivityBar, Analysis, ChatGXY, ChatPanel, ChatHistoryPanel) now go through the unified API, and the chat fields have been removed from activityStore.
- openDockedChat: set location to "right" when in center mode so the dock button actually does something visible - onChatGxyClick: only toggle the left sidebar in center mode to avoid showing two chat surfaces simultaneously in docked mode - dockToSide/dockToBottomPanel: only navigate to "/" when currently on /chatgxy, preserving the user's current route otherwise
Covers all route-matching branches in useActiveContext (tool, dataset, workflow editor/run, job) including the upload1 exclusion and tool name resolution. chatStore tests cover visibility toggling, location changes, and the computed panel-state predicates.
Don't persist chatVisible to localStorage -- the panel location is a preference worth keeping, but visibility is session state that shouldn't survive page reloads. Fix confirmDialogRef to mutate .value instead of reassigning the ref variable. Restore canPurgeSelection guard so the purge menu item is hidden when all selected items are already purged. Fix selectedDbKey reset to include the text property, and clear selectedTags when tag modals are dismissed. Light up the activity bar icon when the docked chat panel is open. Sanitize user-supplied context values in _format_interface_context to strip newlines and cap lengths before they hit the LLM prompt. Trim confirmDialog.ts down to the fields actually used by callers and remove verbose JSDoc.
Users can now type @ in the chat input to reference specific datasets (by HID) or histories from the current session. A floating dropdown with keyboard navigation helps pick entities, mentions render as styled chips in message history, and resolved entity metadata gets passed through to the agent so it can ground its answers on the specific data the user is asking about.
Deduplicate the mention regex (single source of truth in useEntityMentions), convert getMenuItems to a computed, hoist store access to setup scope, collapse redundant key-check branches in ChatInput, and drop WHAT-not-WHY comments. Fix ChatInput tests for the hoisted store access.
These fields are declared on the Pydantic model and the TS interface but never populated or read -- nothing sets them in resolveMentions and nothing reads them downstream. Remove the noise.
galaxy.util.json.safe_loads already handles the try/except-and-fall-back pattern -- returns the parsed dict on success, the original string on failure. Swap it in instead of reimplementing the try/except inline.
Rendering user-authored content through v-html with a hand-rolled escapeHtml is a fragile pattern -- one forgotten escape step and it's an XSS hole. The FontAwesome icons were also hardcoded as raw unicode codepoints that would silently break if the FA version changed. Parse the message into segments once, render text and mention chips in the template, and use the FontAwesomeIcon component for the icons.
dockToSide and dockToBottomPanel were identical except for the location literal. Fold them into one function and pass the target from the template button handlers.
The separate visible watcher installed autoUpdate, and the combined watcher ran updatePosition() directly -- so opening the dropdown computed the position twice (once eagerly, once via autoUpdate's initial callback). Collapse into one watcher that just installs and tears down autoUpdate; its initial callback covers the first layout.
chatLocation and chatVisible are persisted through useUserLocalStorage, so every assignment writes to localStorage and notifies watchers. Guard each setter against same-value writes so repeated calls (e.g. hideChat on every /chatgxy route hit, setActiveChatId on every selection change) don't thrash downstream consumers.
…context
mypy complained about the variable being re-typed: the tool branch
assigns tool_id = _s(...) (str), and the job branch was reassigning
tool_id = ctx.get("toolId") (Any | None) in the same function scope.
Give the job variant a distinct name -- the two mean different things
anyway (tool being viewed vs. tool that produced the job).
Covers _format_interface_context (tool/dataset/etc routes, sanitization, truncation, unknown types) and _format_entity_context (datasets and histories from @mentions). The three sanitization tests for _format_entity_context fail intentionally and demonstrate a prompt injection vector -- next commit fixes that.
_format_interface_context already runs every user-supplied field through _sanitize_context_value (strips newlines, caps length); _format_entity_context was interpolating dataset/history names, extension, and state verbatim. A renamed dataset like "Mapped reads\n\nNew system instruction:..." would inject directly into the LLM prompt. Apply the same sanitizer here.
Adds coverage for the previously untested resolveMentions code path: dataset by hid, dataset by name fragment, unresolved hid fallback, @history:current, history by id, and missing history. Mocks the two Pinia stores via vi.mock so the test stays focused on the resolution logic rather than store internals.
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.
ChatGXY currently only works as a full-page view. This adds the ability to dock it as a persistent side or bottom panel so you can chat with the assistant while continuing to work in Galaxy.
The more interesting piece is context attachment -- a new composable watches the current route and derives what the user is looking at (which tool form is open, which dataset or workflow they're viewing, which job they're inspecting). When ChatGXY is docked, this context is automatically passed to the backend agent, so the assistant understands your current Galaxy interface state without you having to explain it. The backend formats this into natural language and prepends it to the query, giving the router agent situational awareness of what you're doing.
The panel location (center/right/bottom) and visibility are persisted per-user via the existing
useUserLocalStoragepattern, so the preference survives page reloads and sessions. The activity bar item adapts its behavior based on mode -- navigating to the full-page view when in center mode, or toggling the docked panel when docked.