# Project export: RetAIn

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Intelligent Lecture Generation, Explain Key Concepts
- Devpost: https://devpost.com/software/retain-jd1nqm
- GitHub: https://github.com/AnwarMP/retain
- Demo: https://docs.google.com/presentation/d/1S_jkjzLbbmj82Z7h71u_vlbxTTgQ_yOgDgR0aOhuEQE/edit?usp=sharing
- Video: https://www.youtube.com/embed/oUNIyIH6hqg?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Russell Semsem (24 commits), Harinandan Kotamsetti (15 commits), Anwar Mujeeb (7 commits)

## Devpost submission (written by the team)

### Inspiration

In today's fast-paced academic world, students and young professionals often find themselves overwhelmed by complex concepts and dense academic materials. Whether it's sifting through lecture notes, deciphering lengthy reports, or struggling to understand a difficult research paper, the challenge of grasping key ideas can feel daunting. This confusion can slow down the learning process, leaving students frustrated and unsure of where to focus their efforts. This struggle highlights the necessity for tools that can bridge the gap between overwhelming content and true understanding, offering a more efficient and accessible way to digest critical information. What RetAIn does RetAIn harnesses the power of AI to transform academic materials into personalized, easy-to-understand audio lectures. Whether you’re dealing with notes, research papers, or reports, RetAIn takes your content and breaks it down into tailored explanations, guiding you through the most important concepts. By identifying key themes and simplifying complex information, RetAIn turns your study materials into a structured learning experience, helping you focus on what really matters.

### How we built it

We designed the project specifically for school students, aiming to help them streamline their learning process. Frontend We used React to create a clean and user-friendly interface. Backend Our backend stack consisted of Node.js and an Express server connected to a PostgreSQL database and Amazon S3, which managed user data and stored documents. AI Integration Our flask service implements a Retrieval-Augmented Generation (RAG) model using a Flask API. It processes PDF documents, generates embeddings using OpenAI's model, and stores them in a Pinecone vector database for efficient retrieval. When a question is asked, the system queries Pinecone to find relevant text chunks, then uses OpenAI's GPT-4 model within an AutoGen multi-agent framework to generate a comprehensive response. This RAG system enables intelligent processing of lecture materials, generating summaries, answering questions, and creating study guides based on uploaded documents. For text-to-speech generation, we leveraged the Cartesia TTS API to provide a clear and natural-sounding speaking experience.

### Challenges we ran into

One of the biggest challenges we faced was downsizing the project scope. Initially, we wanted to implement highly advanced features, such as generating article-like outputs with animated visualizations. However, this idea proved too complex for our timeline and resources, as it would have required incorporating rendering engines and advanced animation technologies. After realizing the technical and time constraints, we shifted our focus to building a more streamlined application that handled note summarization and study guide generation effectively. It also became a difficulty incorporating sponsor material effectively, so we ended up falling back on our technical backgrounds more with the experience we had before coming here. Another challenge was finding a balance between simplicity and functionality. We had to make sure the AI-generated content was useful without overwhelming the user with too much information, which required fine-tuning the RAG AI model for better relevance and accuracy.

### What we learned

Throughout this project, we gained valuable insights into both front-end and back-end development as well as the use of AI in practical applications. We learned how to effectively build an intuitive user interface, manage databases for large sets of documents, and integrate AI for real-time note summarization and study guide generation. We also gained experience in scaling back ambitious features in favor of a more focused and usable product, learning to prioritize simplicity and functionality. Conclusion Despite the hurdles, we successfully built a functional and user-friendly AI-powered note-taking app. The project not only provided a valuable learning experience in full-stack development and AI integration but also helped us understand the importance of prioritizing and adapting our goals to match our capabilities and timeline.

## README (from the GitHub repository)

# Retain - AI-Powered Lecture Generation Platform

## Overview
Retain is an intelligent platform that transforms academic materials into personalized, easy-to-understand audio lectures using AI. It helps students and professionals tackle the challenge of processing complex academic content by converting documents into structured, accessible learning experiences.

## Problem Statement
Students and professionals face several challenges with traditional learning materials:
- Overwhelming volume of complex academic content
- Difficulty in identifying and focusing on key concepts
- Time-consuming process of digesting dense research papers and notes
- Need for more accessible and efficient learning methods

## Solution
Retain addresses these challenges by:
- Converting academic materials into personalized audio lectures
- Breaking down complex concepts into digestible explanations
- Using AI to identify and focus on key themes
- Providing a structured learning experience through an intuitive interface

## Tech Stack

### Frontend
- **React.js**: Core frontend framework
- **Tailwind CSS**: Utility-first CSS framework for styling
- **React Router**: For navigation and routing
- **Lucide React**: For UI icons and components
- **ShadCN UI**: Component library for modern UI elements

### Backend
- **Node.js & Express**: Server framework
- **PostgreSQL**: Primary database
- **AWS S3**: Document storage
- **Multer**: File upload handling
- **JWT & Bcrypt**: Authentication and security
- **CORS**: Cross-origin resource sharing

### AI & Machine Learning
- **Flask API**: RAG service implementation
- **OpenAI GPT-4**: For text processing and generation
- **Pinecone**: Vector database for embeddings
- **AutoGen**: Multi-agent framework for AI processing
- **Cartesia TTS API**: Text-to-speech conversion

### DevOps & Infrastructure
- **Docker & Docker Compose**: Containerization and orchestration
- **AWS Infrastructure**: Cloud hosting and services
- **Poetry**: Python dependency management
- **NPM**: Node.js package management

## Key Features

### Document Processing
- PDF document upload and processing
- Intelligent text chunking and embedding
- Secure document storage in AWS S3

### AI-Powered Analysis
- Retrieval-Augmented Generation (RAG) for context-aware responses
- Multi-agent processing using AutoGen
- Intelligent summarization and key concept extraction

### User Experience
- Clean, intuitive web interface
- Personal course and lecture management
- Audio lecture generation and playback
- Progress tracking and management

## Setup and Installation

### Prerequisites
- Node.js (>= 12.0.0)
- Python 3.12
- Docker and Docker Compose
- AWS account credentials
- PostgreSQL

### Backend Setup
1. Clone the repository
2. Navigate to the backend directory:
```bash
cd backend
```

3. Set up PostgreSQL using Docker:
```bash
docker-compose up -d
```

4. Install dependencies:
```bash
npm install
```

5. Configure environment variables:
```env
DB_USER=postgres
DB_HOST=localhost
DB_NAME=retain_db
DB_PASSWORD=your_password
DB_PORT=5432
AWS_ACCESS_KEY_ID=your_aws_key
AWS_SECRET_ACCESS_KEY=your_aws_secret
AWS_REGION=your_aws_region
```

6. Start the server:
```bash
npm run dev
```

### Frontend Setup
1. Navigate to the frontend directory:
```bash
cd frontend
```

2. Install dependencies:
```bash
npm install
```

3. Start the development server:
```bash
npm start
```

### RAG Service Setup
1. Navigate to the RAG service directory
2. Set up Python environment:
```bash
poetry install
```

3. Configure API keys:
```env
OPENAI_API_KEY=your_key
PINECONE_API_KEY=your_key
```

4. Start the Flask server:
```bash
poetry run python agent.py
```

## API Endpoints

### Authentication
- `POST /signup`: User registration
- `POST /login`: User authentication

### Courses
- `POST /create-course`: Create a new course
- `GET /courses/:email`: Get all courses for a user

### Lectures
- `POST /create-lecture`: Create a new lecture
- `GET /lectures/:email/:course_id`: Get all lectures for a course

## Contributing
We welcome contributions! Please read our contributing guidelines and submit pull requests for any improvements.

## License
This project is licensed under the ISC License.

## Acknowledgments
- OpenAI for GPT models
- All contributors and supporters of the project


## Detected evidence (automated analysis)

Indexed codebase: 33 recognized source files, 76 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Flask (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- LangChain (technology) — detected in the code
- OpenAI (technology) — detected in the code
- PostgreSQL (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- AWS (technology) — claimed on Devpost, not found in the code
- Docker (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (42 of 42)

```
backend/.env
backend/.gitignore
backend/docker-compose.yml
backend/index.js
backend/package.json
backend/README.md
backend/schema/init.sql
backend/uploads/first.txt
frontend/.gitignore
frontend/package.json
frontend/public/index.html
frontend/public/robots.txt
frontend/README.md
frontend/src/App.css
frontend/src/App.js
frontend/src/App.test.js
frontend/src/components/Header.js
frontend/src/components/TextToSpeech.js
frontend/src/index.css
frontend/src/index.js
frontend/src/pages/Article.js
frontend/src/pages/Home.js
frontend/src/pages/Landing.js
frontend/src/pages/Lecture.js
frontend/src/pages/Login.js
frontend/src/pages/Signup.js
frontend/src/reportWebVitals.js
frontend/src/setupTests.js
frontend/src/Styles/Header.css
frontend/src/Styles/Landing.css
frontend/src/Styles/Lecture.css
frontend/src/Styles/Login.css
frontend/src/Styles/Signup.css
frontend/src/Styles/Universal.css
frontend/tailwind.config.js
rag_service/agent.py
rag_service/crew-agentic-pav.ipynb
rag_service/pyproject.toml
rag_service/rag.py
rag_service/README.md
rag_service/tester.py
README.md
```

### Dependencies

- backend/package.json: aws-sdk@^2.1691.0, bcrypt@^5.1.1, cors@^2.8.5, dotenv@^16.4.5, express@^4.21.1, multer@^1.4.5-lts.1, nodemon@^3.1.7, pg@^8.13.0
- frontend/package.json: @cartesia/cartesia-js@^1.1.0, @headlessui/react@^2.1.10, @heroicons/react@^2.1.5, @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, react@^18.3.1, react-dom@^18.3.1, react-icons@^5.3.0, react-router-dom@^6.27.0, react-scripts@5.0.1, tailwindcss@^3.4.14, web-vitals@^2.1.4
- rag_service/pyproject.toml: flaml@^2.3.1, flask@^3.0.3, langchain@^0.3.4, langchain-community@^0.3.3, openai@^1.52.0, pandas@^2.2.3, pinecone-client@^3.1.0, pypdf@^5.0.1, requests@^2.32.3, tiktoken@^0.8.0

### Recent commits (newest first)

- Create README.md
- Merge pull request #7 from AnwarMP/multiple-voices
- Updated readme
- added more voices
- finished final version of agent
- Merge pull request #6 from AnwarMP/lecture-system-integration
- Merge branch 'main' into lecture-system-integration
- Integrated TTS to selected lecture modal
- protect Home and Lecture from auth
- Made the initial modal for listening to lecture
- UI changes
- hardcoded getTranscript function to return hard coded transcript, waiting for RAG service
- created s3 bucket and can upload files to bucket thru backend server
- Completed init / temp post request of name, prompt, and file path
- signup fix
- final critical changes
- course functionality
- all style/login changes
- pushed code, basic login
- Merge branch 'main' of https://github.com/AnwarMP/retain

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/docker-compose.yml

```yaml
version: '3.8'

services:
  db:
    image: postgres:13
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: example
      POSTGRES_DB: retain_db
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./schema/init.sql:/docker-entrypoint-initdb.d/init.sql  # This runs your SQL script at startup

volumes:
  db_data:  # Define the volume here

```

### rag_service/pyproject.toml

```
[tool.poetry]
name = "simple-rag-flask"
version = "0.1.0"
description = ""
authors = ["Anwar Mujeeb <ibnmujeeb2003@gmail.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.12,<3.13"
flask = "^3.0.3"
openai = "^1.52.0"
requests = "^2.32.3"
langchain = "^0.3.4"
pandas = "^2.2.3"
langchain-community = "^0.3.3"
pypdf = "^5.0.1"
pinecone-client = "^3.1.0"
tiktoken = "^0.8.0"
flaml = {extras = ["automl"], version = "^2.3.1"}


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

```

### backend/package.json

```
{
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "aws-sdk": "^2.1691.0",
    "bcrypt": "^5.1.1",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.21.1",
    "multer": "^1.4.5-lts.1",
    "pg": "^8.13.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@cartesia/cartesia-js": "^1.1.0",
    "@headlessui/react": "^2.1.10",
    "@heroicons/react": "^2.1.5",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-icons": "^5.3.0",
    "react-router-dom": "^6.27.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "tailwindcss": "^3.4.14"
  }
}

```

### backend/index.js

```javascript
const express = require('express');
const cors = require('cors');
const { Pool } = require('pg');
const bcrypt = require('bcrypt');
const multer = require('multer');
const AWS = require('aws-sdk');
const fs = require('fs');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;

// PostgreSQL connection pool
const pool = new Pool({
  user: process.env.DB_USER,      // From .env
  host: process.env.DB_HOST,      // From .env
  database: process.env.DB_NAME,  // From .env
  password: process.env.DB_PASSWORD, // From .env
  port: process.env.DB_PORT,      // From .env
});

// Middleware
app.use(cors());
app.use(express.json());

// AWS Configuration
AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,      
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 
  region: process.env.AWS_REGION,                
});

const s3 = new AWS.S3(); //AWS S3 Bucket

//Multer Middleware for file uploads
// Set up storage for multer
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/'); // Ensure this directory exists and is writable
  },
  filename: function (req, file, cb) {
    // Use a unique filename to prevent overwriting
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + '-' + file.originalname);
  }
});

// Initialize multer with the storage configuration
const upload = multer({ storage: storage }); //used in the Create lecture route to handle file

// Create users table if it does not exist
const createUsersTable = async () => {
    const query = `
      -- create users table: 
      CREATE TABLE IF NOT EXISTS users (
          first_name VARCHAR(100) NOT NULL,
          last_name VARCHAR(100) NOT NULL,
          email VARCHAR(100) PRIMARY KEY,  -- Set email as the primary key
          password VARCHAR(255) NOT NULL
      );

      -- create courses table: 
      CREATE TABLE IF NOT EXISTS courses (
          course_id SERIAL PRIMARY KEY,
          email VARCHAR(100) REFERENCES users(email) ON DELETE CASCADE,
          created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          course_name VARCHAR(255) NOT NULL
      );

      -- create lectures table: 
      CREATE TABLE IF NOT EXISTS lectures (
        lecture_id SERIAL PRIMARY KEY,
        course_id INT REFERENCES courses(course_id) ON DELETE CASCADE,
        created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        aws_folder_link TEXT,
        lecture_name VARCHAR(255) NOT NULL,
        prompt TEXT,
        transcript TEXT
      );
    `;
  
    try {
      await pool.query(query);
      console.log('Users table created or already exists.');
    } catch (err) {
      console.error('Error creating users table:', err);
    }
  };
  
  // Call the function to create the users table
  createUsersTable();

// Test Route
app.get('/', (req, res) => {
  res.send('Hello, Retain API is running!');
});

// Signup Route -- NEW users table entry. 
app.post('/signup', async (req, res) => {
    const { first_name, last_name, email, password } = req.body;

    try {
        // Check if the email already exists in the database
        const existingUser = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
        if (existingUser.rows.length > 0) {
            return res.status(400).json({ message: 'Email already exists' });
        }

        // If not, hash the password and insert the new user
        const hashedPassword = await bcrypt.hash(password, 10);

        console.log(`new user insert success. cck0`);
        const newUser = await pool.query(
            'INSERT INTO users (first_name, last_name, email, password) VALUES ($1, $2, $3, $4) RETURNING *',
            [first_name, last_name, email, hashedPassword]
        );
        // console.log(`new user insert success. cck1`);

        res.json(newUser.rows[0]); // Send back the newly created user
        // console.log(`new user insert success. cck2`);
        
    } catch (error) {
        console.error('Error executing query', error);
        res.status(500).json({ message: 'Server error. Please try again later.' });
    }
});

// Login Route -- AUTH user from users table. 
app.post('/login', async (req, res) => {
  const { email, password } = req.body;

  try {
    const result = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
    const user = result.rows[0];

    if (user && await bcrypt.compare(password, user.password)) {
      res.status(200).json({ message: 'Login successful', user });
    } else {
      res.status(401).json({ message: 'Invalid email or password' });
    }
  } catch (err) {
    console.error('Error executing query', err);
    res.status(500).send('Error logging in');
  }
});

// Create Course Route
app.post('/create-course', async (req, res) => {
  const { email, course_name } = req.body;

  try {
    // Insert new course into the courses table
    const newCourse = await pool.query(
      'INSERT INTO courses (email, course_name) VALUES ($1, $2) RETURNING *',
      [email, course_name]
    );
    res.status(201).json(newCourse.rows[0]);
  } catch (error) {
    console.error('Error creating course:', error);
    res.status(500).json({ message: 'Server error. Please try again later.' });
  }
});

// Create Lecture Route
app.post('/create-lecture', upload.single('file'), async (req, res) => {
  const { course_id, lecture_name, prompt } = req.body;
  const file = req.file; // Access the uploaded file

  if (!file) {
    return res.status(400).json({ message: 'No file uploaded' });
  }

  try {
    // Read the file from the local filesystem
    const fileContent = fs.readFileSync(file.path);

    // Set up S3 upload parameters
    const params = {
      Bucket: 'calhacks24-retain',
      Key: `lectures/${Date.now()}_${file.originalname}`, // File name to save as in S3
      Body: fileContent,
      ContentType: file.mimetype,
    };

    // Uploading files to S3
    s3.upload(params, async 
[truncated — 4256 more characters]
```

### frontend/src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### frontend/src/App.js

```javascript
import logo from './logo.svg';
import './App.css';

import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Landing from './pages/Landing';
import Header from './components/Header'
import Signup from './pages/Signup';
import Login from './pages/Login';
import Lecture from './pages/Lecture';
import Home from './pages/Home'; 
import Article from './pages/Article';

function App() {
  return (
    <Router>
      <div className="App">
        <Routes>
          <Route path="/" element={<Landing />} />
          <Route path="/Signup" element={<Signup />} />
          <Route path="/Login" element={<Login />} />
          <Route path="/Lecture" element={<Lecture />} />
          <Route path="/Home" element={<Home />} />
          <Route path="/Article" element={<Article />} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

```

### frontend/tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}


```

### rag_service/tester.py

```python
import requests
import json

url = "http://localhost:5000/enhanced_sequential_transcript"

payload = {
    "pdf_url": "https://arxiv.org/pdf/1706.03762.pdf",
    "question": "What are transformers and what do they do?"
}

headers = {
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

if response.status_code == 200:
    result = response.json()
    print("Enhanced Transcript:")
    print(result["enhanced_transcript"])
    print("\nChat History:")
    print(json.dumps(result["chat_history"], indent=2))
else:
    print(f"Error: {response.status_code}")
    print(response.text)
```

### rag_service/rag.py

```python
from flask import Flask, request, jsonify
import openai
import os
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.embeddings import OpenAIEmbeddings
# from langchain_community.vectorstores import Pinecone
from pinecone import Pinecone
from pinecone import ServerlessSpec
import requests
import time
from openai import OpenAI


app = Flask(__name__)

# Set your OpenAI API key
api_key = "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"
openai_api_key = api_key

pinecone_api_key = "724c3281-b7ae-4349-9ae8-743c2e4bee8f"

pc = Pinecone(api_key=pinecone_api_key)


cloud = os.environ.get('PINECONE_CLOUD') or 'aws'
region = os.environ.get('PINECONE_REGION') or 'us-east-1'

spec = ServerlessSpec(cloud=cloud, region=region)
index_name = 'semantic-search-fast'

# Check if index exists, if not create it
existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]

if index_name not in existing_indexes:
    pc.create_index(
        index_name,
        dimension=1536,  # dimensionality of OpenAI embeddings
        metric='cosine',
        spec=spec
    )
    # wait for index to be initialized
    while not pc.describe_index(index_name).status['ready']:
        time.sleep(1)

# Connect to index
index = pc.Index(index_name)

@app.route("/ask", methods=["POST"])
def ask_openai():
    try:
        # Get the URL of the PDF and the question from the request
        data = request.json
        pdf_url = data.get("pdf_url")
        question = data.get("question")

        if not pdf_url or not question:
            return jsonify({"error": "Both PDF URL and question are required"}), 400

        # Step 1: Download the PDF file
        response = requests.get(pdf_url)
        if response.status_code != 200:
            return jsonify({"error": "Failed to download PDF"}), 400
        
        file_path = 'temp.pdf'
        with open(file_path, 'wb') as f:
            f.write(response.content)

        # Step 2: Load the PDF and split it into chunks
        loader = PyPDFLoader(file_path)
        documents = loader.load()
        text_splitter = CharacterTextSplitter(chunk_size=2000, chunk_overlap=0)
        docs = text_splitter.split_documents(documents)

        # Step 3: Generate embeddings using OpenAI
        embeddings = OpenAIEmbeddings(api_key=openai_api_key)

        # Step 4: Upsert documents to Pinecone
        for i, doc in enumerate(docs):
            vector = embeddings.embed_query(doc.page_content)
            index.upsert(vectors=[(str(i), vector, {"text": doc.page_content})])

        # Step 5: Generate embedding for the question
        question_vector = embeddings.embed_query(question)

        ## After querying Pinecone and getting results
        results = index.query(vector=question_vector, top_k=3, include_metadata=True)

        if not results['matches']:
            return jsonify({"error": "No relevant information found"}), 404

        # Extract context from the top matches
        context = ' '.join([match['metadata']['text'] for match in results['matches']])

        # Now generate an answer using OpenAI's LLM
        client = OpenAI(api_key=openai_api_key)

        messages = [
            {"role": "system", "content": "You are an AI assistant that provides helpful and concise answers based on the provided context."},
            {"role": "user", "content": f"Context:\n{context}\n\nQuestion:\n{question}"}
        ]
        
        model = "gpt-3.5-turbo" #"gpt-3.5-turbo-1106"


        response = client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=0
        )

        answer =  response.choices[0].message.content

        pc.delete_index(index_name)

        # Return the generated answer
        return jsonify({"question": question, "answer": answer})


    except Exception as e:
        return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True, port=5001)  # or any other port number you prefer

```

[20 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]