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 @@ + + + \ 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")