# Project export: Office Hours

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: An all-in-one note-taking and study tool generator!
- Devpost: https://devpost.com/software/office-hours-7y2cnf
- GitHub: https://github.com/smh-94/calhacksofficehours
- Video: https://www.youtube.com/embed/mAdInKSOSSE?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — RussianGravy (61 commits), mango-ship-it (16 commits), Aarav Desai (12 commits)

## Devpost submission (written by the team)

### Overview

The problem/

### Inspiration

We wanted to build a web and mobile app for study tools that are generated from our own notes. We found that other online platforms who offer something similar lack certain features and decided to build a platform that remedies that need.

### What it does

Our web-app is an online study-tool that generates study tools such as practice exams, flashcards, or short form quizzes using your own notes. We used a multi-agent AI system to handle summarization, flash card and Q&A generation. We then implemented LLM content validation to ensure materials are logical and accurate.

### How we built it

We used the Fetch.AI framework along with the Groq library LLM integration to create a multi-agent study tool generation system. We used agents to ensure content is validated at each step of the content generation, which served to reduce hallucinations in LLM content. Our front end was built using React and our rich text editor incorporated TinyMCE, with a python backend handling agent communication through post requests and context handling. User authentication was handled with Firebase.

### Challenges we ran into

It was 3 of our members' first hackathon so understanding efficient development as a team took time. We experienced challenges like feature creep, and merge-conflicts, but by working together and using our mentors we pulled through. Eventually, we managed to build an effective functioning product with lots more potential.

## README (from the GitHub repository)

# calhacksofficehours

## Detected evidence (automated analysis)

Indexed codebase: 44 recognized source files, 69 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Firebase (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code

## Codebase structure (from repository index)

### Files (75 of 75)

```
.DS_Store
.gitignore
agents/bureau.py
agents/contentChecker.py
agents/contentCleaner.py
agents/economics.txt
agents/groqtest.py
agents/notes/subjectTaggingAgent.py
agents/quiz_generator.py
agents/quizAgent.py
agents/quizCheck.py
agents/raw_notes.txt
agents/short_answer.py
agents/true_or_false.py
LICENSE
myenv/bin/activate
myenv/bin/activate.csh
myenv/bin/activate.fish
myenv/bin/Activate.ps1
myenv/bin/distro
myenv/bin/httpx
myenv/bin/jsonschema
myenv/bin/normalizer
myenv/bin/pip
myenv/bin/pip3
myenv/bin/pip3.12
myenv/bin/python
myenv/bin/python3
myenv/bin/python3.12
myenv/bin/uvicorn
myenv/pyvenv.cfg
quiz_data.json
react-front/package.json
react-front/postcss.config.js
react-front/public/index.html
react-front/public/manifest.json
react-front/public/robots.txt
react-front/README.md
react-front/src/api/server.js
react-front/src/api/test.json
react-front/src/api/txtResponses/raw_notes.txt
react-front/src/api/txtResponses/sample.txt
react-front/src/App.js
react-front/src/App.test.js
react-front/src/components/Auth.jsx
react-front/src/components/Error.jsx
react-front/src/components/Flashcard.jsx
react-front/src/components/FuncBar.jsx
react-front/src/components/Menus.jsx
react-front/src/components/Modal.jsx
react-front/src/components/Navbar.jsx
react-front/src/components/NewNoteButton.jsx
react-front/src/components/NoteIcon.jsx
react-front/src/components/PrivateRoute.js
react-front/src/components/Searchbar.jsx
react-front/src/components/TestWindow.jsx
react-front/src/config/firebase.js
react-front/src/contexts/AuthContext.js
react-front/src/css/App.css
react-front/src/css/card_animation.css
react-front/src/index.css
react-front/src/index.js
react-front/src/pages/ExamPage.jsx
react-front/src/pages/HomePage.jsx
react-front/src/pages/LandingPage.jsx
react-front/src/pages/LogInPage.jsx
react-front/src/pages/NotePage.jsx
react-front/src/pages/SignUpPage.jsx
react-front/src/reportWebVitals.js
react-front/src/setupTests.js
react-front/tailwind.config.js
README.md
tests/mockNotes.rtf
tests/mockNotes2.rtf
tests/short_document.rtf
```

### Dependencies

- react-front/package.json: @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, @tinymce/tinymce-react@^5.1.1, autoprefixer@^10.4.20, express@^4.21.1, firebase@^10.14.1, multer@^1.4.5-lts.1, postcss@^8.4.47, react@^18.3.1, react-dom@^18.3.1, react-router-dom@^6.27.0, react-scripts@5.0.1, tailwind@^4.0.0, tailwindcss@^3.4.14, web-vitals@^2.1.4

### Recent commits (newest first)

- recent bar
- implemented search results
- summaries added
- better styling
- current
- pretty test
- flash card button styled
- styled existing note buttons
- Merge branch 'main' of https://github.com/smh-94/calhacksofficehours
- current
- center icon
- styled menus
- changed chem image and added style to home notes
- flashcards added
- study stuff enabled
- flashcards added
- so far so good
- merged
- my current
- fixes

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

### react-front/package.json

```
{
  "name": "react-front",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@tinymce/tinymce-react": "^5.1.1",
    "express": "^4.21.1",
    "firebase": "^10.14.1",
    "multer": "^1.4.5-lts.1",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.27.0",
    "react-scripts": "5.0.1",
    "tailwind": "^4.0.0",
    "web-vitals": "^2.1.4"
  },
  "proxy": "http://localhost:8001",
  "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": {
    "autoprefixer": "^10.4.20",
    "postcss": "^8.4.47",
    "tailwindcss": "^3.4.14"
  }
}

```

### react-front/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();

```

### react-front/src/App.js

```javascript
import { BrowserRouter, Routes, Route, Router } from "react-router-dom";
import HomePage from "./pages/HomePage";
import NotePage from "./pages/NotePage";
import SignUpPage from "./pages/SignUpPage";
import { AuthProvider } from "./contexts/AuthContext";
import LogInPage from "./pages/LogInPage";
import "./index.css";
import { PrivateRoute } from "./components/PrivateRoute";
import LandingPage from "./pages/LandingPage";
import ExamPage from "./pages/ExamPage";

function App() {
  return (
    <BrowserRouter>
      <AuthProvider>
        <Routes>
          <Route path="/" element={<PrivateRoute Component={HomePage} />} />
          <Route
            path="/exam/:id"
            element={<PrivateRoute Component={ExamPage} />}
          />
          <Route
            path="/note/:id"
            element={<PrivateRoute Component={NotePage} />}
          />
          <Route path="/landing" Component={LandingPage} />
          <Route path="/signup" Component={SignUpPage} />
          <Route path="/login" Component={LogInPage} />
        </Routes>
      </AuthProvider>
    </BrowserRouter>
  );
}

export default App;

```

### react-front/src/api/server.js

```javascript
const express = require("express");
const multer = require("multer");
const path = require("path");
const cors = require("cors");
const fs = require("fs");

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

app.use(cors());

// Endpoint to serve the JSON file
app.get("/data", (req, res) => {
  const filePath = path.join(__dirname, "../../../quiz_data.json");
  fs.readFile(filePath, "utf8", (err, data) => {
    if (err) {
      return res.status(500).send({ error: "Failed to read data file." });
    }
    res.setHeader("Content-Type", "application/json");
    res.send(data);
  });
});

const txtResponsesDir = path.join(__dirname, "../../../agents");

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, txtResponsesDir);
  },
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  },
});

const upload = multer({ storage });

app.post("/upload", upload.single("file"), (req, res) => {
  res.json({ message: "File uploaded successfully!" });
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

```

### react-front/postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

### react-front/tailwind.config.js

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

```

### agents/bureau.py

```python
from uagents import Bureau
from true_or_false import true_or_false
from quiz_generator import quiz_generator
from short_answer import short_answer
from contentChecker import content_checker
from contentCleaner import content_cleaner

bureau = Bureau(port=8001)
bureau.add(true_or_false)
bureau.add(quiz_generator)
bureau.add(short_answer)
bureau.add(content_checker)
bureau.add(content_cleaner)

if __name__ == "__main__":
    bureau.run()
```

### agents/quizAgent.py

```python
from uagents import Agent, Bureau, Context, Model
from uagents.setup import fund_agent_if_low
from typing import Any, Dict
import os
from groq import Groq

client = Groq(
    api_key = os.environ.get("GROQ_API_KEY")
)

# class Message(Model):
#     message: str

class quizRequest(Model):
    notes: str

class quizResponse(Model):
    question: str
    answer: str
    incorrectAns: list

quizAgent = Agent(
    name="quizAgent",
    seed="quizphrase",
    port=8002,
    endpoint="127.0.0.1:8002/submit"
)

fund_agent_if_low(quizAgent.wallet.address())

@quizAgent.on_message(model=quizResponse)
async def contentCheckAgent_message_handler(ctx: Context, sender: str, msg: quizResponse):
    chat_completion = client.chat.completions.create(
    messages=[
        {
            "role": "user",
            "content": f"Generate a quiz using the following{msg}\n Notes: \n ",
        }
    ],
    model="llama3-8b-8192",
    )
    print(msg)
    ctx.logger.info(f"Received response from {sender}:")
    ctx.logger.info(f"question: {msg.question}, answer: {msg.answer},inc answer: {msg.incorrectAns}")


 

if __name__ == "__main__":
    quizAgent.run()
```

### agents/groqtest.py

```python
import os

from groq import Groq

client = Groq(
    api_key=os.environ.get("GROQ_API_KEY"),
)

with open('economics.txt', 'r') as file:
    notes = file.read()

prompt = f"""
You are an AI assistant tasked with analyzing a set of notes for two purposes: key phrase extraction and topic segmentation.

Key Phrase Extraction: Identify the most important concepts, terms, or phrases from the notes. Focus on key ideas that would be useful for creating flashcards or summarizing the material. The phrases should represent significant points, definitions, or concepts that a student should study.

Topic Segmentation: Break the notes into distinct sections based on their topics. Each section should represent a coherent group of information about a specific subject or theme. For each topic, provide a brief summary (one sentence) explaining what the section is about.

Here are the notes: {notes}

Please first list the key phrases, followed by the topics and their summaries.
"""

chat_completion = client.chat.completions.create(
    messages=[
        {
            "role": "system",
            "content": prompt,
        }
    ],
    model="llama3-8b-8192",
)

print(chat_completion.choices[0].message.content)
```

### agents/quizCheck.py

```python
from uagents import Agent, Bureau, Context, Model
from uagents.setup import fund_agent_if_low
import os
from groq import Groq

client = Groq(
    api_key = os.environ.get("GROQ_API_KEY")
    )



class checkRequest(Model):
    quiz: str
    answers: str
    notes: str

class QuizResponse(Model):
    check: bool

quizCheckAgent = Agent(name="QuizCheckAgent",
                       seed="quizphrase",
                       port=8001,
                       endpoint="127.0.0.1:8001"
                       )

fund_agent_if_low(quizCheckAgent.wallet.address())


async def handle_get(ctx: Context) -> dict[str, any]:
    chat_completion = client.chat.completions.create(
    messages=[
        {
            "role": "user",
            "content": "Check if the provided quiz can be answered using the original notes:\nQuiz: \n{msg}\n Notes: \n ",
        }
    ],
    model="llama3-8b-8192",
    )
    return {
        chat_completion.choices[0].message.content
    }


# @quizCheckAgent.on_message(checkRequest)
# async def quizCheckAgent_message_handler(ctx: Context, sender: str, msg: Message):
#     chat_completion = client.chat.completions.create(
#         messages=[
#             {
#                 "role": "user",
#                 "content": "Check if the provided quiz can be answered using the original notes:\nQuiz: \n{msg}\n Notes: \n ",
#             }
#         ],
#         model="llama3-8b-8192",
#     )
#     print(chat_completion.choices[0].message.content)
#     ctx.logger.info(f"Received message from {sender}: {msg.message}")
#     ctx.logger.info(f"Returning generated Quiz from Groq")
 

 


if __name__ == "__main__":
    quizCheckAgent.run()
```

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