Merge branch 'main' into feature/api-key-create

feature/api-key-create
Alex 2 months ago committed by GitHub
commit 87585be687
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -207,7 +207,9 @@ def stream():
prompt_id = data["prompt_id"]
else:
prompt_id = 'default'
if 'chunks' in data:
if 'selectedDocs' in data and data['selectedDocs'] is None:
chunks = 0
elif 'chunks' in data:
chunks = int(data["chunks"])
else:
chunks = 2

@ -227,3 +227,119 @@ JSON response indicating the status of the operation:
```json
{ "status": "ok" }
```
### 7. /api/get_api_keys
**Description:**
The endpoint retrieves a list of API keys for the user.
**Request:**
**Method**: `GET`
**Sample JavaScript Fetch Request:**
```js
// get_api_keys (GET http://127.0.0.1:5000/api/get_api_keys)
fetch("http://localhost:5001/api/get_api_keys", {
"method": "GET",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
})
.then((res) => res.text())
.then(console.log.bind(console))
```
**Response:**
JSON response with a list of created API keys:
```json
[
{
"id": "string",
"name": "string",
"key": "string",
"source": "string"
},
...
]
```
### 8. /api/create_api_key
**Description:**
Create a new API key for the user.
**Request:**
**Method**: `POST`
**Headers**: Content-Type should be set to `application/json; charset=utf-8`
**Request Body**: JSON object with the following fields:
* `name` — A name for the API key.
* `source` — The source documents that will be used.
Here is a JavaScript Fetch Request example:
```js
// create_api_key (POST http://127.0.0.1:5000/api/create_api_key)
fetch("http://127.0.0.1:5000/api/create_api_key", {
"method": "POST",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": JSON.stringify({"name":"Example Key Name","source":"Example Source"})
})
.then((res) => res.json())
.then(console.log.bind(console))
```
**Response**
In response, you will get a JSON document containing the `id` and `key`:
```json
{
"id": "string",
"key": "string"
}
```
### 9. /api/delete_api_key
**Description:**
Delete an API key for the user.
**Request:**
**Method**: `POST`
**Headers**: Content-Type should be set to `application/json; charset=utf-8`
**Request Body**: JSON object with the field:
* `id` — The unique identifier of the API key to be deleted.
Here is a JavaScript Fetch Request example:
```js
// delete_api_key (POST http://127.0.0.1:5000/api/delete_api_key)
fetch("http://127.0.0.1:5000/api/delete_api_key", {
"method": "POST",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": JSON.stringify({"id":"API_KEY_ID"})
})
.then((res) => res.json())
.then(console.log.bind(console))
```
**Response:**
In response, you will get a JSON document indicating the status of the operation:
```json
{
"status": "ok"
}
```

@ -4,7 +4,7 @@ export default function MyApp({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<DocsGPTWidget selectDocs="local/docsgpt-sep.zip/"/>
<DocsGPTWidget selectDocs="local/docsgpt-sep.zip/" apiKey="82962c9a-aa77-4152-94e5-a4f84fd44c6a" />
</>
)
}

@ -285,7 +285,7 @@ const Hero = ({ title, description }: { title: string, description: string }) =>
export const DocsGPTWidget = ({
apiHost = 'https://gptcloud.arc53.com',
selectDocs = 'default',
apiKey = 'docsgpt-public',
apiKey = '82962c9a-aa77-4152-94e5-a4f84fd44c6a',
avatar = 'https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png',
title = 'Get AI assistance',
description = 'DocsGPT\'s AI Chatbot is here to help',

@ -15,23 +15,24 @@ import {
import { Doc } from './preferences/preferenceApi';
import { useDarkTheme } from './hooks';
import Dropdown from './components/Dropdown';
import { ActiveState } from './models/misc';
import PromptsModal from './preferences/PromptsModal';
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'];
const Setting: React.FC = () => {
const tabs = ['General', 'Documents', 'API Keys'];
const [activeTab, setActiveTab] = useState('General');
const [prompts, setPrompts] = useState<
{ name: string; id: string; type: string }[]
>([]);
const selectedPrompt = useSelector(selectPrompt);
const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false);
const documents = useSelector(selectSourceDocs);
const [isAddDocumentModalOpen, setAddDocumentModalOpen] = useState(false);
const dispatch = useDispatch();
@ -41,43 +42,6 @@ const Setting: React.FC = () => {
setWidgetScreenshot(screenshot);
};
useEffect(() => {
const fetchPrompts = async () => {
try {
const response = await fetch(`${apiHost}/api/get_prompts`);
if (!response.ok) {
throw new Error('Failed to fetch prompts');
}
const promptsData = await response.json();
setPrompts(promptsData);
} catch (error) {
console.error(error);
}
};
fetchPrompts();
}, []);
const onDeletePrompt = (name: string, id: string) => {
setPrompts(prompts.filter((prompt) => prompt.id !== id));
fetch(`${apiHost}/api/delete_prompt`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
// send id in body only
body: JSON.stringify({ id: id }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to delete prompt');
}
})
.catch((error) => {
console.error(error);
});
};
const handleDeleteClick = (index: number, doc: Doc) => {
const docPath = 'indexes/' + 'local' + '/' + doc.name;
@ -156,17 +120,6 @@ const Setting: React.FC = () => {
switch (activeTab) {
case 'General':
return <General />;
case 'Prompts':
return (
<Prompts
prompts={prompts}
selectedPrompt={selectedPrompt}
onSelectPrompt={(name, id, type) =>
dispatch(setPrompt({ name: name, id: id, type: type }))
}
setPrompts={setPrompts}
/>
);
case 'Documents':
return (
<Documents
@ -193,6 +146,9 @@ const General: React.FC = () => {
const themes = ['Light', 'Dark'];
const languages = ['English'];
const chunks = ['0', '2', '4', '6', '8', '10'];
const [prompts, setPrompts] = useState<
{ name: string; id: string; type: string }[]
>([]);
const selectedChunks = useSelector(selectChunks);
const [isDarkTheme, toggleTheme] = useDarkTheme();
const [selectedTheme, setSelectedTheme] = useState(
@ -200,6 +156,24 @@ const General: React.FC = () => {
);
const dispatch = useDispatch();
const [selectedLanguage, setSelectedLanguage] = useState(languages[0]);
const selectedPrompt = useSelector(selectPrompt);
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
useEffect(() => {
const fetchPrompts = async () => {
try {
const response = await fetch(`${apiHost}/api/get_prompts`);
if (!response.ok) {
throw new Error('Failed to fetch prompts');
}
const promptsData = await response.json();
setPrompts(promptsData);
} catch (error) {
console.error(error);
}
};
fetchPrompts();
}, []);
return (
<div className="mt-[59px]">
<div className="mb-4">
@ -212,6 +186,8 @@ const General: React.FC = () => {
setSelectedTheme(option);
option !== selectedTheme && toggleTheme();
}}
size="w-56"
rounded="3xl"
/>
</div>
<div className="mb-4">
@ -223,9 +199,11 @@ const General: React.FC = () => {
options={languages}
selectedValue={selectedLanguage}
onSelect={setSelectedLanguage}
size="w-56"
rounded="3xl"
/>
</div>
<div>
<div className="mb-4">
<p className="font-bold text-jet dark:text-bright-gray">
Chunks processed per query
</p>
@ -234,6 +212,19 @@ const General: React.FC = () => {
options={chunks}
selectedValue={selectedChunks}
onSelect={(value: string) => dispatch(setChunks(value))}
size="w-56"
rounded="3xl"
/>
</div>
<div>
<Prompts
prompts={prompts}
selectedPrompt={selectedPrompt}
onSelectPrompt={(name, id, type) =>
dispatch(setPrompt({ name: name, id: id, type: type }))
}
setPrompts={setPrompts}
apiHost={apiHost}
/>
</div>
</div>
@ -241,12 +232,6 @@ 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<PromptProps> = ({
prompts,
@ -263,11 +248,20 @@ const Prompts: React.FC<PromptProps> = ({
id: string;
type: string;
}) => {
setNewPromptName(name);
setEditPromptName(name);
onSelectPrompt(name, id, type);
};
const [newPromptName, setNewPromptName] = useState(selectedPrompt.name);
const [newPromptName, setNewPromptName] = useState('');
const [newPromptContent, setNewPromptContent] = useState('');
const [editPromptName, setEditPromptName] = useState('');
const [editPromptContent, setEditPromptContent] = useState('');
const [currentPromptEdit, setCurrentPromptEdit] = useState({
id: '',
name: '',
type: '',
});
const [modalType, setModalType] = useState<'ADD' | 'EDIT'>('ADD');
const [modalState, setModalState] = useState<ActiveState>('INACTIVE');
const handleAddPrompt = async () => {
try {
@ -291,6 +285,7 @@ const Prompts: React.FC<PromptProps> = ({
{ name: newPromptName, id: newPrompt.id, type: 'private' },
]);
}
setModalState('INACTIVE');
onSelectPrompt(newPromptName, newPrompt.id, newPromptContent);
setNewPromptName(newPromptName);
} catch (error) {
@ -298,16 +293,14 @@ const Prompts: React.FC<PromptProps> = ({
}
};
const handleDeletePrompt = () => {
setPrompts(prompts.filter((prompt) => prompt.id !== selectedPrompt.id));
console.log('selectedPrompt.id', selectedPrompt.id);
const handleDeletePrompt = (id: string) => {
setPrompts(prompts.filter((prompt) => prompt.id !== id));
fetch(`${apiHost}/api/delete_prompt`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ id: selectedPrompt.id }),
body: JSON.stringify({ id: id }),
})
.then((response) => {
if (!response.ok) {
@ -316,7 +309,6 @@ const Prompts: React.FC<PromptProps> = ({
// 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) => {
@ -324,50 +316,65 @@ const Prompts: React.FC<PromptProps> = ({
});
};
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',
},
const fetchPromptContent = async (id: string) => {
console.log('fetching prompt content');
try {
const response = await fetch(
`${apiHost}/api/get_single_prompt?id=${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);
},
);
if (!response.ok) {
throw new Error('Failed to fetch prompt content');
}
};
fetchPromptContent();
}, [selectedPrompt]);
const promptContent = await response.json();
setEditPromptContent(promptContent.content);
} catch (error) {
console.error(error);
}
};
const handleSaveChanges = () => {
const handleSaveChanges = (id: string, type: string) => {
fetch(`${apiHost}/api/update_prompt`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: selectedPrompt.id,
name: newPromptName,
content: newPromptContent,
id: id,
name: editPromptName,
content: editPromptContent,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to update prompt');
}
onSelectPrompt(newPromptName, selectedPrompt.id, selectedPrompt.type);
setNewPromptName(newPromptName);
if (setPrompts) {
const existingPromptIndex = prompts.findIndex(
(prompt) => prompt.id === id,
);
if (existingPromptIndex === -1) {
setPrompts([
...prompts,
{ name: editPromptName, id: id, type: type },
]);
} else {
const updatedPrompts = [...prompts];
updatedPrompts[existingPromptIndex] = {
name: editPromptName,
id: id,
type: type,
};
setPrompts(updatedPrompts);
}
}
setModalState('INACTIVE');
onSelectPrompt(editPromptName, id, type);
})
.catch((error) => {
console.error(error);
@ -375,76 +382,65 @@ const Prompts: React.FC<PromptProps> = ({
};
return (
<div className="mt-[59px]">
<div className="mb-4">
<p className="font-semibold dark:text-bright-gray">Active Prompt</p>
<Dropdown
options={prompts}
selectedValue={selectedPrompt.name}
onSelect={handleSelectPrompt}
/>
</div>
<div className="mb-4">
<p className="dark:text-bright-gray">Prompt name </p>{' '}
<p className="mb-2 text-xs italic text-eerie-black dark:text-bright-gray">
start by editing name
</p>
<input
type="text"
value={newPromptName}
placeholder="Active Prompt Name"
className="w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:bg-transparent dark:text-white"
onChange={(e) => setNewPromptName(e.target.value)}
/>
</div>
<div className="mb-4">
<p className="mb-2 dark:text-bright-gray">Prompt content</p>
<textarea
className="h-32 w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:bg-transparent dark:text-white"
value={newPromptContent}
onChange={(e) => setNewPromptContent(e.target.value)}
placeholder="Active prompt contents"
/>
</div>
<div className="flex justify-between">
<button
className={`rounded-lg bg-green-500 px-4 py-2 font-bold text-white transition-all hover:bg-green-700 ${
newPromptName === selectedPrompt.name
? 'cursor-not-allowed opacity-50'
: ''
}`}
onClick={handleAddPrompt}
disabled={newPromptName === selectedPrompt.name}
>
Add New Prompt
</button>
<button
className={`rounded-lg bg-red-500 px-4 py-2 font-bold text-white transition-all hover:bg-red-700 ${
selectedPrompt.type === 'public'
? 'cursor-not-allowed opacity-50'
: ''
}`}
onClick={handleDeletePrompt}
disabled={selectedPrompt.type === 'public'}
>
Delete Prompt
</button>
<button
className={`rounded-lg bg-blue-500 px-4 py-2 font-bold text-white transition-all hover:bg-blue-700 ${
selectedPrompt.type === 'public'
? 'cursor-not-allowed opacity-50'
: ''
}`}
onClick={handleSaveChanges}
disabled={selectedPrompt.type === 'public'}
>
Save Changes
</button>
<>
<div>
<div className="mb-4 flex flex-row items-center gap-8">
<div>
<p className="font-semibold dark:text-bright-gray">Active Prompt</p>
<Dropdown
options={prompts}
selectedValue={selectedPrompt.name}
onSelect={handleSelectPrompt}
size="w-56"
rounded="3xl"
showEdit
showDelete
onEdit={({
id,
name,
type,
}: {
id: string;
name: string;
type: string;
}) => {
setModalType('EDIT');
setEditPromptName(name);
fetchPromptContent(id);
setCurrentPromptEdit({ id: id, name: name, type: type });
setModalState('ACTIVE');
}}
onDelete={handleDeletePrompt}
/>
</div>
<button
className="mt-[24px] rounded-3xl border-2 border-solid border-purple-30 px-5 py-3 text-purple-30 hover:bg-purple-30 hover:text-white"
onClick={() => {
setModalType('ADD');
setModalState('ACTIVE');
}}
>
Add new
</button>
</div>
</div>
</div>
<PromptsModal
type={modalType}
modalState={modalState}
setModalState={setModalState}
newPromptName={newPromptName}
setNewPromptName={setNewPromptName}
newPromptContent={newPromptContent}
setNewPromptContent={setNewPromptContent}
editPromptName={editPromptName}
setEditPromptName={setEditPromptName}
editPromptContent={editPromptContent}
setEditPromptContent={setEditPromptContent}
currentPromptEdit={currentPromptEdit}
handleAddPrompt={handleAddPrompt}
handleEditPrompt={handleSaveChanges}
/>
</>
);
};
@ -499,10 +495,8 @@ const Documents: React.FC<DocumentsProps> = ({
}) => {
return (
<div className="mt-8">
<div className="flex flex-col overflow-x-auto">
{/* <h2 className="text-xl font-semibold">Documents</h2> */}
<div className="mt-[27px] w-max rounded-xl border dark:border-chinese-silver">
<div className="flex flex-col">
<div className="mt-[27px] w-max overflow-x-auto rounded-xl border dark:border-chinese-silver">
<table className="block w-full table-auto content-center justify-center text-center dark:text-bright-gray">
<thead>
<tr>

@ -1,10 +1,16 @@
import { useState } from 'react';
import Arrow2 from '../assets/dropdown-arrow.svg';
import Edit from '../assets/edit.svg';
import Trash from '../assets/trash.svg';
function Dropdown({
options,
selectedValue,
onSelect,
size = 'w-32',
rounded = 'xl',
showEdit,
onEdit,
showDelete,
onDelete,
placeholder,
@ -20,6 +26,10 @@ function Dropdown({
| ((value: string) => void)
| ((value: { name: string; id: string; type: string }) => void)
| ((value: { label: string; value: string }) => void);
size?: string;
rounded?: 'xl' | '3xl';
showEdit?: boolean;
onEdit?: (value: { name: string; id: string; type: string }) => void;
showDelete?: boolean;
onDelete?: (value: string) => void;
placeholder?: string;
@ -28,19 +38,22 @@ function Dropdown({
}) {
const [isOpen, setIsOpen] = useState(false);
return (
<div className={fullWidth ? 'relative w-full' : 'relative mt-2 w-32'}>
<div
className={[
typeof selectedValue === 'string'
? 'relative mt-2'
: 'relative align-middle',
size,
].join(' ')}
>
<button
onClick={() => setIsOpen(!isOpen)}
className={`flex w-full cursor-pointer items-center justify-between border-2 border-silver bg-white p-3 dark:border-chinese-silver dark:bg-transparent ${
isOpen ? 'rounded-t-3xl' : 'rounded-3xl'
className={`flex w-full cursor-pointer items-center justify-between border-2 border-silver bg-white px-5 py-3 dark:border-chinese-silver dark:bg-transparent ${
isOpen ? `rounded-t-${rounded}` : `rounded-${rounded}`
}`}
>
{typeof selectedValue === 'string' ? (
<span
className={`${
alignMidddle && 'flex-1'
} overflow-hidden text-ellipsis dark:text-bright-gray`}
>
<span className="overflow-hidden text-ellipsis dark:text-bright-gray">
{selectedValue}
</span>
) : (
@ -67,7 +80,7 @@ function Dropdown({
/>
</button>
{isOpen && (
<div className="absolute left-0 right-0 z-50 -mt-1 max-h-40 overflow-y-auto rounded-b-xl border-2 bg-white shadow-lg dark:border-chinese-silver dark:bg-dark-charcoal">
<div className="absolute left-0 right-0 z-20 -mt-1 max-h-40 overflow-y-auto rounded-b-xl border-2 bg-white shadow-lg dark:border-chinese-silver dark:bg-dark-charcoal">
{options.map((option: any, index) => (
<div
key={index}
@ -86,9 +99,35 @@ function Dropdown({
? option.name
: option.label}
</span>
{showEdit && onEdit && (
<img
src={Edit}
alt="Edit"
className="mr-4 h-4 w-4 cursor-pointer hover:opacity-50"
onClick={() => {
onEdit({
id: option.id,
name: option.name,
type: option.type,
});
setIsOpen(false);
}}
/>
)}
{showDelete && onDelete && (
<button onClick={() => onDelete(option)} className="p-2">
Delete
<button
onClick={() => onDelete(option.id)}
disabled={option.type === 'public'}
>
<img
src={Trash}
alt="Delete"
className={`mr-2 h-4 w-4 cursor-pointer hover:opacity-50 ${
option.type === 'public'
? 'cursor-not-allowed opacity-50'
: ''
}`}
/>
</button>
)}
</div>

@ -24,6 +24,12 @@ function SourceDropdown({
const embeddingsName =
import.meta.env.VITE_EMBEDDINGS_NAME ||
'huggingface_sentence-transformers/all-mpnet-base-v2';
const handleEmptyDocumentSelect = () => {
dispatch(setSelectedDocs(null));
setIsDocsListOpen(false);
};
return (
<div className="relative w-5/6 rounded-3xl">
<button
@ -35,7 +41,7 @@ function SourceDropdown({
<span className="ml-1 mr-2 flex-1 overflow-hidden text-ellipsis text-left dark:text-bright-gray">
<div className="flex flex-row gap-2">
<p className="max-w-3/4 truncate whitespace-nowrap">
{selectedDocs?.name}
{selectedDocs?.name || ''}
</p>
<p className="flex flex-col items-center justify-center">
{selectedDocs?.version}
@ -93,6 +99,14 @@ function SourceDropdown({
<p className="ml-5 py-3">No default documentation.</p>
</div>
)}
<div
className="flex cursor-pointer items-center justify-between hover:bg-gray-100 dark:text-bright-gray dark:hover:bg-purple-taupe"
onClick={handleEmptyDocumentSelect}
>
<span className="ml-4 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3">
Empty
</span>
</div>
</div>
)}
</div>

@ -6,7 +6,7 @@ const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
export function fetchAnswerApi(
question: string,
signal: AbortSignal,
selectedDocs: Doc,
selectedDocs: Doc | null,
history: Array<any> = [],
conversationId: string | null,
promptId: string | null,
@ -28,24 +28,26 @@ export function fetchAnswerApi(
title: any;
}
> {
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
let docPath = 'default';
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
if (selectedDocs) {
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
}
}
//in history array remove all keys except prompt and response
history = history.map((item) => {
@ -89,31 +91,33 @@ export function fetchAnswerApi(
export function fetchAnswerSteaming(
question: string,
signal: AbortSignal,
selectedDocs: Doc,
selectedDocs: Doc | null,
history: Array<any> = [],
conversationId: string | null,
promptId: string | null,
chunks: string,
onEvent: (event: MessageEvent) => void,
): Promise<Answer> {
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
let docPath = 'default';
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
if (selectedDocs) {
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
}
}
history = history.map((item) => {
@ -186,7 +190,7 @@ export function fetchAnswerSteaming(
}
export function searchEndpoint(
question: string,
selectedDocs: Doc,
selectedDocs: Doc | null,
conversation_id: string | null,
history: Array<any> = [],
chunks: string,
@ -196,24 +200,25 @@ export function searchEndpoint(
"question": "Summarise",
"conversation_id": null,
"history": "[]" */
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
let docPath = 'default';
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
if (selectedDocs) {
let namePath = selectedDocs.name;
if (selectedDocs.language === namePath) {
namePath = '.project';
}
if (selectedDocs.location === 'local') {
docPath = 'local' + '/' + selectedDocs.name + '/';
} else if (selectedDocs.location === 'remote') {
docPath =
selectedDocs.language +
'/' +
namePath +
'/' +
selectedDocs.version +
'/' +
selectedDocs.model +
'/';
}
}
const body = {

@ -0,0 +1,213 @@
import { ActiveState } from '../models/misc';
function AddPrompt({
setModalState,
handleAddPrompt,
newPromptName,
setNewPromptName,
newPromptContent,
setNewPromptContent,
}: {
setModalState: (state: ActiveState) => void;
handleAddPrompt?: () => void;
newPromptName: string;
setNewPromptName: (name: string) => void;
newPromptContent: string;
setNewPromptContent: (content: string) => void;
}) {
return (
<div className="rounded-3xl px-4 py-2">
<p className="mb-1 text-xl text-jet dark:text-bright-gray">Add Prompt</p>
<p className="mb-7 text-xs text-[#747474] dark:text-[#7F7F82]">
Add your custom prompt and save it to DocsGPT
</p>
<div>
<input
placeholder="Prompt Name"
type="text"
className="h-10 w-full rounded-lg border-2 border-silver px-3 outline-none dark:bg-transparent dark:text-silver"
value={newPromptName}
onChange={(e) => setNewPromptName(e.target.value)}
></input>
<div className="relative bottom-12 left-3 mt-[-3.00px]">
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
Prompt Name
</span>
</div>
<div className="relative top-[7px] left-3">
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
Prompt Text
</span>
</div>
<textarea
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:bg-transparent dark:text-silver"
value={newPromptContent}
onChange={(e) => setNewPromptContent(e.target.value)}
></textarea>
</div>
<div className="mt-6 flex flex-row-reverse gap-4">
<button
onClick={handleAddPrompt}
className="rounded-3xl bg-purple-30 px-5 py-2 text-white transition-all hover:opacity-90"
>
Save
</button>
<button
onClick={() => {
setModalState('INACTIVE');
}}
className="cursor-pointer font-medium dark:text-light-gray"
>
Cancel
</button>
</div>
</div>
);
}
function EditPrompt({
setModalState,
handleEditPrompt,
editPromptName,
setEditPromptName,
editPromptContent,
setEditPromptContent,
currentPromptEdit,
}: {
setModalState: (state: ActiveState) => void;
handleEditPrompt?: (id: string, type: string) => void;
editPromptName: string;
setEditPromptName: (name: string) => void;
editPromptContent: string;
setEditPromptContent: (content: string) => void;
currentPromptEdit: { name: string; id: string; type: string };
}) {
return (
<div className="rounded-3xl px-4 py-2">
<p className="mb-1 text-xl text-jet dark:text-bright-gray">Edit Prompt</p>
<p className="mb-7 text-xs text-[#747474] dark:text-[#7F7F82]">
Edit your custom prompt and save it to DocsGPT
</p>
<div>
<input
placeholder="Prompt Name"
type="text"
className="h-10 w-full rounded-lg border-2 border-silver px-3 outline-none dark:bg-transparent dark:text-silver"
value={editPromptName}
onChange={(e) => setEditPromptName(e.target.value)}
></input>
<div className="relative bottom-12 left-3 mt-[-3.00px]">
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
Prompt Name
</span>
</div>
<div className="relative top-[7px] left-3">
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
Prompt Text
</span>
</div>
<textarea
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:bg-transparent dark:text-silver"
value={editPromptContent}
onChange={(e) => setEditPromptContent(e.target.value)}
></textarea>
</div>
<div className="mt-6 flex flex-row-reverse gap-4">
<button
className={`rounded-3xl bg-purple-30 px-5 py-2 text-white transition-all ${
currentPromptEdit.type === 'public'
? 'cursor-not-allowed opacity-50'
: 'hover:opacity-90'
}`}
onClick={() => {
handleEditPrompt &&
handleEditPrompt(currentPromptEdit.id, currentPromptEdit.type);
}}
disabled={currentPromptEdit.type === 'public'}
>
Save
</button>
<button
onClick={() => {
setModalState('INACTIVE');
}}
className="cursor-pointer font-medium dark:text-light-gray"
>
Cancel
</button>
</div>
</div>
);
}
export default function PromptsModal({
modalState,
setModalState,
type,
newPromptName,
setNewPromptName,
newPromptContent,
setNewPromptContent,
editPromptName,
setEditPromptName,
editPromptContent,
setEditPromptContent,
currentPromptEdit,
handleAddPrompt,
handleEditPrompt,
}: {
modalState: ActiveState;
setModalState: (state: ActiveState) => void;
type: 'ADD' | 'EDIT';
newPromptName: string;
setNewPromptName: (name: string) => void;
newPromptContent: string;
setNewPromptContent: (content: string) => void;
editPromptName: string;
setEditPromptName: (name: string) => void;
editPromptContent: string;
setEditPromptContent: (content: string) => void;
currentPromptEdit: { name: string; id: string; type: string };
handleAddPrompt?: () => void;
handleEditPrompt?: (id: string, type: string) => void;
}) {
let view;
if (type === 'ADD') {
view = (
<AddPrompt
setModalState={setModalState}
handleAddPrompt={handleAddPrompt}
newPromptName={newPromptName}
setNewPromptName={setNewPromptName}
newPromptContent={newPromptContent}
setNewPromptContent={setNewPromptContent}
/>
);
} else if (type === 'EDIT') {
view = (
<EditPrompt
setModalState={setModalState}
handleEditPrompt={handleEditPrompt}
editPromptName={editPromptName}
setEditPromptName={setEditPromptName}
editPromptContent={editPromptContent}
setEditPromptContent={setEditPromptContent}
currentPromptEdit={currentPromptEdit}
/>
);
} else {
view = <></>;
}
return (
<article
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} fixed top-0 left-0 z-30 h-screen w-screen bg-gray-alpha`}
>
<article className="mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg dark:bg-outer-space">
{view}
</article>
</article>
);
}

@ -308,6 +308,8 @@ export default function Upload({
onSelect={(value: { label: string; value: string }) =>
setUrlType(value)
}
size="w-full"
rounded="3xl"
/>
{urlType.label !== 'Reddit' ? (
<>

Loading…
Cancel
Save