diff --git a/app/learn/chat/page.tsx b/app/learn/chat/page.tsx
index bbb1051..b499345 100644
--- a/app/learn/chat/page.tsx
+++ b/app/learn/chat/page.tsx
@@ -58,8 +58,10 @@ const Chat: React.FC = () => {
const savedResponse = localStorage.getItem("chatResponse");
if (savedResponse) {
const parsedResponse = JSON.parse(savedResponse);
- const moduleContent =
- parsedResponse.data[0]?.module || "No content available";
+ const moduleContent = parsedResponse.explanation ||
+ parsedResponse.response ||
+ parsedResponse.summary ||
+ "No content available";
setMessages([{ id: Date.now(), content: moduleContent, sender: "ai" }]);
localStorage.removeItem("chatResponse");
}
@@ -88,7 +90,11 @@ const Chat: React.FC = () => {
files: [] // Future enhancement: add file upload support
});
- const aiContent = response.data.data[0]?.module || "Sorry, I couldn't generate a response";
+ const aiContent = response.data.explanation ||
+ response.data.response ||
+ response.data.summary ||
+ response.data.learning_plan ||
+ "Sorry, I couldn't generate a response";
const aiMessage: Message = {
id: Date.now() + 1,
diff --git a/app/learn/page.tsx b/app/learn/page.tsx
index 2ef112b..3e76d4f 100644
--- a/app/learn/page.tsx
+++ b/app/learn/page.tsx
@@ -50,9 +50,30 @@ export default function UploadModule() {
{ name: string; url: string }[]
>([]);
+ const validateFiles = (files: File[]): { validFiles: File[], invalidFiles: File[] } => {
+ return files.reduce((acc, file) => {
+ if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) {
+ acc.validFiles.push(file);
+ } else {
+ acc.invalidFiles.push(file);
+ }
+ return acc;
+ }, { validFiles: [] as File[], invalidFiles: [] as File[] });
+ };
+
const onDragOver = (e: React.DragEvent
- or click to select files + or click to select PDFs
@@ -274,15 +376,11 @@ export default function UploadModule() {- PDFs, slides, videos, notes - we'll make them interactive. + Lecture slides, research papers, notes - we'll make them interactive.
diff --git a/backend/app.py b/backend/app.py index 82072ba..65e587d 100644 --- a/backend/app.py +++ b/backend/app.py @@ -110,6 +110,21 @@ def process_interaction(): current_topic = data.get('current_topic') active_subtopic = data.get('active_subtopic') session_history = data.get('session_history') + + # Process the interaction through the agent service + response = agent_service.start_new_topic(user_input, current_topic=current_topic, active_subtopic=active_subtopic, session_history=session_history) + + # Convert the response to a dictionary + response_dict = response.to_dict() + + return jsonify(response_dict) + + except Exception as e: + print(f"Error processing interaction: {e}") + return jsonify({ + 'error': str(e) + }), 500 + def generate_audio(text): generator = pipeline( text, voice='af_heart', # <= change voice here @@ -150,8 +165,6 @@ def process_text2speech(): if not text: return jsonify({"error": "No text provided"}), 400 - if not text: - return jsonify({"error": "No text provided"}), 400 audio = generate_audio(text) wav_file = io.BytesIO() @@ -160,42 +173,98 @@ def process_text2speech(): return send_file(wav_file, mimetype='audio/wav', as_attachment=False) - # Process the interaction through the agent service - response = agent_service.start_new_topic(user_input, current_topic=current_topic, active_subtopic=active_subtopic, session_history=session_history) - - # Convert the response to a dictionary - response_dict = response.to_dict() - - return jsonify(response_dict) - +def is_valid_pdf(file_url): + """Check if the file is a valid PDF.""" + try: + # For Uploadcare URLs, we can trust the file extension + if 'ucarecdn.com' in file_url: + return True + + # For other URLs, check the content + response = requests.get(file_url, stream=True) + response.raise_for_status() + + # Check content type header first + content_type = response.headers.get('content-type', '').lower() + if 'application/pdf' in content_type: + return True + + # If no content type header, check magic numbers + magic_numbers = response.raw.read(4) + return magic_numbers.startswith(b'%PDF') except Exception as e: - print(f"Error processing interaction: {e}") - return jsonify({ - 'error': str(e) - }), 500 + print(f"Error validating PDF: {e}") + return False @app.route('/process-content', methods=['POST']) def process_content(): """Process uploaded content.""" try: data = request.json + if not data: + return jsonify({'error': 'No data provided'}), 400 + + print("Received data:", data) # Debug log + notes = data.get('notes', '') files = data.get('files', []) # Process files if any processed_files = [] + all_text = [] + + # Add notes if provided + if notes.strip(): + all_text.append(notes) + + # Process each file for file_url in files: + print(f"Processing file URL: {file_url}") # Debug log + + # Skip empty URLs + if not file_url: + continue + + # Validate PDF + if not is_valid_pdf(file_url): + print(f"Invalid PDF URL: {file_url}") # Debug log + return jsonify({ + 'error': f'Invalid or unsupported file format. Only PDF files are allowed.' + }), 400 + local_file = download_file(file_url) if local_file: processed_files.append(local_file) + try: + text = extract_text_from_pdf(local_file) + if text: + all_text.append(text) + except Exception as e: + print(f"Error extracting text from PDF: {e}") + return jsonify({ + 'error': 'Could not extract text from PDF. Please ensure it is a valid PDF file with extractable text.' + }), 400 + + # If no content was processed, return error + if not all_text: + return jsonify({ + 'error': 'No content could be processed' + }), 400 + + # Combine all text and process with Gemini + combined_text = "\n\n".join(all_text) + processed_content = process_with_gemini(combined_text) - # TODO: Process the content and generate learning plan - # For now, return a mock response - response = [{ - 'learning_plan': f"Generated learning plan from {len(processed_files)} files and notes: {notes[:100]}..." - }] + if not processed_content: + return jsonify({ + 'error': 'Failed to process content with AI' + }), 500 - return jsonify(response) + # Return the processed content + return jsonify({ + 'response': processed_content, + 'status': 'success' + }) except Exception as e: print(f"Error processing content: {e}") diff --git a/backend/Requirements.txt b/backend/requirements.txt similarity index 100% rename from backend/Requirements.txt rename to backend/requirements.txt diff --git a/package-lock.json b/package-lock.json index 2373768..145f00f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -440,6 +440,111 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.7.tgz", + "integrity": "sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.7.tgz", + "integrity": "sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.7.tgz", + "integrity": "sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz", + "integrity": "sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz", + "integrity": "sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.7.tgz", + "integrity": "sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz", + "integrity": "sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6874,111 +6979,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.7.tgz", - "integrity": "sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.7.tgz", - "integrity": "sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.7.tgz", - "integrity": "sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz", - "integrity": "sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz", - "integrity": "sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.7.tgz", - "integrity": "sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz", - "integrity": "sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } }