From 97fabf51b8217198fa0de625986b400be41fa14f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 28 Mar 2024 13:43:10 +0000 Subject: [PATCH 1/5] Refactor conversationSlice.ts and conversationApi.ts --- application/api/answer/routes.py | 78 ++++++++++--------- application/api/user/routes.py | 38 +++++++++ frontend/src/conversation/conversationApi.ts | 7 -- .../src/conversation/conversationSlice.ts | 3 - 4 files changed, 78 insertions(+), 48 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 4c393714..abb2f67c 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -26,6 +26,7 @@ db = mongo["docsgpt"] conversations_collection = db["conversations"] vectors_collection = db["vectors"] prompts_collection = db["prompts"] +api_key_collection = db["api_keys"] answer = Blueprint('answer', __name__) if settings.LLM_NAME == "gpt4": @@ -74,6 +75,12 @@ def run_async_chain(chain, question, chat_history): result["answer"] = answer return result +def get_data_from_api_key(api_key): + data = api_key_collection.find_one({"key": api_key}) + if data is None: + return bad_request(401, "Invalid API key") + return data + def get_vectorstore(data): if "active_docs" in data: @@ -95,8 +102,8 @@ def is_azure_configured(): return settings.OPENAI_API_BASE and settings.OPENAI_API_VERSION and settings.AZURE_DEPLOYMENT_NAME -def complete_stream(question, docsearch, chat_history, api_key, prompt_id, conversation_id): - llm = LLMCreator.create_llm(settings.LLM_NAME, api_key=api_key) +def complete_stream(question, docsearch, chat_history, prompt_id, conversation_id): + llm = LLMCreator.create_llm(settings.LLM_NAME, api_key=settings.API_KEY) if prompt_id == 'default': prompt = chat_combine_template @@ -182,10 +189,15 @@ def stream(): data = request.get_json() # get parameter from url question question = data["question"] - history = data["history"] - # history to json object from string - history = json.loads(history) - conversation_id = data["conversation_id"] + if "history" not in data: + history = [] + else: + history = data["history"] + history = json.loads(history) + if "conversation_id" not in data: + conversation_id = None + else: + conversation_id = data["conversation_id"] if 'prompt_id' in data: prompt_id = data["prompt_id"] else: @@ -193,23 +205,18 @@ def stream(): # check if active_docs is set - if not api_key_set: - api_key = data["api_key"] - else: - api_key = settings.API_KEY - if not embeddings_key_set: - embeddings_key = data["embeddings_key"] - else: - embeddings_key = settings.EMBEDDINGS_KEY - if "active_docs" in data: + if "api_key" in data: + data_key = get_data_from_api_key(data["api_key"]) + vectorstore = get_vectorstore({"active_docs": data_key["source"]}) + elif "active_docs" in data: vectorstore = get_vectorstore({"active_docs": data["active_docs"]}) else: vectorstore = "" - docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, embeddings_key) + docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, settings.EMBEDDINGS_KEY) return Response( complete_stream(question, docsearch, - chat_history=history, api_key=api_key, + chat_history=history, prompt_id=prompt_id, conversation_id=conversation_id), mimetype="text/event-stream" ) @@ -219,20 +226,15 @@ def stream(): def api_answer(): data = request.get_json() question = data["question"] - history = data["history"] + if "history" not in data: + history = [] + else: + history = data["history"] if "conversation_id" not in data: conversation_id = None else: conversation_id = data["conversation_id"] print("-" * 5) - if not api_key_set: - api_key = data["api_key"] - else: - api_key = settings.API_KEY - if not embeddings_key_set: - embeddings_key = data["embeddings_key"] - else: - embeddings_key = settings.EMBEDDINGS_KEY if 'prompt_id' in data: prompt_id = data["prompt_id"] else: @@ -250,13 +252,17 @@ def api_answer(): # use try and except to check for exception try: # check if the vectorstore is set - vectorstore = get_vectorstore(data) + if "api_key" in data: + data_key = get_data_from_api_key(data["api_key"]) + vectorstore = get_vectorstore({"active_docs": data_key["source"]}) + else: + vectorstore = get_vectorstore(data) # loading the index and the store and the prompt template # Note if you have used other embeddings than OpenAI, you need to change the embeddings - docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, embeddings_key) + docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, settings.EMBEDDINGS_KEY) - llm = LLMCreator.create_llm(settings.LLM_NAME, api_key=api_key) + llm = LLMCreator.create_llm(settings.LLM_NAME, api_key=settings.API_KEY) @@ -348,18 +354,14 @@ def api_search(): # get parameter from url question question = data["question"] - if not embeddings_key_set: - if "embeddings_key" in data: - embeddings_key = data["embeddings_key"] - else: - embeddings_key = settings.EMBEDDINGS_KEY - else: - embeddings_key = settings.EMBEDDINGS_KEY - if "active_docs" in data: + if "api_key" in data: + data_key = get_data_from_api_key(data["api_key"]) + vectorstore = data_key["source"] + elif "active_docs" in data: vectorstore = get_vectorstore({"active_docs": data["active_docs"]}) else: vectorstore = "" - docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, embeddings_key) + docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, settings.EMBEDDINGS_KEY) docs = docsearch.search(question, k=2) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 1779472b..239278b9 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1,4 +1,5 @@ import os +import uuid from flask import Blueprint, request, jsonify import requests from pymongo import MongoClient @@ -16,6 +17,7 @@ conversations_collection = db["conversations"] vectors_collection = db["vectors"] prompts_collection = db["prompts"] feedback_collection = db["feedback"] +api_key_collection = db["api_keys"] user = Blueprint('user', __name__) current_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -343,5 +345,41 @@ def update_prompt_name(): +@user.route("/api/get_api_keys", methods=["GET"]) +def get_api_keys(): + user = "local" + keys = api_key_collection.find({"user": user}) + list_keys = [] + for key in keys: + list_keys.append({"id": str(key["_id"]), "name": key["name"], "key": key["key"][:4] + "..." + key["key"][-4:], "source": key["source"]}) + return jsonify(list_keys) +@user.route("/api/create_api_key", methods=["POST"]) +def create_api_key(): + data = request.get_json() + name = data["name"] + source = data["source"] + key = str(uuid.uuid4()) + user = "local" + resp = api_key_collection.insert_one( + { + "name": name, + "key": key, + "source": source, + "user": user, + } + ) + new_id = str(resp.inserted_id) + return {"id": new_id, "key": key} + +@user.route("/api/delete_api_key", methods=["POST"]) +def delete_api_key(): + data = request.get_json() + id = data["id"] + api_key_collection.delete_one( + { + "_id": ObjectId(id), + } + ) + return {"status": "ok"} diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index 8293df1b..4f789f15 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -6,7 +6,6 @@ const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; export function fetchAnswerApi( question: string, signal: AbortSignal, - apiKey: string, selectedDocs: Doc, history: Array = [], conversationId: string | null, @@ -59,8 +58,6 @@ export function fetchAnswerApi( }, body: JSON.stringify({ question: question, - api_key: apiKey, - embeddings_key: apiKey, history: history, active_docs: docPath, conversation_id: conversationId, @@ -90,7 +87,6 @@ export function fetchAnswerApi( export function fetchAnswerSteaming( question: string, signal: AbortSignal, - apiKey: string, selectedDocs: Doc, history: Array = [], conversationId: string | null, @@ -124,8 +120,6 @@ export function fetchAnswerSteaming( return new Promise((resolve, reject) => { const body = { question: question, - api_key: apiKey, - embeddings_key: apiKey, active_docs: docPath, history: JSON.stringify(history), conversation_id: conversationId, @@ -188,7 +182,6 @@ export function fetchAnswerSteaming( } export function searchEndpoint( question: string, - apiKey: string, selectedDocs: Doc, conversation_id: string | null, history: Array = [], diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 35aadd9a..ed4e41e4 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -23,7 +23,6 @@ export const fetchAnswer = createAsyncThunk( await fetchAnswerSteaming( question, signal, - state.preference.apiKey, state.preference.selectedDocs!, state.conversation.queries, state.conversation.conversationId, @@ -47,7 +46,6 @@ export const fetchAnswer = createAsyncThunk( searchEndpoint( //search for sources post streaming question, - state.preference.apiKey, state.preference.selectedDocs!, state.conversation.conversationId, state.conversation.queries, @@ -81,7 +79,6 @@ export const fetchAnswer = createAsyncThunk( const answer = await fetchAnswerApi( question, signal, - state.preference.apiKey, state.preference.selectedDocs!, state.conversation.queries, state.conversation.conversationId, From eed672314742a4bc87e709efb1a7767cd3739fa4 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 28 Mar 2024 19:25:35 +0530 Subject: [PATCH 2/5] feat(settings): api keys tab --- frontend/src/Setting.tsx | 217 ++++++++++++++++++++++++++- frontend/src/components/Dropdown.tsx | 10 +- 2 files changed, 219 insertions(+), 8 deletions(-) diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx index 56423797..87632ce1 100644 --- a/frontend/src/Setting.tsx +++ b/frontend/src/Setting.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import ArrowLeft from './assets/arrow-left.svg'; import ArrowRight from './assets/arrow-right.svg'; +import Exit from './assets/exit.svg'; import Trash from './assets/trash.svg'; import { selectPrompt, @@ -21,13 +22,16 @@ type PromptProps = { }; const Setting: React.FC = () => { - const tabs = ['General', 'Prompts', 'Documents']; + const tabs = ['General', 'Prompts', 'Documents', 'API Keys']; //const tabs = ['General', 'Prompts', 'Documents', 'Widgets']; const [activeTab, setActiveTab] = useState('General'); const [prompts, setPrompts] = useState< { name: string; id: string; type: string }[] >([]); + const [apiKeys, setApiKeys] = useState< + { name: string; key: string; source: string; id: string }[] + >([]); const selectedPrompt = useSelector(selectPrompt); const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); const documents = useSelector(selectSourceDocs); @@ -41,7 +45,18 @@ const Setting: React.FC = () => { const updateWidgetScreenshot = (screenshot: File | null) => { setWidgetScreenshot(screenshot); }; - + const fetchAPIKeys = async () => { + try { + const response = await fetch(`${apiHost}/api/get_api_keys`); + if (!response.ok) { + throw new Error('Failed to fetch API Keys'); + } + const apiKeys = await response.json(); + setApiKeys(apiKeys); + } catch (error) { + console.log(error); + } + }; useEffect(() => { const fetchPrompts = async () => { try { @@ -55,8 +70,8 @@ const Setting: React.FC = () => { console.error(error); } }; - fetchPrompts(); + fetchAPIKeys(); }, []); const onDeletePrompt = (name: string, id: string) => { @@ -184,6 +199,8 @@ const Setting: React.FC = () => { onWidgetScreenshotChange={updateWidgetScreenshot} // Add this line /> ); + case 'API Keys': + return ; default: return null; } @@ -468,7 +485,6 @@ const AddPromptModal: React.FC = ({ ); }; - type DocumentsProps = { documents: Doc[] | null; handleDeleteDocument: (index: number, document: Doc) => void; @@ -480,10 +496,10 @@ const Documents: React.FC = ({ }) => { return (
-
+
{/*

Documents

*/} -
+
@@ -617,7 +633,196 @@ const AddDocumentModal: React.FC = ({ ); }; +const APIKeys: React.FC = () => { + const apiKeys = [ + { name: 'Base', source: '2001', key: '131346543' }, + { name: 'Base', source: '2001', key: '131346543' }, + { name: 'Base', source: '2001', key: '131346543' }, + { name: 'Base', source: '2001', key: '131346543' }, + ]; + const [isCreateModalOpen, setCreateModal] = useState(false); + const [isSaveKeyModalOpen, setSaveKeyModal] = useState(true); + return ( +
+
+
+ +
+ {isCreateModalOpen && ( + setCreateModal(false)} /> + )} + {isSaveKeyModalOpen && ( + setSaveKeyModal(false)} + /> + )} +
+
+
+ + + + + + + + + + {apiKeys?.map((element, index) => ( + + + + + + + ))} + +
Name + Source document + API Key
{element.name}{element.source}{element.key} + Delete { + event.stopPropagation(); + console.log('deleted api key !'); + }} + /> +
+
+
+
+
+ ); +}; +type SaveAPIKeyModalProps = { + apiKey: string; + close: () => void; +}; +const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { + const [isCopied, setIsCopied] = useState(false); + const handleCopyKey = () => { + navigator.clipboard.writeText(apiKey); + setIsCopied(true); + }; + return ( +
+
+ +

Please save your Key

+

+ This is the only time your key will be shown. +

+
+
+

API Key

+ {apiKey} +
+ +
+ +
+
+ ); +}; +type CreateAPIKeyModalProps = { + close: () => void; +}; +const CreateAPIKeyModal: React.FC = ({ close }) => { + const [APIKeyName, setAPIKeyName] = useState(''); + const [selectedDocPath, setSelectedDocPath] = useState(null); + const docs = useSelector(selectSourceDocs); + const handleCreate = () => { + console.log(selectedDocPath, APIKeyName); + close(); + }; + const extractDocPaths = () => + docs + ? docs.map((doc: Doc) => { + let namePath = doc.name; + if (doc.language === namePath) { + namePath = '.project'; + } + let docPath = 'default'; + if (doc.location === 'local') { + docPath = 'local' + '/' + doc.name + '/'; + } else if (doc.location === 'remote') { + docPath = + doc.language + + '/' + + namePath + + '/' + + doc.version + + '/' + + doc.model + + '/'; + } + return { + label: namePath, + value: docPath, + }; + }) + : []; + + return ( +
+
+ +

Create New API Key

+
+ + API Key Name + + setAPIKeyName(e.target.value)} + /> +
+
+ setSelectedDocPath(value)} + options={extractDocPaths()} + /> +
+ +
+
+ ); +}; const Widgets: React.FC<{ widgetScreenshot: File | null; onWidgetScreenshotChange: (screenshot: File | null) => void; diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index 5654b430..6b063cc2 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -7,18 +7,20 @@ function Dropdown({ onSelect, showDelete, onDelete, + placeholder, }: { options: | string[] | { name: string; id: string; type: string }[] | { label: string; value: string }[]; - selectedValue: string | { label: string; value: string }; + selectedValue: string | { label: string; value: string } | null; onSelect: | ((value: string) => void) | ((value: { name: string; id: string; type: string }) => void) | ((value: { label: string; value: string }) => void); showDelete?: boolean; onDelete?: (value: string) => void; + placeholder?: string; }) { const [isOpen, setIsOpen] = useState(false); return ( @@ -51,7 +53,11 @@ function Dropdown({ !selectedValue && 'text-silver' }`} > - {selectedValue ? selectedValue.label : 'From URL'} + {selectedValue + ? selectedValue.label + : placeholder + ? placeholder + : 'From URL'} )} Date: Fri, 29 Mar 2024 03:26:45 +0530 Subject: [PATCH 3/5] feat(settings): api key endpoints --- frontend/src/Setting.tsx | 204 +++++++++++++++++---------- frontend/src/components/Dropdown.tsx | 18 ++- 2 files changed, 143 insertions(+), 79 deletions(-) diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx index 87632ce1..a27f3d86 100644 --- a/frontend/src/Setting.tsx +++ b/frontend/src/Setting.tsx @@ -13,14 +13,11 @@ import { import { Doc } from './preferences/preferenceApi'; import { useDarkTheme } from './hooks'; import Dropdown from './components/Dropdown'; -type PromptProps = { - prompts: { name: string; id: string; type: string }[]; - 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 apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; +const embeddingsName = + import.meta.env.VITE_EMBEDDINGS_NAME || + 'huggingface_sentence-transformers/all-mpnet-base-v2'; const Setting: React.FC = () => { const tabs = ['General', 'Prompts', 'Documents', 'API Keys']; //const tabs = ['General', 'Prompts', 'Documents', 'Widgets']; @@ -29,9 +26,6 @@ const Setting: React.FC = () => { const [prompts, setPrompts] = useState< { name: string; id: string; type: string }[] >([]); - const [apiKeys, setApiKeys] = useState< - { name: string; key: string; source: string; id: string }[] - >([]); const selectedPrompt = useSelector(selectPrompt); const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); const documents = useSelector(selectSourceDocs); @@ -39,24 +33,12 @@ const Setting: React.FC = () => { const dispatch = useDispatch(); - const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; const [widgetScreenshot, setWidgetScreenshot] = useState(null); const updateWidgetScreenshot = (screenshot: File | null) => { setWidgetScreenshot(screenshot); }; - const fetchAPIKeys = async () => { - try { - const response = await fetch(`${apiHost}/api/get_api_keys`); - if (!response.ok) { - throw new Error('Failed to fetch API Keys'); - } - const apiKeys = await response.json(); - setApiKeys(apiKeys); - } catch (error) { - console.log(error); - } - }; + useEffect(() => { const fetchPrompts = async () => { try { @@ -71,7 +53,6 @@ const Setting: React.FC = () => { } }; fetchPrompts(); - fetchAPIKeys(); }, []); const onDeletePrompt = (name: string, id: string) => { @@ -182,7 +163,6 @@ const Setting: React.FC = () => { dispatch(setPrompt({ name: name, id: id, type: type })) } setPrompts={setPrompts} - apiHost={apiHost} /> ); case 'Documents': @@ -243,13 +223,18 @@ const General: React.FC = () => { }; export default Setting; +type PromptProps = { + prompts: { name: string; id: string; type: string }[]; + selectedPrompt: { name: string; id: string; type: string }; + onSelectPrompt: (name: string, id: string, type: string) => void; + setPrompts: (prompts: { name: string; id: string; type: string }[]) => void; +}; const Prompts: React.FC = ({ prompts, selectedPrompt, onSelectPrompt, setPrompts, - apiHost, }) => { const handleSelectPrompt = ({ name, @@ -634,14 +619,74 @@ const AddDocumentModal: React.FC = ({ ); }; const APIKeys: React.FC = () => { - const apiKeys = [ - { name: 'Base', source: '2001', key: '131346543' }, - { name: 'Base', source: '2001', key: '131346543' }, - { name: 'Base', source: '2001', key: '131346543' }, - { name: 'Base', source: '2001', key: '131346543' }, - ]; const [isCreateModalOpen, setCreateModal] = useState(false); - const [isSaveKeyModalOpen, setSaveKeyModal] = useState(true); + const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false); + const [newKey, setNewKey] = useState(''); + const [apiKeys, setApiKeys] = useState< + { name: string; key: string; source: string; id: string }[] + >([]); + const handleDeleteKey = (id: string) => { + fetch(`${apiHost}/api/delete_api_key`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to delete API Key'); + } + return response.json(); + }) + .then((data) => { + data.status === 'ok' && + setApiKeys((previous) => previous.filter((elem) => elem.id !== id)); + }) + .catch((error) => { + console.error(error); + }); + }; + useEffect(() => { + fetchAPIKeys(); + }, []); + const fetchAPIKeys = async () => { + try { + const response = await fetch(`${apiHost}/api/get_api_keys`); + if (!response.ok) { + throw new Error('Failed to fetch API Keys'); + } + const apiKeys = await response.json(); + setApiKeys(apiKeys); + } catch (error) { + console.log(error); + } + }; + const createAPIKey = (payload: { name: string; source: string }) => { + fetch(`${apiHost}/api/create_api_key`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to create API Key'); + } + return response.json(); + }) + .then((data) => { + setApiKeys([...apiKeys, data]); + setCreateModal(false); //close the create key modal + setNewKey(data.key); + setSaveKeyModal(true); // render the newly created key + fetchAPIKeys(); + }) + .catch((error) => { + console.error(error); + }); + }; return (
@@ -654,11 +699,14 @@ const APIKeys: React.FC = () => {
{isCreateModalOpen && ( - setCreateModal(false)} /> + setCreateModal(false)} + createAPIKey={createAPIKey} + /> )} {isSaveKeyModalOpen && ( setSaveKeyModal(false)} /> )} @@ -687,10 +735,7 @@ const APIKeys: React.FC = () => { alt="Delete" className="h-4 w-4 cursor-pointer hover:opacity-50" id={`img-${index}`} - onClick={(event) => { - event.stopPropagation(); - console.log('deleted api key !'); - }} + onClick={() => handleDeleteKey(element.id)} /> @@ -749,41 +794,48 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { type CreateAPIKeyModalProps = { close: () => void; + createAPIKey: (payload: { name: string; source: string }) => void; }; -const CreateAPIKeyModal: React.FC = ({ close }) => { +const CreateAPIKeyModal: React.FC = ({ + close, + createAPIKey, +}) => { const [APIKeyName, setAPIKeyName] = useState(''); - const [selectedDocPath, setSelectedDocPath] = useState(null); + const [sourcePath, setSourcePath] = useState<{ + label: string; + value: string; + } | null>(null); const docs = useSelector(selectSourceDocs); - const handleCreate = () => { - console.log(selectedDocPath, APIKeyName); - close(); - }; + console.log(docs); + const extractDocPaths = () => docs - ? docs.map((doc: Doc) => { - let namePath = doc.name; - if (doc.language === namePath) { - namePath = '.project'; - } - let docPath = 'default'; - if (doc.location === 'local') { - docPath = 'local' + '/' + doc.name + '/'; - } else if (doc.location === 'remote') { - docPath = - doc.language + - '/' + - namePath + - '/' + - doc.version + - '/' + - doc.model + - '/'; - } - return { - label: namePath, - value: docPath, - }; - }) + ? docs + .filter((doc) => doc.model === embeddingsName) + .map((doc: Doc) => { + let namePath = doc.name; + if (doc.language === namePath) { + namePath = '.project'; + } + let docPath = 'default'; + if (doc.location === 'local') { + docPath = 'local' + '/' + doc.name + '/'; + } else if (doc.location === 'remote') { + docPath = + doc.language + + '/' + + namePath + + '/' + + doc.version + + '/' + + doc.model + + '/'; + } + return { + label: doc.name, + value: docPath, + }; + }) : []; return ( @@ -806,15 +858,21 @@ const CreateAPIKeyModal: React.FC = ({ close }) => {
setSelectedDocPath(value)} + selectedValue={sourcePath} + onSelect={(selection: { label: string; value: string }) => + setSourcePath(selection) + } options={extractDocPaths()} />
{isOpen && ( -
+
{options.map((option: any, index) => (
Date: Fri, 29 Mar 2024 04:13:12 +0530 Subject: [PATCH 4/5] adding dark mode - api key --- frontend/src/Setting.tsx | 17 ++++++++--------- frontend/src/components/Dropdown.tsx | 16 ++++++---------- frontend/tailwind.config.cjs | 3 ++- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx index a27f3d86..f6a25853 100644 --- a/frontend/src/Setting.tsx +++ b/frontend/src/Setting.tsx @@ -760,7 +760,7 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { }; return (
-
+
@@ -771,10 +771,10 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => {

API Key

- {apiKey} + {apiKey}
@@ -806,8 +805,6 @@ const CreateAPIKeyModal: React.FC = ({ value: string; } | null>(null); const docs = useSelector(selectSourceDocs); - console.log(docs); - const extractDocPaths = () => docs ? docs @@ -840,11 +837,13 @@ const CreateAPIKeyModal: React.FC = ({ return (
-
+
-

Create New API Key

+

+ Create New API Key +

API Key Name diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index 67fd54c3..d5907237 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -8,7 +8,6 @@ function Dropdown({ showDelete, onDelete, placeholder, - className, }: { options: | string[] @@ -28,14 +27,11 @@ function Dropdown({ const [isOpen, setIsOpen] = useState(false); return (
-

Please save your Key

+

Please save your Key

This is the only time your key will be shown.

@@ -782,7 +782,7 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => {
@@ -836,15 +836,15 @@ const CreateAPIKeyModal: React.FC = ({ : []; return ( -
-
- -

+ Create New API Key -

-
+ +
API Key Name @@ -855,7 +855,7 @@ const CreateAPIKeyModal: React.FC = ({ onChange={(e) => setAPIKeyName(e.target.value)} />
-
+
= ({ sourcePath && createAPIKey({ name: APIKeyName, source: sourcePath.value }) } - className="float-right m-4 rounded-full bg-purple-30 px-4 py-3 text-white disabled:opacity-50" + className="float-right my-4 rounded-full bg-purple-30 px-4 py-3 text-white disabled:opacity-50" > Create diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index d5907237..3ede2f2f 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -35,7 +35,7 @@ function Dropdown({ >