diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index e6a8a8b8..19c1b10f 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -25,6 +25,7 @@ mongo = MongoClient(settings.MONGO_URI) db = mongo["docsgpt"] conversations_collection = db["conversations"] vectors_collection = db["vectors"] +prompts_collection = db["prompts"] answer = Blueprint('answer', __name__) if settings.LLM_NAME == "gpt4": @@ -43,10 +44,10 @@ with open(os.path.join(current_dir, "prompts", "chat_reduce_prompt.txt"), "r") a chat_reduce_template = f.read() with open(os.path.join(current_dir, "prompts", "chat_combine_creative.txt"), "r") as f: - chat_reduce_creative = f.read() + chat_combine_creative = f.read() with open(os.path.join(current_dir, "prompts", "chat_combine_strict.txt"), "r") as f: - chat_reduce_strict = f.read() + chat_combine_strict = f.read() api_key_set = settings.API_KEY is not None embeddings_key_set = settings.EMBEDDINGS_KEY is not None @@ -90,23 +91,6 @@ def get_vectorstore(data): return vectorstore -# def get_docsearch(vectorstore, embeddings_key): -# if settings.EMBEDDINGS_NAME == "openai_text-embedding-ada-002": -# if is_azure_configured(): -# os.environ["OPENAI_API_TYPE"] = "azure" -# openai_embeddings = OpenAIEmbeddings(model=settings.AZURE_EMBEDDINGS_DEPLOYMENT_NAME) -# else: -# openai_embeddings = OpenAIEmbeddings(openai_api_key=embeddings_key) -# docsearch = FAISS.load_local(vectorstore, openai_embeddings) -# elif settings.EMBEDDINGS_NAME == "huggingface_sentence-transformers/all-mpnet-base-v2": -# docsearch = FAISS.load_local(vectorstore, HuggingFaceHubEmbeddings()) -# elif settings.EMBEDDINGS_NAME == "huggingface_hkunlp/instructor-large": -# docsearch = FAISS.load_local(vectorstore, HuggingFaceInstructEmbeddings()) -# elif settings.EMBEDDINGS_NAME == "cohere_medium": -# docsearch = FAISS.load_local(vectorstore, CohereEmbeddings(cohere_api_key=embeddings_key)) -# return docsearch - - def is_azure_configured(): return settings.OPENAI_API_BASE and settings.OPENAI_API_VERSION and settings.AZURE_DEPLOYMENT_NAME @@ -115,13 +99,16 @@ def complete_stream(question, docsearch, chat_history, api_key, prompt_id, conve llm = LLMCreator.create_llm(settings.LLM_NAME, api_key=api_key) if prompt_id == 'default': - prompt = chat_reduce_template + prompt = chat_combine_template elif prompt_id == 'creative': - prompt = chat_reduce_creative + prompt = chat_combine_creative elif prompt_id == 'strict': - prompt = chat_reduce_strict + prompt = chat_combine_strict else: - prompt = chat_reduce_template + prompt = prompts_collection.find_one({"_id": ObjectId(prompt_id)})["content"] + import sys + print(prompt_id, file=sys.stderr) + print(prompt, file=sys.stderr) docs = docsearch.search(question, k=2) @@ -253,6 +240,19 @@ def api_answer(): embeddings_key = data["embeddings_key"] else: embeddings_key = settings.EMBEDDINGS_KEY + if 'prompt_id' in data: + prompt_id = data["prompt_id"] + else: + prompt_id = 'default' + + if prompt_id == 'default': + prompt = chat_combine_template + elif prompt_id == 'creative': + prompt = chat_combine_creative + elif prompt_id == 'strict': + prompt = chat_combine_strict + else: + prompt = prompts_collection.find_one({"_id": ObjectId(prompt_id)})["content"] # use try and except to check for exception try: @@ -270,7 +270,7 @@ def api_answer(): docs = docsearch.search(question, k=2) # join all page_content together with a newline docs_together = "\n".join([doc.page_content for doc in docs]) - p_chat_combine = chat_combine_template.replace("{summaries}", docs_together) + p_chat_combine = prompt.replace("{summaries}", docs_together) messages_combine = [{"role": "system", "content": p_chat_combine}] source_log_docs = [] for doc in docs: diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 4dcbfd4d..97e589ed 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1,11 +1,9 @@ import os from flask import Blueprint, request, jsonify import requests -import json from pymongo import MongoClient from bson.objectid import ObjectId from werkzeug.utils import secure_filename -import http.client from application.api.user.tasks import ingest @@ -17,6 +15,7 @@ db = mongo["docsgpt"] conversations_collection = db["conversations"] vectors_collection = db["vectors"] prompts_collection = db["prompts"] +feedback_collection = db["feedback"] user = Blueprint('user', __name__) current_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -71,19 +70,15 @@ def api_feedback(): answer = data["answer"] feedback = data["feedback"] - print("-" * 5) - print("Question: " + question) - print("Answer: " + answer) - print("Feedback: " + feedback) - print("-" * 5) - response = requests.post( - url="https://86x89umx77.execute-api.eu-west-2.amazonaws.com/docsgpt-feedback", - headers={ - "Content-Type": "application/json; charset=utf-8", - }, - data=json.dumps({"answer": answer, "question": question, "feedback": feedback}), + + feedback_collection.insert_one( + { + "question": question, + "answer": answer, + "feedback": feedback, + } ) - return {"status": http.client.responses.get(response.status_code, "ok")} + return {"status": "ok"} @user.route("/api/delete_by_ids", methods=["get"]) def delete_by_ids(): @@ -249,18 +244,20 @@ def check_docs(): @user.route("/api/create_prompt", methods=["POST"]) def create_prompt(): data = request.get_json() - prompt = data["prompt"] + content = data["content"] name = data["name"] + if name == "": + return {"status": "error"} user = "local" - # write to mongodb - prompts_collection.insert_one( + resp = prompts_collection.insert_one( { "name": name, - "prompt": prompt, + "content": content, "user": user, } ) - return {"status": "ok"} + new_id = str(resp.inserted_id) + return {"id": new_id} @user.route("/api/get_prompts", methods=["GET"]) def get_prompts(): @@ -271,32 +268,51 @@ def get_prompts(): list_prompts.append({"id": "creative", "name": "creative", "type": "public"}) list_prompts.append({"id": "precise", "name": "precise", "type": "public"}) for prompt in prompts: - list_prompts.append({"id": str(prompt["_id"]), "name": prompt["name"], type: "private"}) + list_prompts.append({"id": str(prompt["_id"]), "name": prompt["name"], "type": "private"}) return jsonify(list_prompts) @user.route("/api/get_single_prompt", methods=["GET"]) def get_single_prompt(): prompt_id = request.args.get("id") + if prompt_id == 'default': + with open(os.path.join(current_dir, "prompts", "chat_combine_default.txt"), "r") as f: + chat_combine_template = f.read() + return jsonify({"content": chat_combine_template}) + elif prompt_id == 'creative': + with open(os.path.join(current_dir, "prompts", "chat_combine_creative.txt"), "r") as f: + chat_reduce_creative = f.read() + return jsonify({"content": chat_reduce_creative}) + elif prompt_id == 'strict': + with open(os.path.join(current_dir, "prompts", "chat_combine_strict.txt"), "r") as f: + chat_reduce_strict = f.read() + return jsonify({"content": chat_reduce_strict}) + + prompt = prompts_collection.find_one({"_id": ObjectId(prompt_id)}) - return jsonify(prompt['prompt']) + return jsonify({"content": prompt["content"]}) @user.route("/api/delete_prompt", methods=["POST"]) def delete_prompt(): - prompt_id = request.args.get("id") + data = request.get_json() + id = data["id"] prompts_collection.delete_one( { - "_id": ObjectId(prompt_id), + "_id": ObjectId(id), } ) return {"status": "ok"} -@user.route("/api/update_prompt_name", methods=["POST"]) +@user.route("/api/update_prompt", methods=["POST"]) def update_prompt_name(): data = request.get_json() id = data["id"] name = data["name"] - prompts_collection.update_one({"_id": ObjectId(id)},{"$set":{"name":name}}) + content = data["content"] + # check if name is null + if name == "": + return {"status": "error"} + prompts_collection.update_one({"_id": ObjectId(id)},{"$set":{"name":name, "content": content}}) return {"status": "ok"} diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx index 5f652a84..65756bea 100644 --- a/frontend/src/Setting.tsx +++ b/frontend/src/Setting.tsx @@ -13,14 +13,10 @@ import { Doc } from './preferences/preferenceApi'; type PromptProps = { prompts: { name: string; id: string; type: string }[]; - selectedPrompt: { name: string; id: string }; - onSelectPrompt: (name: string, id: string) => void; - onAddPrompt: (name: string) => void; - newPromptName: string; - onNewPromptNameChange: (name: string) => void; - isAddPromptModalOpen: boolean; - onToggleAddPromptModal: () => void; - onDeletePrompt: (name: string, id: string) => void; + selectedPrompt: { name: string; id: string; type: string }; + onSelectPrompt: (name: string, id: string, type: string) => void; + setPrompts: (prompts: { name: string; id: string; type: string }[]) => void; + apiHost: string; }; const Setting: React.FC = () => { @@ -32,15 +28,10 @@ const Setting: React.FC = () => { { name: string; id: string; type: string }[] >([]); const selectedPrompt = useSelector(selectPrompt); - const [newPromptName, setNewPromptName] = useState(''); const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); const documents = useSelector(selectSourceDocs); const [isAddDocumentModalOpen, setAddDocumentModalOpen] = useState(false); - const [newDocument, setNewDocument] = useState({ - name: '', - vectorDate: '', - vectorLocation: '', - }); + const dispatch = useDispatch(); const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; @@ -50,10 +41,6 @@ const Setting: React.FC = () => { setWidgetScreenshot(screenshot); }; - // Function to toggle the Add Document modal - const toggleAddDocumentModal = () => { - setAddDocumentModalOpen(!isAddDocumentModalOpen); - }; useEffect(() => { const fetchPrompts = async () => { try { @@ -172,19 +159,11 @@ const Setting: React.FC = () => { - dispatch(setPrompt({ name: name, id: id })) + onSelectPrompt={(name, id, type) => + dispatch(setPrompt({ name: name, id: id, type: type })) } - onAddPrompt={addPrompt} - newPromptName={''} - onNewPromptNameChange={function (name: string): void { - throw new Error('Function not implemented.'); - }} - isAddPromptModalOpen={false} - onToggleAddPromptModal={function (): void { - throw new Error('Function not implemented.'); - }} - onDeletePrompt={onDeletePrompt} + setPrompts={setPrompts} + apiHost={apiHost} /> ); case 'Documents': @@ -205,17 +184,6 @@ const Setting: React.FC = () => { return null; } } - - function addPrompt(name: string) { - if (name) { - setNewPromptName(''); - toggleAddPromptModal(); - } - } - - function toggleAddPromptModal() { - setAddPromptModalOpen(!isAddPromptModalOpen); - } }; const General: React.FC = () => { @@ -252,65 +220,199 @@ const Prompts: React.FC = ({ prompts, selectedPrompt, onSelectPrompt, - onAddPrompt, - onDeletePrompt, + setPrompts, + apiHost, }) => { - const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); - const [newPromptName, setNewPromptName] = useState(''); - - const openAddPromptModal = () => { - setAddPromptModalOpen(true); + const handleSelectPrompt = ({ + name, + id, + type, + }: { + name: string; + id: string; + type: string; + }) => { + setNewPromptName(name); + onSelectPrompt(name, id, type); }; + const [newPromptName, setNewPromptName] = useState(selectedPrompt.name); + const [newPromptContent, setNewPromptContent] = useState(''); - const closeAddPromptModal = () => { - setAddPromptModalOpen(false); - }; - - const handleSelectPrompt = (name: string) => { - const selected = prompts.find((prompt) => prompt.name === name); - if (selected) { - onSelectPrompt(selected.name, selected.id); + const handleAddPrompt = async () => { + try { + const response = await fetch(`${apiHost}/api/create_prompt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: newPromptName, + content: newPromptContent, + }), + }); + if (!response.ok) { + throw new Error('Failed to add prompt'); + } + const newPrompt = await response.json(); + if (setPrompts) { + setPrompts([ + ...prompts, + { name: newPromptName, id: newPrompt.id, type: 'private' }, + ]); + } + onSelectPrompt(newPromptName, newPrompt.id, newPromptContent); + setNewPromptName(newPromptName); + } catch (error) { + console.error(error); } }; - const handleDeletePrompt = (name: string) => { - const selected = prompts.find((prompt) => prompt.name === name); - if (selected) { - onDeletePrompt(selected.name, selected.id); - } + const handleDeletePrompt = () => { + setPrompts(prompts.filter((prompt) => prompt.id !== selectedPrompt.id)); + console.log('selectedPrompt.id', selectedPrompt.id); + + fetch(`${apiHost}/api/delete_prompt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id: selectedPrompt.id }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to delete prompt'); + } + // get 1st prompt and set it as selected + if (prompts.length > 0) { + onSelectPrompt(prompts[0].name, prompts[0].id, prompts[0].type); + setNewPromptName(prompts[0].name); + } + }) + .catch((error) => { + console.error(error); + }); + }; + + useEffect(() => { + const fetchPromptContent = async () => { + console.log('fetching prompt content'); + try { + const response = await fetch( + `${apiHost}/api/get_single_prompt?id=${selectedPrompt.id}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + if (!response.ok) { + throw new Error('Failed to fetch prompt content'); + } + const promptContent = await response.json(); + setNewPromptContent(promptContent.content); + } catch (error) { + console.error(error); + } + }; + + fetchPromptContent(); + }, [selectedPrompt]); + + const handleSaveChanges = () => { + fetch(`${apiHost}/api/update_prompt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + id: selectedPrompt.id, + name: newPromptName, + content: newPromptContent, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to update prompt'); + } + onSelectPrompt(newPromptName, selectedPrompt.id, selectedPrompt.type); + setNewPromptName(newPromptName); + }) + .catch((error) => { + console.error(error); + }); }; return (
-

Active Prompt

+

Active Prompt

- {/*
+ +
+

Prompt name

{' '} +

+ start by editing name +

+ setNewPromptName(e.target.value)} + /> +
+ +
+

Prompt content

+