Merge pull request #22707 from dannon/rebrand-chatgxy

Rebrand ChatGXY to GalaxyAI
This commit is contained in:
Marius van den Beek
2026-05-21 08:27:52 +02:00
committed by GitHub
26 changed files with 113 additions and 113 deletions
@@ -16,7 +16,7 @@ import { useUnprivilegedToolStore } from "@/stores/unprivilegedToolStore";
import { useUserStore } from "@/stores/userStore";
import localize from "@/utils/localization";
import ChatHistoryPanel from "../ChatGXY/ChatHistoryPanel.vue";
import ChatHistoryPanel from "../GalaxyAI/ChatHistoryPanel.vue";
import InvocationsPanel from "../Panels/InvocationsPanel.vue";
import ActivityBarHeader from "./ActivityBarHeader.vue";
import ActivityBarSeparator from "./ActivityBarSeparator.vue";
@@ -127,7 +127,7 @@ const activities = computed({
if (activity.id === "interactivetools" && !config.value?.interactivetools_enable) {
return false;
}
if (activity.id === "chatgxy" && !config.value?.llm_api_configured) {
if (activity.id === "galaxyai" && !config.value?.llm_api_configured) {
return false;
}
return true;
@@ -139,7 +139,7 @@ const activities = computed({
(activity) =>
(activity.id === "user-defined-tools" && !canUseUnprivilegedTools.value) ||
(activity.id === "interactivetools" && !config.value?.interactivetools_enable) ||
(activity.id === "chatgxy" && !config.value?.llm_api_configured),
(activity.id === "galaxyai" && !config.value?.llm_api_configured),
);
storeActivities.value = [...newActivities, ...filteredOut];
},
@@ -425,7 +425,7 @@ defineExpose({
<InvocationsPanel v-else-if="isActiveSideBar('invocation')" />
<VisualizationPanel v-else-if="isActiveSideBar('visualizations')" />
<MultiviewPanel v-else-if="isActiveSideBar('multiview')" />
<ChatHistoryPanel v-else-if="isActiveSideBar('chatgxy')" />
<ChatHistoryPanel v-else-if="isActiveSideBar('galaxyai')" />
<NotificationsPanel v-else-if="isActiveSideBar('notifications')" />
<UserToolPanel v-if="isActiveSideBar('user-defined-tools')" in-panel />
<InteractiveToolsPanel v-else-if="isActiveSideBar('interactivetools')" />
@@ -10,12 +10,12 @@ import { type AgentResponse, useAgentActions } from "@/composables/agentActions"
import { useMarkdown } from "@/composables/markdown";
import { errorMessageAsString } from "@/utils/simple-error";
import { getAgentIcon } from "./ChatGXY/agentTypes";
import type { ChatHistoryItem, ChatMessage } from "./ChatGXY/chatTypes";
import { generateId, scrollToBottom } from "./ChatGXY/chatUtils";
import { getAgentIcon } from "./GalaxyAI/agentTypes";
import type { ChatHistoryItem, ChatMessage } from "./GalaxyAI/chatTypes";
import { generateId, scrollToBottom } from "./GalaxyAI/chatUtils";
import ChatInput from "./ChatGXY/ChatInput.vue";
import ChatMessageCell from "./ChatGXY/ChatMessageCell.vue";
import ChatInput from "./GalaxyAI/ChatInput.vue";
import ChatMessageCell from "./GalaxyAI/ChatMessageCell.vue";
import Heading from "@/components/Common/Heading.vue";
const props = withDefaults(
@@ -71,7 +71,7 @@ function showWelcome() {
id: generateId(),
role: "assistant",
content:
"Welcome to ChatGXY. Ask about tools, workflows, errors, or data quality " +
"Welcome to GalaxyAI. Ask about tools, workflows, errors, or data quality " +
"and your question will be routed to the appropriate specialist agent.",
timestamp: new Date(),
agentType: "router",
@@ -118,7 +118,7 @@ async function submitQuery() {
});
if (error) {
const errorText = errorMessageAsString(error, "Failed to get response from ChatGXY.");
const errorText = errorMessageAsString(error, "Failed to get response from GalaxyAI.");
const errorMsg: ChatMessage = {
id: generateId(),
role: "assistant",
@@ -322,17 +322,17 @@ async function deleteCurrentChat() {
function popOutToWindowManager() {
const Galaxy = getGalaxyInstance();
const path = currentChatId.value ? `/chatgxy/${currentChatId.value}` : "/chatgxy";
const path = currentChatId.value ? `/galaxyai/${currentChatId.value}` : "/galaxyai";
const url = `${path}?compact=true`;
Galaxy.frame.add({ title: "ChatGXY", url });
Galaxy.frame.add({ title: "GalaxyAI", url });
}
</script>
<template>
<div class="chatgxy-container" :class="{ 'chatgxy-compact': compact }">
<div v-if="!compact" class="chatgxy-header">
<div class="galaxyai-container" :class="{ 'galaxyai-compact': compact }">
<div v-if="!compact" class="galaxyai-header">
<Heading h2 :icon="faMagic" size="lg">
<span>ChatGXY</span>
<span>GalaxyAI</span>
</Heading>
<div class="header-actions">
<button class="btn btn-sm btn-outline-primary" title="Start New Chat" @click="startNewChat">
@@ -380,7 +380,7 @@ function popOutToWindowManager() {
</div>
</div>
<div class="chatgxy-footer">
<div class="galaxyai-footer">
<ChatInput v-model="query" :busy="busy" @submit="submitQuery" />
</div>
</div>
@@ -389,7 +389,7 @@ function popOutToWindowManager() {
<style lang="scss" scoped>
@import "@/style/scss/theme/blue.scss";
.chatgxy-container {
.galaxyai-container {
height: calc(100vh - #{$masthead-height} - 2rem);
display: flex;
flex-direction: column;
@@ -398,19 +398,19 @@ function popOutToWindowManager() {
overflow: hidden;
}
.chatgxy-compact {
.galaxyai-compact {
height: 100vh;
.chat-messages {
padding: 0.75rem 1rem;
}
.chatgxy-footer {
.galaxyai-footer {
padding: 0.5rem 0.75rem;
}
}
.chatgxy-header {
.galaxyai-header {
display: flex;
align-items: center;
justify-content: space-between;
@@ -424,7 +424,7 @@ function popOutToWindowManager() {
}
}
.chatgxy-footer {
.galaxyai-footer {
padding: 0.75rem 1rem;
background: $panel-bg-color;
border-top: $border-default;
@@ -58,9 +58,9 @@ function handleItemClick(item: ChatHistoryItem, index: number, event: MouseEvent
const Galaxy = getGalaxyInstance();
if (Galaxy?.frame?.active) {
// @ts-ignore - monkeypatched router, second arg is RouterPushOptions
router.push(`/chatgxy/${item.id}?compact=true`, { title: "ChatGXY" });
router.push(`/galaxyai/${item.id}?compact=true`, { title: "GalaxyAI" });
} else {
router.push(`/chatgxy/${item.id}`);
router.push(`/galaxyai/${item.id}`);
}
}
@@ -68,9 +68,9 @@ function startNewChat() {
const Galaxy = getGalaxyInstance();
if (Galaxy?.frame?.active) {
// @ts-ignore - monkeypatched router, second arg is RouterPushOptions
router.push("/chatgxy?compact=true", { title: "ChatGXY" });
router.push("/galaxyai?compact=true", { title: "GalaxyAI" });
} else {
router.push("/chatgxy");
router.push("/galaxyai");
}
}
@@ -94,7 +94,7 @@ async function deleteSelected() {
</script>
<template>
<ActivityPanel title="ChatGXY" go-to-all-title="Open ChatGXY" href="/chatgxy">
<ActivityPanel title="GalaxyAI" go-to-all-title="Open GalaxyAI" href="/galaxyai">
<template v-slot:header-buttons>
<button class="btn btn-sm btn-outline-primary" title="New Chat" @click="startNewChat">
<FontAwesomeIcon :icon="faPlus" fixed-width />
@@ -8,8 +8,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { usePageEditorStore } from "@/stores/pageEditorStore";
import PageChatPanel from "./PageChatPanel.vue";
import ChatInput from "@/components/ChatGXY/ChatInput.vue";
import ChatMessageCell from "@/components/ChatGXY/ChatMessageCell.vue";
import ChatInput from "@/components/GalaxyAI/ChatInput.vue";
import ChatMessageCell from "@/components/GalaxyAI/ChatMessageCell.vue";
// Mock GalaxyApi
const mockPOST = vi.fn();
@@ -10,8 +10,8 @@ import { BAlert, BButton, BSkeleton } from "bootstrap-vue";
import { computed, nextTick, onMounted, ref, watch } from "vue";
import { GalaxyApi } from "@/api";
import type { ChatMessage } from "@/components/ChatGXY/chatTypes";
import { generateId, scrollToBottom } from "@/components/ChatGXY/chatUtils";
import type { ChatMessage } from "@/components/GalaxyAI/chatTypes";
import { generateId, scrollToBottom } from "@/components/GalaxyAI/chatUtils";
import { PAGE_LABELS } from "@/components/Page/constants";
import { type AgentResponse, type EditProposal, useAgentActions } from "@/composables/agentActions";
import { useMarkdown } from "@/composables/markdown";
@@ -23,8 +23,8 @@ import { applySectionEdit } from "./sectionDiffUtils";
import PageChatHistoryList from "./PageChatHistoryList.vue";
import ProposalDiffView from "./ProposalDiffView.vue";
import SectionPatchView from "./SectionPatchView.vue";
import ChatInput from "@/components/ChatGXY/ChatInput.vue";
import ChatMessageCell from "@/components/ChatGXY/ChatMessageCell.vue";
import ChatInput from "@/components/GalaxyAI/ChatInput.vue";
import ChatMessageCell from "@/components/GalaxyAI/ChatMessageCell.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
const props = withDefaults(
+1 -1
View File
@@ -2,7 +2,7 @@
* Composable for fetching and watching history datasets based on filter criteria.
* This encapsulates the logic for fetching datasets when history ID, update time,
* or filter text changes, making it reusable across components like `CollectionCreatorIndex`
* (and in the future `ChatGXY`).
* (and in the future `GalaxyAI`).
*/
import { type MaybeRefOrGetter, toValue } from "@vueuse/shared";
import { computed, ref, watch } from "vue";
+3 -3
View File
@@ -18,7 +18,6 @@ import { patchRouterPush } from "./router-push";
import CenterFrame from "./modules/CenterFrame.vue";
import AboutGalaxy from "@/components/AboutGalaxy.vue";
import AvailableDatatypes from "@/components/AvailableDatatypes/AvailableDatatypes.vue";
import ChatGXY from "@/components/ChatGXY.vue";
import CitationsList from "@/components/Citation/CitationsList.vue";
import ClientError from "@/components/ClientError.vue";
import CollectionEditView from "@/components/Collections/common/CollectionEditView.vue";
@@ -36,6 +35,7 @@ import ManageFileSourceIndex from "@/components/FileSources/Instances/ManageInde
import UpgradeFileSourceInstance from "@/components/FileSources/Instances/UpgradeInstance.vue";
import CreateUserFileSource from "@/components/FileSources/Templates/CreateUserFileSource.vue";
import FormGeneric from "@/components/Form/FormGeneric.vue";
import GalaxyAI from "@/components/GalaxyAI.vue";
import GalaxyWizard from "@/components/GalaxyWizard.vue";
import GridInvocation from "@/components/Grid/GridInvocation.vue";
import GridPage from "@/components/Grid/GridPage.vue";
@@ -618,8 +618,8 @@ export function getRouter(Galaxy) {
component: TourList,
},
{
path: "chatgxy/:exchangeId?",
component: ChatGXY,
path: "galaxyai/:exchangeId?",
component: GalaxyAI,
redirect: redirectAnon(),
props: (route) => ({
exchangeId: route.params.exchangeId || undefined,
+5 -5
View File
@@ -68,15 +68,15 @@ export const defaultActivities = [
anonymous: false,
description: "AI-powered assistant to help with Galaxy tasks and troubleshooting.",
icon: faComments,
id: "chatgxy",
id: "galaxyai",
mutable: false,
optional: true,
panel: true,
title: "ChatGXY",
to: "/chatgxy",
tooltip: "Chat with Galaxy AI Assistant",
title: "GalaxyAI",
to: "/galaxyai",
tooltip: "Chat with GalaxyAI",
visible: true,
windowTitle: "ChatGXY",
windowTitle: "GalaxyAI",
},
{
anonymous: true,
+7 -7
View File
@@ -1636,13 +1636,13 @@ job_details:
selector: '//td[@id="galaxy-tool-id"][normalize-space(text()) = "${tool_id}"]'
tool_exit_code: '#exit-code'
chatgxy:
galaxyai:
selectors:
activity: '#activity-chatgxy'
_: '.chatgxy-container'
header: '.chatgxy-header'
new_chat_button: '.chatgxy-header .btn-outline-primary'
delete_chat_button: '.chatgxy-header .btn-outline-danger'
activity: '#activity-galaxyai'
_: '.galaxyai-container'
header: '.galaxyai-header'
new_chat_button: '.galaxyai-header .btn-outline-primary'
delete_chat_button: '.galaxyai-header .btn-outline-danger'
messages: '.chat-messages'
input: '#chat-input'
send_button: '.send-button'
@@ -1659,7 +1659,7 @@ chatgxy:
history_panel:
selectors:
_: '.activity-panel[data-description="ChatGXY"]'
_: '.activity-panel[data-description="GalaxyAI"]'
toggle_selection_mode: 'button[title="Select chats to delete"]'
cancel_selection: 'button[title="Cancel selection"]'
history_item: '[data-description="sidebar item"]'
+5 -5
View File
@@ -6,14 +6,14 @@ Galaxy includes a multi-agent AI system built on [pydantic-ai](https://github.co
When AI is configured, Galaxy exposes two main user-facing features:
- **ChatGXY**: A sidebar chat interface (visible in the Activity Bar) that routes user questions to specialized agents.
- **GalaxyAI**: A sidebar chat interface (visible in the Activity Bar) that routes user questions to specialized agents.
- **GalaxyWizard**: An error-analysis widget that appears on failed job pages to help users understand what went wrong.
All AI configuration lives in `galaxy.yml` under the `galaxy:` section. There is no admin UI for toggling agents -- everything is controlled through configuration files.
## Minimum Required Configuration
The recommended way to configure AI is through `inference_services`. Setting this value (or the deprecated `ai_api_key` / `ai_api_base_url`) is what activates the entire agent system. Without at least one of these, no agent code loads, the ChatGXY sidebar entry is hidden, and the GalaxyWizard error-analysis widget does not appear.
The recommended way to configure AI is through `inference_services`. Setting this value (or the deprecated `ai_api_key` / `ai_api_base_url`) is what activates the entire agent system. Without at least one of these, no agent code loads, the GalaxyAI sidebar entry is hidden, and the GalaxyWizard error-analysis widget does not appear.
```yaml
galaxy:
@@ -231,9 +231,9 @@ curl -s -H "x-api-key: YOUR_GALAXY_API_KEY" \
You should see a list of enabled agents with their types. If AI is not configured, this endpoint returns an error indicating that the agent system is not available.
### Check if ChatGXY Appears in the Sidebar
### Check if GalaxyAI Appears in the Sidebar
Log in to the Galaxy web interface. If AI is properly configured, a **ChatGXY** entry should appear in the Activity Bar on the left side of the screen. If it does not appear:
Log in to the Galaxy web interface. If AI is properly configured, a **GalaxyAI** entry should appear in the Activity Bar on the left side of the screen. If it does not appear:
1. Verify that `inference_services` is set in `galaxy.yml` (or the deprecated `ai_api_key` / `ai_api_base_url`).
2. Check that Galaxy was restarted after the configuration change.
@@ -251,7 +251,7 @@ This should return `"llm_api_configured": true` when AI is active.
## Troubleshooting
### ChatGXY does not appear in the sidebar
### GalaxyAI does not appear in the sidebar
- Confirm that `inference_services` is set in `galaxy.yml` under the `galaxy:` section (or the deprecated `ai_api_key` / `ai_api_base_url`).
- Restart Galaxy after any configuration change.
+5 -5
View File
@@ -16,10 +16,10 @@ Discover some of the most exciting new capabilities and improvements in Galaxy 2
*Galaxy 26.0 brings artificial intelligence directly into the Galaxy experience, expanding how users explore data, discover tools, and troubleshoot analyses.*
ChatGXY: Intelligent Assistance Inside Galaxy
---------------------------------------------
GalaxyAI: Intelligent Assistance Inside Galaxy
----------------------------------------------
Galaxy introduces **ChatGXY**, a built in AI assistant integrated directly into the interface.
Galaxy introduces **GalaxyAI**, a built in AI assistant integrated directly into the interface.
- **Conversational help**. Ask questions about tools, workflows, results, or errors without leaving Galaxy.
- **Error analysis guidance**. Receive contextual explanations and suggestions when jobs fail.
@@ -27,12 +27,12 @@ Galaxy introduces **ChatGXY**, a built in AI assistant integrated directly into
- **Training discovery**. Find relevant Galaxy Training Network tutorials directly from within Galaxy.
- **Extensible architecture**. Built on a flexible agent framework designed to grow with additional specialized AI capabilities over time.
ChatGXY provides a conversational entry point to Galaxy's AI capabilities and lays the groundwork for connecting Galaxy users to an expanding set of intelligent capabilities.
GalaxyAI provides a conversational entry point to Galaxy's AI capabilities and lays the groundwork for connecting Galaxy users to an expanding set of intelligent capabilities.
[`#21434 <https://github.com/galaxyproject/galaxy/pull/21434>`__]
.. raw:: html
<iframe width="100%" height="360" style="margin-bottom: 1em;" src="https://www.youtube.com/embed/DQsiSbzMQgE?si=NMSpS2KyEO7AmWSM" title="Galaxy 26.0 - ChatGXY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe width="100%" height="360" style="margin-bottom: 1em;" src="https://www.youtube.com/embed/DQsiSbzMQgE?si=NMSpS2KyEO7AmWSM" title="Galaxy 26.0 - GalaxyAI" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
AI Assisted Notebooks with JupyterLite
--------------------------------------
+1 -1
View File
@@ -128,7 +128,7 @@ class ChatManager:
self, trans: ProvidesUserContext, query: str, response_data: Any, agent_type: str = "unknown"
) -> ChatExchange:
"""
Create a general ChatGXY exchange (not job-related) in the database.
Create a general GalaxyAI exchange (not job-related) in the database.
Stores both the user's query and the full agent response.
:param query: The user's query
+19 -19
View File
@@ -1646,31 +1646,31 @@ class NavigatesGalaxy(HasDriverProxy[WaitType]):
self.components.invocations.activity.wait_for_and_click()
self.components.invocations.activity_expand.wait_for_and_click()
def navigate_to_chatgxy(self):
def navigate_to_galaxyai(self):
self.home()
self.components.chatgxy.activity.wait_for_and_click()
self.components.galaxyai.activity.wait_for_and_click()
def chatgxy_ensure_new_chat(self):
"""Ensure ChatGXY center panel shows an empty conversation."""
chatgxy = self.components.chatgxy
chatgxy._.wait_for_visible()
if len(chatgxy.query_cell.all()) > 0 or len(chatgxy.response_content.all()) > 0:
chatgxy.new_chat_button.wait_for_and_click()
self._chatgxy_assert_chat_empty()
def galaxyai_ensure_new_chat(self):
"""Ensure GalaxyAI center panel shows an empty conversation."""
galaxyai = self.components.galaxyai
galaxyai._.wait_for_visible()
if len(galaxyai.query_cell.all()) > 0 or len(galaxyai.response_content.all()) > 0:
galaxyai.new_chat_button.wait_for_and_click()
self._galaxyai_assert_chat_empty()
def chatgxy_send_message(self, text):
def galaxyai_send_message(self, text):
"""Type a message, click send, and wait for the response to appear."""
chatgxy = self.components.chatgxy
chatgxy.input.wait_for_and_send_keys(text)
chatgxy.send_button.wait_for_and_click()
chatgxy.loading.wait_for_absent_or_hidden()
chatgxy.response_content.wait_for_visible()
galaxyai = self.components.galaxyai
galaxyai.input.wait_for_and_send_keys(text)
galaxyai.send_button.wait_for_and_click()
galaxyai.loading.wait_for_absent_or_hidden()
galaxyai.response_content.wait_for_visible()
@retry_during_transitions
def _chatgxy_assert_chat_empty(self):
chatgxy = self.components.chatgxy
assert len(chatgxy.query_cell.all()) == 0
assert len(chatgxy.response_content.all()) == 0
def _galaxyai_assert_chat_empty(self):
galaxyai = self.components.galaxyai
assert len(galaxyai.query_cell.all()) == 0
assert len(galaxyai.response_content.all()) == 0
def navigate_to_dataset_error(self, hid):
"""Display a dataset and click the error tab."""
+1 -1
View File
@@ -133,7 +133,7 @@ class ChatAPI:
trans: ProvidesUserContext = DependsOnTrans,
user: User = DependsOnUser,
) -> ChatResponse:
"""ChatGXY endpoint - handles both job-based and general chat queries
"""GalaxyAI endpoint - handles both job-based and general chat queries
Backwards compatible with both formats:
1. Old format: job_id in query params + payload body with query/context
+2 -2
View File
@@ -235,8 +235,8 @@ def app_pair(global_conf, load_app_kwds=None, wsgi_preflight=True, **kwargs):
webapp.add_client_route("/workflow_landings/{uuid}")
webapp.add_client_route("/tours")
webapp.add_client_route("/tours/{tour_id}")
webapp.add_client_route("/chatgxy")
webapp.add_client_route("/chatgxy/{exchange_id}")
webapp.add_client_route("/galaxyai")
webapp.add_client_route("/galaxyai/{exchange_id}")
webapp.add_client_route("/user")
webapp.add_client_route("/user/notifications{path:.*?}")
webapp.add_client_route("/user/{form_id}")
@@ -1,4 +1,4 @@
"""E2E tests for the ChatGXY conversational AI interface.
"""E2E tests for the GalaxyAI conversational AI interface.
Uses the static agent backend for deterministic assertions — no LLM calls.
Skipped when agents are not configured (skip_without_agents decorator).
@@ -12,7 +12,7 @@ from .framework import (
)
class TestChatGXY(SeleniumTestCase):
class TestGalaxyAI(SeleniumTestCase):
ensure_registered = True
@skip_without_agents
@@ -20,50 +20,50 @@ class TestChatGXY(SeleniumTestCase):
def test_chat_greeting_flow(self):
"""Activity visible, send greeting, get response, give feedback, check metadata."""
self.home()
chatgxy = self.components.chatgxy
galaxyai = self.components.galaxyai
# Activity bar icon visible
chatgxy.activity.wait_for_visible()
galaxyai.activity.wait_for_visible()
# Navigate, start fresh
self.navigate_to_chatgxy()
self.chatgxy_ensure_new_chat()
self.navigate_to_galaxyai()
self.galaxyai_ensure_new_chat()
# Send greeting
self.chatgxy_send_message("Hello!")
self.galaxyai_send_message("Hello!")
# Verify query cell and response
assert chatgxy.query_cell.wait_for_text() == "Hello!"
assert galaxyai.query_cell.wait_for_text() == "Hello!"
@retry_assertion_during_transitions
def assert_response():
assert "Hello" in chatgxy.response_content.wait_for_text()
assert "Hello" in galaxyai.response_content.wait_for_text()
assert_response()
# Agent indicator and metadata tags present
chatgxy.agent_indicator.wait_for_visible()
assert len(chatgxy.meta_tag.all()) >= 1
galaxyai.agent_indicator.wait_for_visible()
assert len(galaxyai.meta_tag.all()) >= 1
# Feedback
chatgxy.feedback_up.wait_for_and_click()
assert "Thanks" in chatgxy.feedback_ack.wait_for_text()
galaxyai.feedback_up.wait_for_and_click()
assert "Thanks" in galaxyai.feedback_ack.wait_for_text()
@skip_without_agents
@selenium_test
def test_multi_turn_and_new_chat(self):
"""Two queries build up conversation, 'New' resets it."""
self.navigate_to_chatgxy()
self.chatgxy_ensure_new_chat()
chatgxy = self.components.chatgxy
self.navigate_to_galaxyai()
self.galaxyai_ensure_new_chat()
galaxyai = self.components.galaxyai
self.chatgxy_send_message("Hello!")
self.chatgxy_send_message("How do I analyze RNA-seq data?")
self.galaxyai_send_message("Hello!")
self.galaxyai_send_message("How do I analyze RNA-seq data?")
@retry_assertion_during_transitions
def assert_two_exchanges():
assert len(chatgxy.query_cell.all()) == 2
responses = chatgxy.response_content.all()
assert len(galaxyai.query_cell.all()) == 2
responses = galaxyai.response_content.all()
assert len(responses) >= 2
# Second response should be domain-specific
assert "HISAT2" in responses[-1].text or "RNA-seq" in responses[-1].text
@@ -71,8 +71,8 @@ class TestChatGXY(SeleniumTestCase):
assert_two_exchanges()
# New chat resets
chatgxy.new_chat_button.wait_for_and_click()
self._chatgxy_assert_chat_empty()
galaxyai.new_chat_button.wait_for_and_click()
self._galaxyai_assert_chat_empty()
@skip_without_agents
@selenium_test
@@ -81,21 +81,21 @@ class TestChatGXY(SeleniumTestCase):
# Clear any existing chat history so test is deterministic
self.api_delete("chat/history")
self.navigate_to_chatgxy()
chatgxy = self.components.chatgxy
history = chatgxy.history_panel
self.navigate_to_galaxyai()
galaxyai = self.components.galaxyai
history = galaxyai.history_panel
# Create first chat
self.chatgxy_ensure_new_chat()
self.chatgxy_send_message("Hello!")
self.galaxyai_ensure_new_chat()
self.galaxyai_send_message("Hello!")
# Create second chat
self.chatgxy_ensure_new_chat()
self.chatgxy_send_message("How do I analyze RNA-seq data?")
self.galaxyai_ensure_new_chat()
self.galaxyai_send_message("How do I analyze RNA-seq data?")
# Switch sidebar away from chatgxy then back to force ChatHistoryPanel remount
# Switch sidebar away from galaxyai then back to force ChatHistoryPanel remount
self.components.tools.activity.wait_for_and_click()
chatgxy.activity.wait_for_and_click()
galaxyai.activity.wait_for_and_click()
history._.wait_for_visible()
# Wait for history items to appear