mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge branch 'main' of https://github.com/arc53/DocsGPT
This commit is contained in:
@@ -316,7 +316,7 @@ class UploadFile(Resource):
|
||||
for file in files:
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(temp_dir, filename))
|
||||
|
||||
print(f"Saved file: {filename}")
|
||||
zip_path = shutil.make_archive(
|
||||
base_name=os.path.join(save_dir, job_name),
|
||||
format="zip",
|
||||
@@ -324,6 +324,26 @@ class UploadFile(Resource):
|
||||
)
|
||||
final_filename = os.path.basename(zip_path)
|
||||
shutil.rmtree(temp_dir)
|
||||
task = ingest.delay(
|
||||
settings.UPLOAD_FOLDER,
|
||||
[
|
||||
".rst",
|
||||
".md",
|
||||
".pdf",
|
||||
".txt",
|
||||
".docx",
|
||||
".csv",
|
||||
".epub",
|
||||
".html",
|
||||
".mdx",
|
||||
".json",
|
||||
".xlsx",
|
||||
".pptx",
|
||||
],
|
||||
job_name,
|
||||
final_filename,
|
||||
user,
|
||||
)
|
||||
else:
|
||||
file = files[0]
|
||||
final_filename = secure_filename(file.filename)
|
||||
@@ -350,9 +370,10 @@ class UploadFile(Resource):
|
||||
final_filename,
|
||||
user,
|
||||
)
|
||||
except Exception as err:
|
||||
return make_response(jsonify({"success": False, "error": str(err)}), 400)
|
||||
|
||||
except Exception as err:
|
||||
print(f"Error: {err}")
|
||||
return make_response(jsonify({"success": False, "error": str(err)}), 400)
|
||||
return make_response(jsonify({"success": True, "task_id": task.id}), 200)
|
||||
|
||||
|
||||
@@ -423,6 +444,11 @@ class TaskStatus(Resource):
|
||||
|
||||
task = celery.AsyncResult(task_id)
|
||||
task_meta = task.info
|
||||
print(f"Task status: {task.status}")
|
||||
if not isinstance(
|
||||
task_meta, (dict, list, str, int, float, bool, type(None))
|
||||
):
|
||||
task_meta = str(task_meta) # Convert to a string representation
|
||||
except Exception as err:
|
||||
return make_response(jsonify({"success": False, "error": str(err)}), 400)
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ function Dropdown({
|
||||
: option.description
|
||||
}`}
|
||||
</span>
|
||||
{showEdit && onEdit && (
|
||||
{showEdit && onEdit && option.type !== 'public' && (
|
||||
<img
|
||||
src={Edit}
|
||||
alt="Edit"
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import newChatIcon from '../assets/openNewChat.svg';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Hero from '../Hero';
|
||||
import ArrowDown from '../assets/arrow-down.svg';
|
||||
import newChatIcon from '../assets/openNewChat.svg';
|
||||
import Send from '../assets/send.svg';
|
||||
import SendDark from '../assets/send_dark.svg';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import SpinnerDark from '../assets/spinner-dark.svg';
|
||||
import Spinner from '../assets/spinner.svg';
|
||||
import RetryIcon from '../components/RetryIcon';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Hero from '../Hero';
|
||||
import { useDarkTheme, useMediaQuery } from '../hooks';
|
||||
import { ShareConversationModal } from '../modals/ShareConversationModal';
|
||||
import { setConversation, updateConversationId } from './conversationSlice';
|
||||
import { selectConversationId } from '../preferences/preferenceSlice';
|
||||
import { AppDispatch } from '../store';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import { handleSendFeedback } from './conversationHandlers';
|
||||
import { FEEDBACK, Query } from './conversationModels';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import {
|
||||
addQuery,
|
||||
fetchAnswer,
|
||||
selectQueries,
|
||||
selectStatus,
|
||||
setConversation,
|
||||
updateConversationId,
|
||||
updateQuery,
|
||||
} from './conversationSlice';
|
||||
|
||||
@@ -302,14 +303,14 @@ export default function Conversation() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 pb-1 sm:w-[62%] h-auto">
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 z-3 sm:w-[62%] h-auto">
|
||||
<div className="flex w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
|
||||
<textarea
|
||||
id="inputbox"
|
||||
ref={inputRef}
|
||||
tabIndex={1}
|
||||
placeholder={t('inputPlaceholder')}
|
||||
className={`inputbox-style h-16 w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white pt-5 pb-[22px] text-base leading-tight opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`}
|
||||
className={`inputbox-style w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-transparent py-5 text-base leading-tight opacity-100 focus:outline-none dark:bg-transparent dark:text-bright-gray`}
|
||||
onInput={handleInput}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import { forwardRef, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import DocsGPT3 from '../assets/cute_docsgpt3.svg';
|
||||
import Dislike from '../assets/dislike.svg?react';
|
||||
import Document from '../assets/document.svg';
|
||||
@@ -16,13 +18,13 @@ import Sources from '../assets/sources.svg';
|
||||
import Avatar from '../components/Avatar';
|
||||
import CopyButton from '../components/CopyButton';
|
||||
import Sidebar from '../components/Sidebar';
|
||||
import SpeakButton from '../components/TextToSpeechButton';
|
||||
import {
|
||||
selectChunks,
|
||||
selectSelectedDocs,
|
||||
} from '../preferences/preferenceSlice';
|
||||
import classes from './ConversationBubble.module.css';
|
||||
import { FEEDBACK, MESSAGE_TYPE } from './conversationModels';
|
||||
import SpeakButton from '../components/TextToSpeechButton';
|
||||
|
||||
const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
|
||||
|
||||
@@ -41,6 +43,7 @@ const ConversationBubble = forwardRef<
|
||||
{ message, type, className, feedback, handleFeedback, sources, retryBtn },
|
||||
ref,
|
||||
) {
|
||||
// const bubbleRef = useRef<HTMLDivElement | null>(null);
|
||||
const chunks = useSelector(selectChunks);
|
||||
const selectedDocs = useSelector(selectSelectedDocs);
|
||||
const [isLikeHovered, setIsLikeHovered] = useState(false);
|
||||
@@ -141,7 +144,7 @@ const ConversationBubble = forwardRef<
|
||||
/>
|
||||
<p className="text-base font-semibold">Sources</p>
|
||||
</div>
|
||||
<div className="ml-3 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="fade-in ml-3 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
||||
{sources?.slice(0, 3)?.map((source, index) => (
|
||||
<div key={index} className="relative">
|
||||
@@ -190,7 +193,7 @@ const ConversationBubble = forwardRef<
|
||||
</div>
|
||||
{activeTooltip === index && (
|
||||
<div
|
||||
className={`absolute left-1/2 z-30 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl dark:bg-chinese-black dark:text-chinese-silver sm:w-56`}
|
||||
className={`absolute left-1/2 z-50 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl dark:bg-chinese-black dark:text-chinese-silver sm:w-56`}
|
||||
onMouseOver={() => setActiveTooltip(index)}
|
||||
onMouseOut={() => setActiveTooltip(null)}
|
||||
>
|
||||
@@ -231,14 +234,14 @@ const ConversationBubble = forwardRef<
|
||||
<p className="text-base font-semibold">Answer</p>
|
||||
</div>
|
||||
<div
|
||||
className={`ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
type === 'ERROR'
|
||||
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
}`}
|
||||
>
|
||||
<ReactMarkdown
|
||||
className="whitespace-pre-wrap break-normal leading-normal"
|
||||
className="fade-in whitespace-pre-wrap break-normal leading-normal"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
|
||||
@@ -514,3 +514,29 @@ input:-webkit-autofill:focus {
|
||||
.logs-table {
|
||||
font-family: 'IBMPlexMono-Medium', system-ui;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in-bubble {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
animation: fadeInUp 0.5s forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ActiveState } from '../models/misc';
|
||||
import Exit from '../assets/exit.svg';
|
||||
import Input from '../components/Input';
|
||||
import React from 'react';
|
||||
|
||||
function AddPrompt({
|
||||
setModalState,
|
||||
@@ -9,6 +10,7 @@ function AddPrompt({
|
||||
setNewPromptName,
|
||||
newPromptContent,
|
||||
setNewPromptContent,
|
||||
disableSave,
|
||||
}: {
|
||||
setModalState: (state: ActiveState) => void;
|
||||
handleAddPrompt?: () => void;
|
||||
@@ -16,6 +18,7 @@ function AddPrompt({
|
||||
setNewPromptName: (name: string) => void;
|
||||
newPromptContent: string;
|
||||
setNewPromptContent: (content: string) => void;
|
||||
disableSave: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative">
|
||||
@@ -23,6 +26,8 @@ function AddPrompt({
|
||||
className="absolute top-3 right-4 m-2 w-3"
|
||||
onClick={() => {
|
||||
setModalState('INACTIVE');
|
||||
setNewPromptName('');
|
||||
setNewPromptContent('');
|
||||
}}
|
||||
>
|
||||
<img className="filter dark:invert" src={Exit} />
|
||||
@@ -41,7 +46,7 @@ function AddPrompt({
|
||||
className="h-10 rounded-lg"
|
||||
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
|
||||
@@ -62,6 +67,8 @@ function AddPrompt({
|
||||
<button
|
||||
onClick={handleAddPrompt}
|
||||
className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:opacity-90"
|
||||
disabled={disableSave}
|
||||
title={disableSave && newPromptName ? 'Name already exists' : ''}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
@@ -79,6 +86,7 @@ function EditPrompt({
|
||||
editPromptContent,
|
||||
setEditPromptContent,
|
||||
currentPromptEdit,
|
||||
disableSave,
|
||||
}: {
|
||||
setModalState: (state: ActiveState) => void;
|
||||
handleEditPrompt?: (id: string, type: string) => void;
|
||||
@@ -87,6 +95,7 @@ function EditPrompt({
|
||||
editPromptContent: string;
|
||||
setEditPromptContent: (content: string) => void;
|
||||
currentPromptEdit: { name: string; id: string; type: string };
|
||||
disableSave: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative">
|
||||
@@ -140,7 +149,8 @@ function EditPrompt({
|
||||
handleEditPrompt &&
|
||||
handleEditPrompt(currentPromptEdit.id, currentPromptEdit.type);
|
||||
}}
|
||||
disabled={currentPromptEdit.type === 'public'}
|
||||
disabled={currentPromptEdit.type === 'public' || disableSave}
|
||||
title={disableSave && editPromptName ? 'Name already exists' : ''}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
@@ -151,6 +161,7 @@ function EditPrompt({
|
||||
}
|
||||
|
||||
export default function PromptsModal({
|
||||
existingPrompts,
|
||||
modalState,
|
||||
setModalState,
|
||||
type,
|
||||
@@ -166,6 +177,7 @@ export default function PromptsModal({
|
||||
handleAddPrompt,
|
||||
handleEditPrompt,
|
||||
}: {
|
||||
existingPrompts: { name: string; id: string; type: string }[];
|
||||
modalState: ActiveState;
|
||||
setModalState: (state: ActiveState) => void;
|
||||
type: 'ADD' | 'EDIT';
|
||||
@@ -181,6 +193,25 @@ export default function PromptsModal({
|
||||
handleAddPrompt?: () => void;
|
||||
handleEditPrompt?: (id: string, type: string) => void;
|
||||
}) {
|
||||
const [disableSave, setDisableSave] = React.useState(true);
|
||||
const handlePrompNameChange = (edit: boolean, newName: string) => {
|
||||
const nameExists = existingPrompts.find(
|
||||
(prompt) => newName === prompt.name,
|
||||
);
|
||||
|
||||
if (newName && !nameExists) {
|
||||
setDisableSave(false);
|
||||
} else {
|
||||
setDisableSave(true);
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
setEditPromptName(newName);
|
||||
} else {
|
||||
setNewPromptName(newName);
|
||||
}
|
||||
};
|
||||
|
||||
let view;
|
||||
|
||||
if (type === 'ADD') {
|
||||
@@ -189,9 +220,10 @@ export default function PromptsModal({
|
||||
setModalState={setModalState}
|
||||
handleAddPrompt={handleAddPrompt}
|
||||
newPromptName={newPromptName}
|
||||
setNewPromptName={setNewPromptName}
|
||||
setNewPromptName={handlePrompNameChange.bind(null, false)}
|
||||
newPromptContent={newPromptContent}
|
||||
setNewPromptContent={setNewPromptContent}
|
||||
disableSave={disableSave}
|
||||
/>
|
||||
);
|
||||
} else if (type === 'EDIT') {
|
||||
@@ -200,10 +232,11 @@ export default function PromptsModal({
|
||||
setModalState={setModalState}
|
||||
handleEditPrompt={handleEditPrompt}
|
||||
editPromptName={editPromptName}
|
||||
setEditPromptName={setEditPromptName}
|
||||
setEditPromptName={handlePrompNameChange.bind(null, true)}
|
||||
editPromptContent={editPromptContent}
|
||||
setEditPromptContent={setEditPromptContent}
|
||||
currentPromptEdit={currentPromptEdit}
|
||||
disableSave={disableSave}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -58,7 +58,8 @@ export default function Prompts({
|
||||
}
|
||||
setModalState('INACTIVE');
|
||||
onSelectPrompt(newPromptName, newPrompt.id, newPromptContent);
|
||||
setNewPromptName(newPromptName);
|
||||
setNewPromptName('');
|
||||
setNewPromptContent('');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -178,6 +179,7 @@ export default function Prompts({
|
||||
</div>
|
||||
</div>
|
||||
<PromptsModal
|
||||
existingPrompts={prompts}
|
||||
type={modalType}
|
||||
modalState={modalState}
|
||||
setModalState={setModalState}
|
||||
|
||||
@@ -76,7 +76,7 @@ function Upload({
|
||||
<div className="relative w-32 h-32 rounded-full">
|
||||
<div className="absolute inset-0 rounded-full shadow-[0_0_10px_2px_rgba(0,0,0,0.3)_inset] dark:shadow-[0_0_10px_2px_rgba(0,0,0,0.3)_inset]"></div>
|
||||
<div
|
||||
className={`absolute inset-0 rounded-full ${progressPercent === 100 ? 'shadow-xl shadow-lime-300/50 dark:shadow-lime-300/50 bg-gradient-to-r from-white to-gray-400 dark:bg-gradient-to-br dark:from-gray-500 dark:to-gray-300' : 'shadow-[0_2px_0_#FF3D00_inset] dark:shadow-[0_2px_0_#FF3D00_inset]'}`}
|
||||
className={`absolute inset-0 rounded-full ${progressPercent === 100 ? 'shadow-xl shadow-lime-300/50 dark:shadow-lime-300/50 bg-gradient-to-r from-white to-gray-400 dark:bg-gradient-to-br dark:from-gray-500 dark:to-gray-300' : 'shadow-[0_4px_0_#7D54D1] dark:shadow-[0_4px_0_#7D54D1]'}`}
|
||||
style={{
|
||||
animation: `${progressPercent === 100 ? 'none' : 'rotate 2s linear infinite'}`,
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user