# Project export: Odin AI

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: TreeHacks 2025
- Tagline: Transform raw notes and lecture recordings into interconnected knowledge graphs, revealing key themes and identifying gaps in understanding.
- Devpost: https://devpost.com/software/odin-ai-m5nw4d
- GitHub: https://github.com/ML72/Odin-AI
- Video: https://www.youtube.com/embed/Sn8RU2b9Vxo?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Michael (39 commits), Nishka Kacheria (24 commits), sriramk117 (23 commits), malti (22 commits)

## Devpost submission (written by the team)

### Overview

📄 Overview Odin constructs a knowledge graph from your (possibly handwritten) notes and compares it to a knowledge graph constructed from lecture recordings. From this, we identify lecture topics that are missing from your notes, as well as interactions between main concepts and themes. Based on these personalized knowledge graphs, Odin can also generate quiz questions at the frontier of your abilities, intended to maximize the efficiency of your study process (in accordance with psychology principles such as the Zone of Proximal Development). 💡

### Inspiration

You’re in a lecture hall, fluorescent light shining harshly on the walls, the screeching sound of the chalk and the professor’s voice filling the air. They’re talking about pointers, or maybe eigenvalues, but all you can think about is the ant crawling on your friend’s hand. Should you tell them? Or is it more amusing if they don’t know? You snap out of your haze. How long has it been? 30 seconds? 5 minutes? Whatever it was, you completely missed that part of the lecture. For our team members, this scene hits close to home. As college students who attend large, fast-paced lectures, it’s often impossible to fully capture the content taught in lecture. What’s more, amid P-Sets, clubs, and spending time with friends, it’s hard to block in time to rewatch lecture recordings to see what we missed. And even if we do find the time, it’s possible that we will miss the content again. To solve this problem and make learning more efficient, we created Odin AI. In Norse mythology, Odin sacrificed his eye to see everything that happens in the world. In the same way, Odin AI superpowers your studies, providing visualization and analysis tools that ensure you never miss a topic again! ⭐

### What it does

Simply upload a photo of your handwritten notes from class and a recording from lecture, and let the magic happen. Odin AI will compare your understanding with the ground truth of the lecture recording and construct a personalized graph with sample questions to help you study. With a graph to easily visualize class concepts, you can see how different ideas relate to each other, as well as areas to keep focusing on. As you keep practicing, your graph will evolve to reflect your mastery. No notes? No recording? No worries, Odin doesn't need both. Upload what you have, and Odin AI will still generate a study plan and a concept map. Summary of our main features: Concept graph generation from images of your notes Concept graph generation from recordings of your lectures Personalized quiz question generation based on your concept graph(s) Inspect concept details and connections to other concepts Graph visualization that represents concept relationships (red is closer to lecture material, purple is closer to your notes, edge and node sizes are scaled according to importance, etc.) Automatic and persistent saving of your past graphs, so you can review old content anytime! 🤖

### How we built it

This project heavily leveraged multimodal foundation models and fast-inference AI systems, as well as classical algorithms, to cohesively merge together our multiple data sources. Odin includes the use of Optical Character Recognition (OCR) via Tesseract, Speech-to-Text Transcription via Whisper, Large Language Model prompting via OpenAI API, Markov chain traversals, and self-designed graph algorithms. Our web application is built with React.js with Material UI styling. We use Reagraph for graph visualization. Step 1: Parse Text Data Once a .png of handwritten notes or a .mp3 of a lecture recording is uploaded, we process the different data sources and generate transcriptions of each. We use Tesseract for OCR on handwritten notes, and Whisper for transcription on lecture recordings. We run these initial texts through a "markdown transform" LLM prompt which adds organization and corrects spelling mistakes in the initial transcripts. Step 2: Construct Preliminary Graphs From there, we use an LLM to generate a list of key concept nodes and edges between nodes that describe their connection to each other. Now, we have two graphs: one which describes what we call our "ground truth," or the lecture recording, and the other which describes "understanding", or the handwritten notes. To calculate the importance of each concept, or node, in our graphs, we use a node centrality algorithm we created ourselves that uses metrics like node degree, BLEU score, and term frequency inverse document frequency (TF-IDF). From here, we’re able to calculate edge weights to measure how relevant the connections between two nodes are. Step 3: Combine Graphs and Analyze Knowledge Gaps Once we’ve composed these two graphs and populated them with these values, we’re ready to merge them and find the knowledge gaps! We use cosine similarity in concept embedding space to consolidate similar nodes together and update edge weights in our "understanding" graph, using an algorithm that computes a value based on adjacent nodes in the "understanding" and "ground truth" graphs. Assume that Node A, of weight a_0, connects to Node B, of weight b_0, with Edge E, of weight e_0, in our "ground truth" graph. Similarly, there is a connection of weights a_1, b_1, and e_1 in our "understanding" graph. As we merge to find gaps in understanding, we re-weight e_1 to be: e_1 = (a_1 * b_1 * e_1)/(a_0 * b_0) We now have consistency between our "understanding" and "ground truth" graph. For the final step, we compare edges between our graphs and reweight them into a personalized graph where an edge weight is (for some fine-tuned alpha): edge weight = "ground truth" weight + max(difference("ground truth" weight, "understanding" weight), 0) * alpha This formula allows us to appropriately weigh understanding with the importance of content. Step 4: Visualize Graph and Generate Questions From here, we use these recalculated weights to visualize our personalized graph and write focused practice questions to build mastery. Our graph color-codes nodes that belong in our "understanding" graph as purple and "ground truth" as red. Node sizes and edge thickness are also scaled to correspond to importance. To generate questions, we first conduct a Markov chain traversal on the personalized graph to identify combinations of concepts that would produce the most challenging questinos, given the user understanding. We then use an LLM to generate questions from those concepts, and update our edge weights as mastery increases. This is visualized on our graph in real time. ⛰️

### Challenges we ran into

One of our main challenges arose in our multimodal transcription methods. OCR can be highly inaccurate and has a hard time deciphering a lot of handwritten text. To help smooth these discrepancies, we used an LLM to clean up our text and infer what was actually written or spoken in our files. This worked a lot better and made our model more robust towards bad handwriting and unclear audio, similar conditions one would face when uploading lecture notes and recordings. Building our graph schema was also a challenging undertaking. Rapidly adapting our structure to incorporate more features as we went along resulted in frequent merge conflicts, which took quite a bit of time to resolve. Additionally, coming up with our algorithms to calculate weightage and importance was hard to standardize and resulted in a lot of conversations where we discussed the best way to measure and visualize understanding of material. Building the graph and visualizing edge weights was difficult as well. Our graphics library (Reagraph) had inconsistent scaling on the backend, making it an arduous undertaking to change the thickness of our edges in a way that was aesthetically pleasing and representative of edge importances. We normalized our node weights, which reflected in the size of our nodes, and scaled our edge weights to demonstrate which parts of our understanding were weaker or stronger. 🚀

### Accomplishments we're proud of

We are delighted at how we were able to synthesize our data from multiple sources and compare them in a meaningful way. The first time we were able to see our concept graph from just a simple image was a momentous milestone for our team, and we definitely scared the couple having lunch together at Arrillaga with our excitement. From designing effective graph algorithms and building strong agentic workflows to visualizing the data and integrating our various components, our project was a feat of engineering (with 100+ GitHub commits) which we are proud of. Most of all, we're delighted that we developed a tool that we would use on a regular basis and recommend to friends. 🦙

### What we learned

We all came into this hackathon with varying levels of experience. For some of us, this was our first time using LLMs and handling complex graph visualizations. From this project, we learned so much about web development, AI transcription, graph algorithms, custom data structures, and graphics libraries. Despite the late hours, we had a lot of fun, although the energy drinks and llamas definitely helped with that. 🍓

### What's next

We want to keep working on this project to build functionality for some of the code we’ve already written: diagram transcription with VLMs. Additionally, we’d like to improve our visualization capabilities, especially with regards to our edge weights. We’re excited to keep working on this and make education and learning more personalized and efficient. A few specific extension features we have in mind: Support more types of input (lecture slides, typed documents, diagrams, etc.) Generating an optimized curriculum for learning the content (use a modified topsort) Provide deeper insights on the combined graph and relationships between main concepts

## README (from the GitHub repository)

# Odin AI

In Norse mythology, Odin sacrificed his eye to see everything that happens in the world. In the same way, Odin AI superpowers your studies, providing visualization and analysis tools that ensure you never miss a topic again.

![Odin Example](public/diagrams/example.png)

Odin constructs a knowledge graph from your notes and compares it to a knowledge graph constructed from lecture recordings. This allows easy identification of topics missing from your notes, as well as interactions between various concepts and themes. Based on your knowledge graph, Odin can also generate quiz questions at the frontier of your abilities, intended to maximize the efficiency of your study process.

## Getting started

1. Install dependencies in the project directory:

    ```
    npm install
    ```

2. Replace the placeholders in `keys.ts` with valid API keys.

3. Run the following to start the development server:

    ```
    npm run dev
    ```

4. Open a browser and go to `localhost:5173` to see the app!

5. Type `Ctrl+C` or `q` in the terminal with the server to stop the server.

## How Odin Works

Odin uses a combination of classic algorithms and modern AI techniques. A rough sketch of our pipeline is depicted below.

![Odin Pipeline](public/diagrams/pipeline.png)


## Detected evidence (automated analysis)

Indexed codebase: 37 recognized source files, 76 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (45 of 45)

```
.browserslistrc
.eslintrc.js
.gitignore
capacitor.config.ts
data/sample_md.md
index.html
ionic.config.json
keys.ts
LICENSE
package.json
public/manifest.json
README.md
src/App.css
src/App.tsx
src/components/Alert.tsx
src/components/CustomPage.tsx
src/components/Progress.tsx
src/graph-class.tsx
src/knowledge-gap-finder.tsx
src/main.tsx
src/pages/DisplayGraph.tsx
src/pages/DisplayStats.tsx
src/pages/History.tsx
src/pages/Home.tsx
src/pages/Upload.tsx
src/service/external/diagram_transcribe.ts
src/service/external/generate_concepts.ts
src/service/external/markdownTransform.ts
src/service/external/quiz_gen.ts
src/service/external/transcribeAudio.ts
src/service/external/transcribeWritten.ts
src/service/graphs/combine_graphs.ts
src/service/graphs/graph.ts
src/service/graphs/knowledge_gaps.ts
src/service/graphs/node_weight.ts
src/service/metrics/fordFulkerson.ts
src/service/redux.ts
src/service/upload.ts
src/store/slices/graph.ts
src/store/slices/ui.ts
src/store/store.ts
src/vite-env.d.ts
tsconfig.json
tsconfig.node.json
vite.config.ts
```

### Dependencies

- package.json: @capacitor/android@^4.8.2, @capacitor/app@4.1.1, @capacitor/assets@^2.0.4, @capacitor/cli@4.7.3, @capacitor/core@4.8.0, @capacitor/haptics@4.1.0, @capacitor/ios@^4.8.2, @capacitor/keyboard@4.1.1, @capacitor/preferences@^4.0.2, @capacitor/status-bar@4.1.1, @emotion/react@^11.13.3, @emotion/styled@^11.13.0, @ionic/react@^7.0.0, @ionic/react-router@^7.8.6, @mui/icons-material@^5.16.7, @mui/material@^5.16.7, @reduxjs/toolkit@^1.9.7, @testing-library/jest-dom@^5.17.0, @testing-library/react@^14.3.1, @testing-library/user-event@^14.5.2, @types/aos@^3.0.7, @types/react@^18.3.11, @types/react-dom@^18.3.1, @types/react-router@^5.1.20, @types/react-router-dom@^5.3.3, @types/uuid@^9.0.8, @vitejs/plugin-react@^3.1.0, aos@^2.3.4, dotenv@^16.4.7, eslint@^8.57.1, eslint-plugin-react@^7.37.1, ionicons@^7.4.0, jsdom@^21.1.2, openai@^4.85.1, react@^18.3.1, react-dom@^18.3.1, react-latex-next@^3.0.0, react-redux@^8.1.3, react-router@^5.3.4, react-router-dom@^5.3.4, reagraph@^4.21.2, redux-persist@^6.0.0, tesseract.js@^6.0.0, tsx@^4.19.2, typescript@^4.9.5, uuid@^9.0.1, vite@^4.5.5

### Recent commits (newest first)

- Add readme
- Merge pull request #7 from ML72/generate_question
- Heat maps and better naming conventions
- outgoing edge based coloring
- Quick display graph position fix
- Generate question
- Question generation works
- Connections info
- Merge branch 'generate_question' of https://github.com/ML72/Odin-AI into generate_question
- Generate question beginning
- Merge branch 'main' of https://github.com/ML72/Odinx-AI
- Add logo and rename
- log scale edge weights
- added edge interactivity
- Merge branch 'main' of https://github.com/ML72/Odinx-AI
- Upload page UX fix
- Quiz gen + updating weights
- Merge branch 'main' of https://github.com/ML72/Odinx-AI
- removed fudge factors
- Merge branch 'main' of https://github.com/ML72/Odinx-AI

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

### data/sample_md.md

```markdown
# The American Revolution (1775-1783)

## Introduction
The American Revolution was a political and military struggle between the thirteen American colonies and Great Britain. It resulted in the establishment of the United States of America as an independent nation. The conflict was fueled by grievances over taxation, lack of representation, and British control over colonial affairs.

## Causes of the Revolution

### 1. Taxation Without Representation
The British government imposed several taxes on the American colonies without granting them representation in Parliament. Notable examples include:
- **The Sugar Act (1764):** Imposed taxes on sugar and molasses.
- **The Stamp Act (1765):** Required colonists to pay a tax on printed materials.
- **The Tea Act (1773):** Allowed the British East India Company to sell tea directly to the colonies, leading to the Boston Tea Party.

### 2. Growing Colonial Resistance
Colonists formed groups such as the **Sons of Liberty** to protest British policies. The Boston Massacre (1770) and the Boston Tea Party (1773) were key events that heightened tensions.

```

### package.json

```
{
  "name": "odin-ai",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "lint": "eslint",
    "sync": "npm run build && npx cap sync"
  },
  "dependencies": {
    "@capacitor/android": "^4.8.2",
    "@capacitor/app": "4.1.1",
    "@capacitor/assets": "^2.0.4",
    "@capacitor/core": "4.8.0",
    "@capacitor/haptics": "4.1.0",
    "@capacitor/ios": "^4.8.2",
    "@capacitor/keyboard": "4.1.1",
    "@capacitor/preferences": "^4.0.2",
    "@capacitor/status-bar": "4.1.1",
    "@emotion/react": "^11.13.3",
    "@emotion/styled": "^11.13.0",
    "@ionic/react": "^7.0.0",
    "@ionic/react-router": "^7.8.6",
    "@mui/icons-material": "^5.16.7",
    "@mui/material": "^5.16.7",
    "@reduxjs/toolkit": "^1.9.7",
    "@types/react-router": "^5.1.20",
    "@types/react-router-dom": "^5.3.3",
    "aos": "^2.3.4",
    "dotenv": "^16.4.7",
    "ionicons": "^7.4.0",
    "openai": "^4.85.1",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-latex-next": "^3.0.0",
    "react-redux": "^8.1.3",
    "react-router": "^5.3.4",
    "react-router-dom": "^5.3.4",
    "reagraph": "^4.21.2",
    "redux-persist": "^6.0.0",
    "tesseract.js": "^6.0.0",
    "tsx": "^4.19.2",
    "uuid": "^9.0.1"
  },
  "devDependencies": {
    "@capacitor/cli": "4.7.3",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^14.3.1",
    "@testing-library/user-event": "^14.5.2",
    "@types/aos": "^3.0.7",
    "@types/react": "^18.3.11",
    "@types/react-dom": "^18.3.1",
    "@types/uuid": "^9.0.8",
    "@vitejs/plugin-react": "^3.1.0",
    "eslint": "^8.57.1",
    "eslint-plugin-react": "^7.37.1",
    "jsdom": "^21.1.2",
    "typescript": "^4.9.5",
    "vite": "^4.5.5"
  }
}

```

### src/main.tsx

```typescript
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { persistor } from './store/store';

import App from './App';
import { store } from './store/store';

const container = document.getElementById('root');
const root = createRoot(container!);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </React.StrictMode>
);
```

### src/App.tsx

```typescript
import { Route } from 'react-router-dom';
import {
  IonApp,
  IonRouterOutlet,
  setupIonicReact
} from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router';

/* Your page imports */
import Home from './pages/Home';
import DisplayGraph from './pages/DisplayGraph';
import DisplayStats from './pages/DisplayStats';
import Upload from './pages/Upload';
import History from './pages/History';

/* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css';
import '@ionic/react/css/structure.css';
import '@ionic/react/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/react/css/padding.css';
import '@ionic/react/css/float-elements.css';
import '@ionic/react/css/text-alignment.css';
import '@ionic/react/css/text-transformation.css';
import '@ionic/react/css/flex-utils.css';
import '@ionic/react/css/display.css';

/* Your CSS */
import './App.css';



setupIonicReact();

const App: React.FC = () => {

  return (
    <IonApp>
      <IonReactRouter>
        <IonRouterOutlet>
          {/** Page routing here */}
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/display/:id/graph">
            <DisplayGraph />
          </Route>
          <Route path="/display/:id/stats">
            <DisplayStats />
          </Route>
          <Route path="/upload">
            <Upload />
          </Route>
          <Route path="/history">
            <History />
          </Route>
        </IonRouterOutlet>
      </IonReactRouter>
    </IonApp>
  );
}

export default App;

```

### keys.ts

```typescript
// DO NOT GIT COMMIT CHANGES TO THIS FILE
export const OPENAI_API_KEY = "";

```

### vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### capacitor.config.ts

```typescript
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.odin.app', // Must be unique and should correspond to domain
  appName: 'Odin-AI',
  webDir: 'dist',
  bundledWebRuntime: false
};

export default config;

```

### .eslintrc.js

```javascript
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:react/recommended',
    'eslint:recommended'
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
  }
}

```

### index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Odin AI | Supplement your notes with AI</title>
    
    <base href="/" />
    
    <meta name="color-scheme" content="light dark" />
    <meta
      name="viewport"
      content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" />
    
    <link rel="manifest" href="/manifest.json" />
    
    <link rel="shortcut icon" type="image/png" href="/logo.svg" />
    
    <!-- add to homescreen for ios -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-title" content="Ionic App" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### src/vite-env.d.ts

```typescript
/// <reference types="vite/client" />

```

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