From 166786e52affe05738b1cc127a6bf28f947e036d Mon Sep 17 00:00:00 2001 From: Ashique Date: Sun, 15 Dec 2024 19:57:01 +0530 Subject: [PATCH 1/6] attach button added --- .../QuestionInput/QuestionInput.tsx | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/QuestionInput/QuestionInput.tsx b/frontend/src/components/QuestionInput/QuestionInput.tsx index 8253c3ea..e3bc031d 100644 --- a/frontend/src/components/QuestionInput/QuestionInput.tsx +++ b/frontend/src/components/QuestionInput/QuestionInput.tsx @@ -1,13 +1,14 @@ import { useState } from "react"; import { Stack, TextField } from "@fluentui/react"; import { getTokenOrRefresh } from './token_util'; -import { Send28Filled, BookOpenMicrophone28Filled, SlideMicrophone32Filled } from "@fluentui/react-icons"; +import { Send28Filled, BookOpenMicrophone28Filled, SlideMicrophone32Filled, AttachFilled } from "@fluentui/react-icons"; import { ResultReason, SpeechConfig, AudioConfig, SpeechRecognizer } from 'microsoft-cognitiveservices-speech-sdk'; import { getLanguageText } from '../../utils/languageUtils'; import styles from "./QuestionInput.module.css"; + interface Props { - onSend: (question: string) => void; + onSend: (question: string, file?: File) => void; disabled: boolean; placeholder?: string; clearOnSend?: boolean; @@ -15,16 +16,18 @@ interface Props { export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend }: Props) => { const [question, setQuestion] = useState(""); + const [selectedFile, setSelectedFile] = useState(null); const sendQuestion = () => { if (disabled || !question.trim()) { return; } - onSend(question); + onSend(question, selectedFile || undefined); if (clearOnSend) { setQuestion(""); + setSelectedFile(null); } }; @@ -66,6 +69,12 @@ export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend }: Pr } }; + const onFileChange = (ev: React.ChangeEvent) => { + if (ev.target.files && ev.target.files.length > 0) { + setSelectedFile(ev.target.files[0]); + } + }; + const sendQuestionDisabled = disabled || !question.trim(); return ( @@ -95,6 +104,16 @@ export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend }: Pr > + + ); From 0390507cca106d71306ee99fa3ab08889bf5a3ef Mon Sep 17 00:00:00 2001 From: Ashique Date: Tue, 17 Dec 2024 00:07:28 +0530 Subject: [PATCH 2/6] basic flow completed for image,pdf --- backend/app.py | 9 +++++ frontend/src/api/api.ts | 1 + frontend/src/api/models.ts | 1 + frontend/src/pages/chat/Chat.module.css | 7 ++++ frontend/src/pages/chat/Chat.tsx | 50 +++++++++++++++++++++++-- 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/backend/app.py b/backend/app.py index 903354e5..5bc70de2 100644 --- a/backend/app.py +++ b/backend/app.py @@ -139,6 +139,8 @@ def chatgpt(): start_time = time.time() # Start the timer conversation_id = request.json["conversation_id"] question = request.json["query"] + file = request.json["file"] + logging.info(f"[webbackend] from orchestrator file : {file}") logging.info("[webbackend] conversation_id: " + conversation_id) logging.info("[webbackend] question: " + question) @@ -217,6 +219,13 @@ def getGptSpeechToken(): logging.exception("[webbackend] exception in /api/get-speech-token") return jsonify({"error": str(e)}), 500 +@app.route("/api/upload-file", methods=["POST"]) +def uploadBlob(): + logging.info(f"Starting upload blob function for blob") + if "file" not in request.files: + return jsonify({"error": "No file part in the request"}), 400 + return jsonify({'file_url':'www.sample.com/sample'}) + @app.route("/api/get-storage-account", methods=["GET"]) def getStorageAccount(): if not STORAGE_ACCOUNT: diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index 5851e351..90482f73 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -13,6 +13,7 @@ export async function chatApiGpt(options: ChatRequestGpt): Promise { const [userId, setUserId] = useState(""); const triggered = useRef(false); + const [filePreview, setFilePreview] = useState(null); - const makeApiRequestGpt = async (question: string) => { + + const makeApiRequestGpt = async (question: string,selectedFile?: File) => { lastQuestionRef.current = question; error && setError(undefined); @@ -58,12 +60,43 @@ const Chat = () => { setActiveAnalysisPanelTab(undefined); try { + let fileUrl = null; + if (selectedFile) { + const formData = new FormData(); + formData.append("file", selectedFile); + + // Extract MIME type for file + const fileMimeType = selectedFile.type; + setFileType(fileMimeType); + console.log('error {',fileMimeType); + + // Preview the file based on its type + const previewUrl = URL.createObjectURL(selectedFile); + setFilePreview(previewUrl); + + try { + const uploadResponse = await fetch("/api/upload-file", { + method: "POST", + body: formData, + }); + if (!uploadResponse.ok) { + throw new Error("Failed to upload file"); + } + const uploadResult = await uploadResponse.json(); + fileUrl = uploadResult.file_url; // Assuming the API returns the file URL + } catch (uploadError) { + console.error("File upload error:", uploadError); + setError(uploadError); + return; + } + } const history: ChatTurn[] = answers.map(a => ({ user: a[0], bot: a[1].answer })); const request: ChatRequestGpt = { history: [...history, { user: question, bot: undefined }], approach: Approaches.ReadRetrieveRead, conversation_id: userId, query: question, + file: fileUrl, overrides: { promptTemplate: promptTemplate.length === 0 ? undefined : promptTemplate, excludeCategory: excludeCategory.length === 0 ? undefined : excludeCategory, @@ -261,7 +294,6 @@ const Chat = () => { console.log('activeAnalysisPanelTab is now:', activeAnalysisPanelTab); }; - return (
@@ -269,6 +301,18 @@ const Chat = () => {
+ {/* Preview Section */} + {filePreview && ( +
+ {fileType.startsWith("image") ? ( + file preview + ) : fileType === "text" || fileType === "application/pdf" ? ( +