From fc3928ee1b9ef879920e7d9a536ac769c891d0d9 Mon Sep 17 00:00:00 2001
From: tcollins2011
Date: Wed, 23 Oct 2024 08:35:21 -0400
Subject: [PATCH] updating Wizard route
---
client/src/api/schema/schema.ts | 7 ++
.../DatasetInformation/DatasetError.vue | 7 +-
client/src/components/GalaxyWizard.vue | 83 +++++++++++++++++++
client/src/entry/analysis/router.js | 5 ++
doc/source/admin/galaxy_options.rst | 9 ++
lib/galaxy/config/sample/galaxy.yml.sample | 3 +
lib/galaxy/config/schemas/config_schema.yml | 6 ++
lib/galaxy/dependencies/__init__.py | 3 +
.../dependencies/conditional-requirements.txt | 1 +
lib/galaxy/schema/schema.py | 11 +++
lib/galaxy/webapps/galaxy/api/chat.py | 73 ++++++++++++++++
lib/galaxy/webapps/galaxy/buildapp.py | 1 +
12 files changed, 208 insertions(+), 1 deletion(-)
create mode 100644 client/src/components/GalaxyWizard.vue
create mode 100644 lib/galaxy/webapps/galaxy/api/chat.py
diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts
index 87e99248cd64..16c6dd9afd0d 100644
--- a/client/src/api/schema/schema.ts
+++ b/client/src/api/schema/schema.ts
@@ -21,6 +21,13 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/api/chat": {
+ /**
+ * Query
+ * @description We're off to ask the wizard
+ */
+ post: operations["query_api_chat_post"];
+};
"/api/configuration": {
parameters: {
query?: never;
diff --git a/client/src/components/DatasetInformation/DatasetError.vue b/client/src/components/DatasetInformation/DatasetError.vue
index e2254fc71953..fb56598c83ea 100644
--- a/client/src/components/DatasetInformation/DatasetError.vue
+++ b/client/src/components/DatasetInformation/DatasetError.vue
@@ -16,6 +16,7 @@ import { errorMessageAsString } from "@/utils/simple-error";
import DatasetErrorDetails from "@/components/DatasetInformation/DatasetErrorDetails.vue";
import FormElement from "@/components/Form/FormElement.vue";
+import GalaxyWizard from "@/components/GalaxyWizard.vue";
library.add(faBug);
@@ -187,7 +188,11 @@ onMounted(async () => {
-
+
+ What might have happened?
+
+
+
Issue Report
diff --git a/client/src/components/GalaxyWizard.vue b/client/src/components/GalaxyWizard.vue
new file mode 100644
index 000000000000..6d0124fa2d5d
--- /dev/null
+++ b/client/src/components/GalaxyWizard.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
Ask the wizard
+
+
+
+
+ Let our Help Wizard Figure it out!
+
+
+
+
+
+
+
+
+
+
+
+
{{ queryResponse }}
+
+
+
+
\ No newline at end of file
diff --git a/client/src/entry/analysis/router.js b/client/src/entry/analysis/router.js
index 011bb9ffd082..91dee0f23439 100644
--- a/client/src/entry/analysis/router.js
+++ b/client/src/entry/analysis/router.js
@@ -7,6 +7,7 @@ import DatasetAttributes from "components/DatasetInformation/DatasetAttributes";
import DatasetDetails from "components/DatasetInformation/DatasetDetails";
import DatasetError from "components/DatasetInformation/DatasetError";
import FormGeneric from "components/Form/FormGeneric";
+import GalaxyWizard from "components/GalaxyWizard.vue";
import HelpTerm from "components/Help/HelpTerm";
import HistoryExportTasks from "components/History/Export/HistoryExport";
import HistoryPublished from "components/History/HistoryPublished";
@@ -614,6 +615,10 @@ export function getRouter(Galaxy) {
activeList: "published",
},
},
+ {
+ path: "wizard",
+ component: GalaxyWizard,
+ },
{
path: "workflows/create",
component: WorkflowCreate,
diff --git a/doc/source/admin/galaxy_options.rst b/doc/source/admin/galaxy_options.rst
index 45386d72d861..e4a264438c2b 100644
--- a/doc/source/admin/galaxy_options.rst
+++ b/doc/source/admin/galaxy_options.rst
@@ -5364,6 +5364,15 @@
:Default: ``-1``
:Type: int
+~~~~~~~~~~~~~~~~~~
+``openai_api_key``
+~~~~~~~~~~~~~~~~~~
+
+:Description:
+ API key for OpenAI (https://openai.com/) to enable the wizard (or
+ more?)
+:Default: ``None``
+:Type: str
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``enable_tool_recommendations``
diff --git a/lib/galaxy/config/sample/galaxy.yml.sample b/lib/galaxy/config/sample/galaxy.yml.sample
index d6718cf176e7..e0aa04c6c346 100644
--- a/lib/galaxy/config/sample/galaxy.yml.sample
+++ b/lib/galaxy/config/sample/galaxy.yml.sample
@@ -2881,6 +2881,9 @@ galaxy:
# as threshold (above threshold: regular select fields will be used)
#select_type_workflow_threshold: -1
+ # API key for OpenAI (https://openai.com/) to enable the wizard
+ #openai_api_key: null
+
# Allow the display of tool recommendations in workflow editor and
# after tool execution. If it is enabled and set to true, please
# enable 'tool_recommendation_model_path' as well
diff --git a/lib/galaxy/config/schemas/config_schema.yml b/lib/galaxy/config/schemas/config_schema.yml
index 3ed0c7455e74..cb2b8e0e1898 100644
--- a/lib/galaxy/config/schemas/config_schema.yml
+++ b/lib/galaxy/config/schemas/config_schema.yml
@@ -3923,6 +3923,12 @@ mapping:
use -1 (default) in order to always use the regular select fields,
use any other positive number as threshold (above threshold: regular select fields will be used)
+ openai_api_key:
+ type: str
+ required: false
+ desc: |
+ OpenAI API key to enable the wizard.
+
enable_tool_recommendations:
type: bool
default: false
diff --git a/lib/galaxy/dependencies/__init__.py b/lib/galaxy/dependencies/__init__.py
index d7a9d0740048..deb5fd8a715d 100644
--- a/lib/galaxy/dependencies/__init__.py
+++ b/lib/galaxy/dependencies/__init__.py
@@ -289,6 +289,9 @@ def check_influxdb(self):
def check_tensorflow(self):
return asbool(self.config["enable_tool_recommendations"])
+
+ def check_openai(self):
+ return self.config.get("openai_api_key", None) is not None
def check_weasyprint(self):
# See notes in ./conditional-requirements.txt for more information.
diff --git a/lib/galaxy/dependencies/conditional-requirements.txt b/lib/galaxy/dependencies/conditional-requirements.txt
index d59bd0916e69..7747d7dadb75 100644
--- a/lib/galaxy/dependencies/conditional-requirements.txt
+++ b/lib/galaxy/dependencies/conditional-requirements.txt
@@ -14,6 +14,7 @@ python-pam
galaxycloudrunner
pkce
total-perspective-vortex>=2.3.2,<3
+openai
# For file sources plugins
fs.webdavfs>=0.4.2 # type: webdav
diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py
index 0d9646316e59..4288a8b57d75 100644
--- a/lib/galaxy/schema/schema.py
+++ b/lib/galaxy/schema/schema.py
@@ -3708,6 +3708,17 @@ class MaterializeDatasetInstanceAPIRequest(MaterializeDatasetOptions):
class MaterializeDatasetInstanceRequest(MaterializeDatasetInstanceAPIRequest):
history_id: DecodedDatabaseIdField
+class ChatPayload(Model):
+ query: str = Field(
+ ...,
+ title="Query",
+ description="The query to be sent to the chatbot.",
+ )
+ context: Optional[str] = Field(
+ default=""
+ title="Context",
+ description="The context for the chatbot.",
+ )
class CreatePagePayload(PageSummaryBase):
content_format: PageContentFormat = ContentFormatField
diff --git a/lib/galaxy/webapps/galaxy/api/chat.py b/lib/galaxy/webapps/galaxy/api/chat.py
new file mode 100644
index 000000000000..ba917817018d
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/chat.py
@@ -0,0 +1,73 @@
+"""
+API Controller providing Chat functionality
+"""
+import logging
+
+try:
+ import openai
+except ImportError:
+ openai = None
+
+from galaxy.config import GalaxyAppConfiguration
+from galaxy.managers.context import ProvidesUserContext
+from galaxy.webapps.galaxy.api import (
+ depends,
+ DependsOnTrans,
+)
+from galaxy.exceptions import ConfigurationError
+from galaxy.schema.schema import ChatPayload
+from . import (
+ depends,
+ Router,
+)
+
+log = logging.getLogger(__name__)
+
+router = Router(tags=["chat"])
+
+PROMPT = """
+You are a highly intelligent question answering agent, expert on the Galaxy analysis platform and in the fields of computer science, bioinformatics, and genomics.
+You will try to answer questions about Galaxy, and if you don't know the answer, you will ask the user to rephrase the question.
+"""
+
+
+@router.cbv
+class ChatAPI:
+ config: GalaxyAppConfiguration = depends(GalaxyAppConfiguration)
+
+ @router.post("/api/chat")
+ def query(self, query: ChatPayload, trans: ProvidesUserContext = DependsOnTrans) -> str:
+ """We're off to ask the wizard"""
+
+ if openai is None or self.config.openai_api_key is None:
+ raise ConfigurationError("OpenAI is not configured for this instance.")
+ else:
+ openai.api_key = self.config.openai_api_key
+
+ messages=[
+ {"role": "system", "content": PROMPT},
+ {"role": "user", "content": query.query},
+ ]
+
+ if query.context == "username":
+ user = trans.user
+ if user is not None:
+ log.debug(f"CHATGPTuser: {user.username}")
+ msg = f"You will address the user as {user.username}"
+ else:
+ msg = f"You will address the user as Anonymous User"
+ messages.append({"role": "system", "content": msg})
+ elif query.context == "tool_error":
+ msg = "The user will provide you a Galaxy tool error, and you will try to debug and explain what happened"
+ messages.append({"role": "system", "content": msg})
+
+ log.debug(f"CHATGPTmessages: {messages}")
+
+ response = openai.ChatCompletion.create(
+ model="gpt-3.5-turbo",
+ messages=messages,
+ temperature=0,
+ )
+
+ answer = response["choices"][0]["message"]["content"]
+ return answer
\ No newline at end of file
diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py
index e1897420f9a5..9c1f9de277e6 100644
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -284,6 +284,7 @@ def app_pair(global_conf, load_app_kwds=None, wsgi_preflight=True, **kwargs):
webapp.add_client_route("/collection/{collection_id}/edit")
webapp.add_client_route("/jobs/submission/success")
webapp.add_client_route("/jobs/{job_id}/view")
+ webapp.add_client_route("/wizard")
webapp.add_client_route("/workflows/list")
webapp.add_client_route("/workflows/list_published")
webapp.add_client_route("/workflows/list_shared_with_me")