Spaces:
Sleeping
Sleeping
Aditya commited on
Commit ·
a52cc98
1
Parent(s): fc72b2a
Major upgrade: auto-metrics, Plotly charts, Qwen2.5, PDF support, expanded KB
Browse files- Add ROUGE-L and semantic similarity auto-metrics per answer card
- Add Plotly bar charts for aggregate quality/speed comparison in Analytics section
- Upgrade fine-tuned model from TinyLlama to Qwen/Qwen2.5-1.5B-Instruct
- Update Colab notebook with Qwen2.5 model and ChatML prompt format
- Add PDF ingestion to RAG pipeline (drop PDFs into data/pdfs/)
- Add 5 new knowledge base docs: big_o_notation, heaps, sql_basics, typescript_basics, git_basics
- Add data/reference_answers.json with 21 Q&A pairs for automated scoring
- Add HuggingFace Spaces frontmatter and deploy guide to README
- Add plotly, rouge-score, pypdf, langchain-huggingface, numpy to requirements
- README.md +52 -17
- data/docs/big_o_notation.txt +38 -0
- data/docs/git_basics.txt +62 -0
- data/docs/heaps.txt +46 -0
- data/docs/sql_basics.txt +61 -0
- data/docs/typescript_basics.txt +72 -0
- data/pdfs/.gitkeep +0 -0
- data/reference_answers.json +23 -0
- demo.py +121 -5
- requirements.txt +5 -0
- system2_rag.py +19 -5
- system3_finetune_colab.ipynb +4 -42
- system3_inference.py +25 -7
README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# 🧙 CodeSage
|
| 2 |
|
| 3 |
**A comparative study of three LLM strategies for domain-specific question answering.**
|
|
@@ -24,9 +35,9 @@ There is no universal winner. The goal of CodeSage is to **make that trade-off v
|
|
| 24 |
|
| 25 |
1. You type (or click) a programming question.
|
| 26 |
2. All three systems answer it simultaneously.
|
| 27 |
-
3.
|
| 28 |
4. You rate each answer (correctness, completeness, hallucination).
|
| 29 |
-
5. A live metrics table
|
| 30 |
|
| 31 |
---
|
| 32 |
|
|
@@ -42,7 +53,7 @@ There is no universal winner. The goal of CodeSage is to **make that trade-off v
|
|
| 42 |
└─────────┼─────────────────┼────────────────┼─────────┘
|
| 43 |
│ │ │
|
| 44 |
▼ ▼ ▼
|
| 45 |
-
Groq API FAISS index
|
| 46 |
Llama 3.1 8B + HuggingFace (LoRA adapter loaded
|
| 47 |
(prompt only) embeddings locally via PEFT)
|
| 48 |
│
|
|
@@ -52,15 +63,15 @@ There is no universal winner. The goal of CodeSage is to **make that trade-off v
|
|
| 52 |
```
|
| 53 |
|
| 54 |
### System 1 — Baseline LLM
|
| 55 |
-
Sends the question directly to **Llama 3.1 8B** (via Groq) with a short system prompt. No extra knowledge.
|
| 56 |
|
| 57 |
### System 2 — RAG (Retrieval-Augmented Generation)
|
| 58 |
1. The question is embedded with `all-MiniLM-L6-v2` (HuggingFace Sentence Transformers).
|
| 59 |
-
2. Top-3 most similar chunks are retrieved from a **FAISS** vector store built from
|
| 60 |
3. Those chunks are injected as context into the prompt sent to **Llama 3.1 8B** via Groq.
|
| 61 |
|
| 62 |
### System 3 — Fine-Tuned Model
|
| 63 |
-
**
|
| 64 |
|
| 65 |
---
|
| 66 |
|
|
@@ -73,13 +84,30 @@ Sends the question directly to **Llama 3.1 8B** (via Groq) with a short system p
|
|
| 73 |
| Embeddings | `sentence-transformers/all-MiniLM-L6-v2` |
|
| 74 |
| Vector store | FAISS (CPU) via LangChain |
|
| 75 |
| Fine-tuning | HuggingFace Transformers + PEFT (LoRA) |
|
| 76 |
-
| Base model |
|
|
|
|
|
|
|
| 77 |
| Training platform | Google Colab |
|
| 78 |
-
| Background animation | HTML5 Canvas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
---
|
| 81 |
|
| 82 |
-
## How to Run
|
| 83 |
|
| 84 |
### 1. Clone & install
|
| 85 |
```bash
|
|
@@ -101,19 +129,24 @@ streamlit run demo.py
|
|
| 101 |
```
|
| 102 |
The FAISS index is built automatically on first launch.
|
| 103 |
|
| 104 |
-
### 4. (Optional)
|
|
|
|
|
|
|
|
|
|
| 105 |
1. Open `system3_finetune_colab.ipynb` in Google Colab.
|
| 106 |
-
2. Run all cells —
|
| 107 |
3. Download `finetuned_model.zip` and unzip it in the project root.
|
| 108 |
-
4. Restart the
|
| 109 |
|
| 110 |
---
|
| 111 |
|
| 112 |
## Knowledge Base
|
| 113 |
|
| 114 |
-
The RAG system retrieves from
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
|
| 118 |
---
|
| 119 |
|
|
@@ -122,12 +155,14 @@ The RAG system retrieves from 17 topic documents in `data/docs/`:
|
|
| 122 |
```
|
| 123 |
├── demo.py # Streamlit app
|
| 124 |
├── system1_baseline.py # Baseline LLM (Groq)
|
| 125 |
-
├── system2_rag.py # RAG pipeline (FAISS + Groq)
|
| 126 |
-
├── system3_inference.py # Fine-tuned model inference
|
| 127 |
├── system3_finetune_colab.ipynb # LoRA training notebook
|
| 128 |
├── data/
|
| 129 |
-
│ ├── docs/ #
|
|
|
|
| 130 |
│ ├── faiss_index/ # FAISS vector store (auto-built)
|
|
|
|
| 131 |
│ ├── finetune_data.jsonl # Training data for fine-tuning
|
| 132 |
│ └── questions.csv # Evaluation question set
|
| 133 |
├── finetuned_model/ # LoRA adapter (after training)
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: CodeSage
|
| 3 |
+
emoji: 🧙
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.45.0
|
| 8 |
+
app_file: demo.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
# 🧙 CodeSage
|
| 13 |
|
| 14 |
**A comparative study of three LLM strategies for domain-specific question answering.**
|
|
|
|
| 35 |
|
| 36 |
1. You type (or click) a programming question.
|
| 37 |
2. All three systems answer it simultaneously.
|
| 38 |
+
3. Each answer shows auto-computed **ROUGE-L** and **semantic similarity** scores against a reference answer.
|
| 39 |
4. You rate each answer (correctness, completeness, hallucination).
|
| 40 |
+
5. A live metrics table and **Plotly bar charts** aggregate your ratings across all questions.
|
| 41 |
|
| 42 |
---
|
| 43 |
|
|
|
|
| 53 |
└─────────┼─────────────────┼────────────────┼─────────┘
|
| 54 |
│ │ │
|
| 55 |
▼ ▼ ▼
|
| 56 |
+
Groq API FAISS index Qwen2.5-1.5B
|
| 57 |
Llama 3.1 8B + HuggingFace (LoRA adapter loaded
|
| 58 |
(prompt only) embeddings locally via PEFT)
|
| 59 |
│
|
|
|
|
| 63 |
```
|
| 64 |
|
| 65 |
### System 1 — Baseline LLM
|
| 66 |
+
Sends the question directly to **Llama 3.1 8B** (via Groq) with a short system prompt. No extra knowledge.
|
| 67 |
|
| 68 |
### System 2 — RAG (Retrieval-Augmented Generation)
|
| 69 |
1. The question is embedded with `all-MiniLM-L6-v2` (HuggingFace Sentence Transformers).
|
| 70 |
+
2. Top-3 most similar chunks are retrieved from a **FAISS** vector store built from 22 documents (`.txt` files + any PDFs dropped into `data/pdfs/`).
|
| 71 |
3. Those chunks are injected as context into the prompt sent to **Llama 3.1 8B** via Groq.
|
| 72 |
|
| 73 |
### System 3 — Fine-Tuned Model
|
| 74 |
+
**Qwen2.5-1.5B-Instruct** fine-tuned with **LoRA** on ~20 curated programming Q&A pairs. Training is done on Google Colab (free GPU). The adapter is loaded locally via `peft`.
|
| 75 |
|
| 76 |
---
|
| 77 |
|
|
|
|
| 84 |
| Embeddings | `sentence-transformers/all-MiniLM-L6-v2` |
|
| 85 |
| Vector store | FAISS (CPU) via LangChain |
|
| 86 |
| Fine-tuning | HuggingFace Transformers + PEFT (LoRA) |
|
| 87 |
+
| Base model | Qwen/Qwen2.5-1.5B-Instruct |
|
| 88 |
+
| Auto metrics | ROUGE-L (`rouge-score`) + cosine semantic similarity |
|
| 89 |
+
| Charts | Plotly |
|
| 90 |
| Training platform | Google Colab |
|
| 91 |
+
| Background animation | HTML5 Canvas 3D particle network |
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
## Deploy to HuggingFace Spaces
|
| 96 |
+
|
| 97 |
+
1. Create a new Space at [huggingface.co/new-space](https://huggingface.co/new-space) — SDK: **Streamlit**
|
| 98 |
+
2. Push this repo to the Space:
|
| 99 |
+
```bash
|
| 100 |
+
git remote add space https://huggingface.co/spaces/YOUR_USERNAME/codesage
|
| 101 |
+
git push space main
|
| 102 |
+
```
|
| 103 |
+
3. Add your `GROQ_API_KEY` as a Space secret (Settings → Variables and secrets)
|
| 104 |
+
4. The app launches automatically — the FAISS index is built on first run (~30s)
|
| 105 |
+
|
| 106 |
+
> System 3 (fine-tuned) will show "model unavailable" on Spaces unless you upload the `finetuned_model/` adapter folder to the Space repo.
|
| 107 |
|
| 108 |
---
|
| 109 |
|
| 110 |
+
## How to Run Locally
|
| 111 |
|
| 112 |
### 1. Clone & install
|
| 113 |
```bash
|
|
|
|
| 129 |
```
|
| 130 |
The FAISS index is built automatically on first launch.
|
| 131 |
|
| 132 |
+
### 4. (Optional) Expand the knowledge base with PDFs
|
| 133 |
+
Drop any `.pdf` files into `data/pdfs/`, then delete `data/faiss_index/` and restart — PDFs are ingested automatically.
|
| 134 |
+
|
| 135 |
+
### 5. (Optional) Load the fine-tuned model
|
| 136 |
1. Open `system3_finetune_colab.ipynb` in Google Colab.
|
| 137 |
+
2. Run all cells — trains **Qwen2.5-1.5B-Instruct** with LoRA, takes ~15 min on a free T4 GPU.
|
| 138 |
3. Download `finetuned_model.zip` and unzip it in the project root.
|
| 139 |
+
4. Restart the app — System 3 returns real answers.
|
| 140 |
|
| 141 |
---
|
| 142 |
|
| 143 |
## Knowledge Base
|
| 144 |
|
| 145 |
+
The RAG system retrieves from 22 documents in `data/docs/`:
|
| 146 |
+
|
| 147 |
+
`binary_search` · `sorting_algorithms` · `dynamic_programming` · `graph_algorithms` · `trees` · `linked_list` · `stack_queue` · `recursion` · `backtracking` · `greedy_algorithms` · `hashing` · `string_algorithms` · `two_pointers` · `react_hooks` · `rest_api` · `javascript_promises` · `css_flexbox` · `big_o_notation` · `heaps` · `sql_basics` · `typescript_basics` · `git_basics`
|
| 148 |
|
| 149 |
+
Plus any PDFs placed in `data/pdfs/`.
|
| 150 |
|
| 151 |
---
|
| 152 |
|
|
|
|
| 155 |
```
|
| 156 |
├── demo.py # Streamlit app
|
| 157 |
├── system1_baseline.py # Baseline LLM (Groq)
|
| 158 |
+
├── system2_rag.py # RAG pipeline (FAISS + PDF support + Groq)
|
| 159 |
+
├── system3_inference.py # Fine-tuned model inference (Qwen2.5-1.5B)
|
| 160 |
├── system3_finetune_colab.ipynb # LoRA training notebook
|
| 161 |
├── data/
|
| 162 |
+
│ ├── docs/ # 22 knowledge base documents (.txt)
|
| 163 |
+
│ ├── pdfs/ # Drop PDFs here to expand the knowledge base
|
| 164 |
│ ├── faiss_index/ # FAISS vector store (auto-built)
|
| 165 |
+
│ ├── reference_answers.json # Reference answers for ROUGE-L / semantic similarity
|
| 166 |
│ ├── finetune_data.jsonl # Training data for fine-tuning
|
| 167 |
│ └── questions.csv # Evaluation question set
|
| 168 |
├── finetuned_model/ # LoRA adapter (after training)
|
data/docs/big_o_notation.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Big O Notation and Time Complexity
|
| 2 |
+
|
| 3 |
+
Big O notation describes the upper bound of an algorithm's time or space complexity as input size (n) grows. It answers: "How does performance scale?"
|
| 4 |
+
|
| 5 |
+
Common Complexities (best to worst):
|
| 6 |
+
- O(1) – Constant: always same time. Example: array index access, hash map lookup.
|
| 7 |
+
- O(log n) – Logarithmic: halves problem each step. Example: binary search, balanced BST operations.
|
| 8 |
+
- O(n) – Linear: proportional to input. Example: linear search, single loop over array.
|
| 9 |
+
- O(n log n) – Linearithmic: divide and conquer sorts. Example: merge sort, heap sort, quick sort (average).
|
| 10 |
+
- O(n²) – Quadratic: nested loops. Example: bubble sort, insertion sort, selection sort.
|
| 11 |
+
- O(2^n) – Exponential: doubles with each addition. Example: recursive Fibonacci, subset generation.
|
| 12 |
+
- O(n!) – Factorial: permutations. Example: brute-force traveling salesman.
|
| 13 |
+
|
| 14 |
+
Rules for calculating Big O:
|
| 15 |
+
1. Drop constants: O(2n) → O(n)
|
| 16 |
+
2. Drop lower-order terms: O(n² + n) → O(n²)
|
| 17 |
+
3. Different inputs = different variables: two arrays of size a and b → O(a + b) not O(n²)
|
| 18 |
+
|
| 19 |
+
Space Complexity:
|
| 20 |
+
- Also uses Big O notation
|
| 21 |
+
- Measures extra memory used by the algorithm (not including input)
|
| 22 |
+
- Example: merge sort uses O(n) extra space for the merge step
|
| 23 |
+
|
| 24 |
+
Best, Average, Worst Case:
|
| 25 |
+
- Best case (Ω): minimum operations needed (often not useful)
|
| 26 |
+
- Average case (Θ): expected operations for random input
|
| 27 |
+
- Worst case (O): maximum operations (most commonly used for guarantees)
|
| 28 |
+
|
| 29 |
+
Amortized Analysis:
|
| 30 |
+
- Average cost per operation over a sequence
|
| 31 |
+
- Example: dynamic array append is O(1) amortized even though occasional resize is O(n)
|
| 32 |
+
|
| 33 |
+
Common Data Structure Operations:
|
| 34 |
+
- Array: access O(1), search O(n), insert/delete O(n)
|
| 35 |
+
- Linked List: access O(n), search O(n), insert/delete at head O(1)
|
| 36 |
+
- Hash Table: search/insert/delete average O(1), worst O(n)
|
| 37 |
+
- BST (balanced): search/insert/delete O(log n)
|
| 38 |
+
- Heap: insert O(log n), get-min/max O(1), delete O(log n)
|
data/docs/git_basics.txt
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Git Basics
|
| 2 |
+
|
| 3 |
+
Git is a distributed version control system that tracks changes in source code over time.
|
| 4 |
+
|
| 5 |
+
Setup:
|
| 6 |
+
git config --global user.name "Your Name"
|
| 7 |
+
git config --global user.email "you@example.com"
|
| 8 |
+
|
| 9 |
+
Core Workflow:
|
| 10 |
+
git init — initialize a new repository
|
| 11 |
+
git clone <url> — copy a remote repository locally
|
| 12 |
+
git status — show working tree status (staged, unstaged, untracked)
|
| 13 |
+
git add <file> — stage a file for commit
|
| 14 |
+
git add . — stage all changes
|
| 15 |
+
git commit -m "msg" — save staged changes with a message
|
| 16 |
+
git push origin main — push commits to remote
|
| 17 |
+
git pull — fetch and merge from remote
|
| 18 |
+
|
| 19 |
+
Branching:
|
| 20 |
+
git branch — list all local branches
|
| 21 |
+
git branch feature-x — create a new branch
|
| 22 |
+
git checkout feature-x — switch to branch (older syntax)
|
| 23 |
+
git switch feature-x — switch to branch (newer syntax)
|
| 24 |
+
git checkout -b feature-x — create and switch in one step
|
| 25 |
+
git merge feature-x — merge branch into current branch
|
| 26 |
+
git branch -d feature-x — delete a branch (after merge)
|
| 27 |
+
|
| 28 |
+
Remote:
|
| 29 |
+
git remote -v — show remote URLs
|
| 30 |
+
git remote add origin <url> — add a remote
|
| 31 |
+
git fetch origin — download remote changes (don't merge)
|
| 32 |
+
git pull origin main — fetch + merge remote main into current branch
|
| 33 |
+
|
| 34 |
+
Undoing Changes:
|
| 35 |
+
git restore <file> — discard unstaged changes in a file
|
| 36 |
+
git restore --staged <file> — unstage a file
|
| 37 |
+
git revert <commit> — create a new commit that undoes a commit (safe)
|
| 38 |
+
git reset --soft HEAD~1 — undo last commit, keep changes staged
|
| 39 |
+
git reset --hard HEAD~1 — undo last commit AND discard changes (destructive)
|
| 40 |
+
|
| 41 |
+
Viewing History:
|
| 42 |
+
git log — full commit history
|
| 43 |
+
git log --oneline — compact one-line per commit
|
| 44 |
+
git diff — show unstaged changes
|
| 45 |
+
git diff --staged — show staged changes
|
| 46 |
+
git show <commit> — show changes in a specific commit
|
| 47 |
+
|
| 48 |
+
Stashing:
|
| 49 |
+
git stash — temporarily save uncommitted changes
|
| 50 |
+
git stash pop — restore last stash and remove it
|
| 51 |
+
git stash list — show all stashes
|
| 52 |
+
|
| 53 |
+
Rebasing vs Merging:
|
| 54 |
+
- Merge: creates a merge commit, preserves history of both branches
|
| 55 |
+
- Rebase: replays commits on top of another branch, creates linear history
|
| 56 |
+
git rebase main — rebase current branch onto main
|
| 57 |
+
- Rule: never rebase commits that have been pushed to a shared remote
|
| 58 |
+
|
| 59 |
+
.gitignore:
|
| 60 |
+
- Lists files/patterns Git should not track
|
| 61 |
+
- Common entries: node_modules/, __pycache__/, .env, *.pyc, dist/, build/
|
| 62 |
+
- Check gitignore.io for language-specific templates
|
data/docs/heaps.txt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Heaps and Priority Queues
|
| 2 |
+
|
| 3 |
+
A heap is a complete binary tree that satisfies the heap property:
|
| 4 |
+
- Min-Heap: every parent node is smaller than or equal to its children. Root is the minimum.
|
| 5 |
+
- Max-Heap: every parent node is greater than or equal to its children. Root is the maximum.
|
| 6 |
+
|
| 7 |
+
Heap Properties:
|
| 8 |
+
- Complete binary tree: all levels fully filled except possibly the last, filled left to right.
|
| 9 |
+
- Stored efficiently as an array: for node at index i, left child = 2i+1, right child = 2i+2, parent = (i-1)//2.
|
| 10 |
+
|
| 11 |
+
Core Operations and Complexities:
|
| 12 |
+
- Insert (push): Add at end, bubble up (heapify up). O(log n)
|
| 13 |
+
- Get min/max: Return root. O(1)
|
| 14 |
+
- Remove min/max (pop): Replace root with last element, bubble down (heapify down). O(log n)
|
| 15 |
+
- Build heap from array (heapify): O(n) — better than inserting one by one O(n log n)
|
| 16 |
+
- Search: O(n) — heaps do not support efficient search
|
| 17 |
+
|
| 18 |
+
Priority Queue:
|
| 19 |
+
- Abstract data type where each element has a priority
|
| 20 |
+
- Element with highest (or lowest) priority is dequeued first
|
| 21 |
+
- Implemented with a heap
|
| 22 |
+
- Python: use `heapq` module (min-heap by default)
|
| 23 |
+
- heapq.heappush(heap, item)
|
| 24 |
+
- heapq.heappop(heap) — removes and returns smallest
|
| 25 |
+
- heapq.heapify(list) — converts list to heap in O(n)
|
| 26 |
+
- For max-heap in Python: negate values (push -val, pop and negate result)
|
| 27 |
+
|
| 28 |
+
Common Use Cases:
|
| 29 |
+
- Dijkstra's shortest path algorithm
|
| 30 |
+
- Prim's minimum spanning tree
|
| 31 |
+
- K largest / K smallest elements
|
| 32 |
+
- Merge K sorted lists
|
| 33 |
+
- Task scheduling / job queues
|
| 34 |
+
- Median maintenance (two heaps: max-heap for lower half, min-heap for upper half)
|
| 35 |
+
|
| 36 |
+
Heap Sort:
|
| 37 |
+
1. Build a max-heap from the array: O(n)
|
| 38 |
+
2. Repeatedly extract max and place at end: O(n log n)
|
| 39 |
+
3. Total: O(n log n) time, O(1) space (in-place)
|
| 40 |
+
- Not stable (doesn't preserve relative order of equal elements)
|
| 41 |
+
|
| 42 |
+
Example — K largest elements using min-heap of size K:
|
| 43 |
+
- Maintain a min-heap of the K largest seen so far
|
| 44 |
+
- For each element, push it; if heap size > K, pop the smallest
|
| 45 |
+
- Final heap contains K largest elements
|
| 46 |
+
- Time: O(n log k), Space: O(k)
|
data/docs/sql_basics.txt
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SQL Basics
|
| 2 |
+
|
| 3 |
+
SQL (Structured Query Language) is used to manage and query relational databases.
|
| 4 |
+
|
| 5 |
+
Core Statements:
|
| 6 |
+
- SELECT: retrieve data
|
| 7 |
+
- INSERT: add new rows
|
| 8 |
+
- UPDATE: modify existing rows
|
| 9 |
+
- DELETE: remove rows
|
| 10 |
+
- CREATE TABLE: define a new table
|
| 11 |
+
- DROP TABLE: delete a table
|
| 12 |
+
|
| 13 |
+
SELECT Syntax:
|
| 14 |
+
SELECT column1, column2 FROM table_name WHERE condition ORDER BY column LIMIT n;
|
| 15 |
+
- SELECT *: selects all columns (avoid in production for performance)
|
| 16 |
+
- DISTINCT: removes duplicate rows — SELECT DISTINCT city FROM users
|
| 17 |
+
- WHERE: filters rows — WHERE age > 18 AND country = 'India'
|
| 18 |
+
- ORDER BY: sorts results — ORDER BY name ASC / DESC
|
| 19 |
+
- LIMIT: restricts number of rows returned — LIMIT 10
|
| 20 |
+
|
| 21 |
+
Aggregate Functions:
|
| 22 |
+
- COUNT(*): number of rows
|
| 23 |
+
- SUM(col): total of numeric column
|
| 24 |
+
- AVG(col): average value
|
| 25 |
+
- MIN(col) / MAX(col): minimum and maximum values
|
| 26 |
+
- Used with GROUP BY to aggregate per group
|
| 27 |
+
|
| 28 |
+
GROUP BY and HAVING:
|
| 29 |
+
SELECT department, AVG(salary) FROM employees GROUP BY department HAVING AVG(salary) > 50000;
|
| 30 |
+
- GROUP BY groups rows sharing a value into summary rows
|
| 31 |
+
- HAVING filters groups (like WHERE but for aggregates)
|
| 32 |
+
|
| 33 |
+
JOINs — combining tables:
|
| 34 |
+
- INNER JOIN: only rows with matching values in both tables
|
| 35 |
+
- LEFT JOIN: all rows from left table, matched rows from right (NULL if no match)
|
| 36 |
+
- RIGHT JOIN: all rows from right table, matched rows from left
|
| 37 |
+
- FULL OUTER JOIN: all rows from both tables
|
| 38 |
+
|
| 39 |
+
SELECT orders.id, users.name FROM orders INNER JOIN users ON orders.user_id = users.id;
|
| 40 |
+
|
| 41 |
+
Subqueries:
|
| 42 |
+
SELECT name FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
|
| 43 |
+
- Can appear in WHERE, FROM, or SELECT clause
|
| 44 |
+
|
| 45 |
+
Indexes:
|
| 46 |
+
- Speed up SELECT queries at the cost of slower INSERT/UPDATE/DELETE
|
| 47 |
+
- CREATE INDEX idx_name ON table(column)
|
| 48 |
+
- Automatically created on PRIMARY KEY and UNIQUE columns
|
| 49 |
+
- Avoid over-indexing — each index takes storage and slows writes
|
| 50 |
+
|
| 51 |
+
Constraints:
|
| 52 |
+
- PRIMARY KEY: uniquely identifies each row
|
| 53 |
+
- FOREIGN KEY: links to primary key of another table (enforces referential integrity)
|
| 54 |
+
- UNIQUE: no duplicate values in column
|
| 55 |
+
- NOT NULL: column cannot have NULL value
|
| 56 |
+
- CHECK: enforces a condition on column values
|
| 57 |
+
|
| 58 |
+
Transactions:
|
| 59 |
+
- BEGIN / COMMIT / ROLLBACK
|
| 60 |
+
- ACID properties: Atomicity, Consistency, Isolation, Durability
|
| 61 |
+
- All statements in a transaction succeed together or all fail together
|
data/docs/typescript_basics.txt
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TypeScript Basics
|
| 2 |
+
|
| 3 |
+
TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. It adds optional type annotations, interfaces, and generics.
|
| 4 |
+
|
| 5 |
+
Basic Types:
|
| 6 |
+
let name: string = "Alice";
|
| 7 |
+
let age: number = 30;
|
| 8 |
+
let active: boolean = true;
|
| 9 |
+
let items: string[] = ["a", "b"]; // array of strings
|
| 10 |
+
let tuple: [string, number] = ["x", 1]; // fixed-length typed array
|
| 11 |
+
let value: any = "anything"; // opt out of type checking
|
| 12 |
+
let unknown: unknown = 5; // safer than any — must check type before use
|
| 13 |
+
let nothing: void = undefined; // for functions that return nothing
|
| 14 |
+
let n: null = null;
|
| 15 |
+
let u: undefined = undefined;
|
| 16 |
+
|
| 17 |
+
Union and Intersection Types:
|
| 18 |
+
let id: string | number; // can be string OR number
|
| 19 |
+
type AB = TypeA & TypeB; // must satisfy both types
|
| 20 |
+
|
| 21 |
+
Type Aliases:
|
| 22 |
+
type Point = { x: number; y: number };
|
| 23 |
+
type ID = string | number;
|
| 24 |
+
|
| 25 |
+
Interfaces:
|
| 26 |
+
interface User {
|
| 27 |
+
id: number;
|
| 28 |
+
name: string;
|
| 29 |
+
email?: string; // optional property
|
| 30 |
+
readonly createdAt: Date; // cannot be reassigned
|
| 31 |
+
}
|
| 32 |
+
- Interfaces can be extended: interface Admin extends User { role: string }
|
| 33 |
+
- Classes can implement interfaces: class MyUser implements User { ... }
|
| 34 |
+
|
| 35 |
+
Functions:
|
| 36 |
+
function add(a: number, b: number): number { return a + b; }
|
| 37 |
+
const greet = (name: string): string => `Hello, ${name}`;
|
| 38 |
+
function log(msg: string, level?: string): void { ... } // optional param
|
| 39 |
+
|
| 40 |
+
Generics:
|
| 41 |
+
function identity<T>(arg: T): T { return arg; }
|
| 42 |
+
const result = identity<string>("hello");
|
| 43 |
+
|
| 44 |
+
interface ApiResponse<T> { data: T; status: number; }
|
| 45 |
+
// Can use any type: ApiResponse<User>, ApiResponse<string[]>
|
| 46 |
+
|
| 47 |
+
Enums:
|
| 48 |
+
enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT" }
|
| 49 |
+
let dir: Direction = Direction.Up;
|
| 50 |
+
|
| 51 |
+
Type Assertions:
|
| 52 |
+
const input = document.getElementById("name") as HTMLInputElement;
|
| 53 |
+
input.value = "Alice";
|
| 54 |
+
|
| 55 |
+
keyof and typeof:
|
| 56 |
+
type Keys = keyof User; // "id" | "name" | "email" | "createdAt"
|
| 57 |
+
type MyType = typeof someObject; // infer type from value
|
| 58 |
+
|
| 59 |
+
Utility Types:
|
| 60 |
+
Partial<T> — all properties optional
|
| 61 |
+
Required<T> — all properties required
|
| 62 |
+
Readonly<T> — all properties readonly
|
| 63 |
+
Pick<T, K> — only keep listed keys
|
| 64 |
+
Omit<T, K> — remove listed keys
|
| 65 |
+
Record<K, V> — object with keys K and values V
|
| 66 |
+
ReturnType<T> — return type of a function type
|
| 67 |
+
|
| 68 |
+
tsconfig.json key options:
|
| 69 |
+
"strict": true — enables all strict type checks
|
| 70 |
+
"target": "ES2020" — compile output JS version
|
| 71 |
+
"module": "CommonJS" or "ESNext"
|
| 72 |
+
"outDir": "./dist" — output folder
|
data/pdfs/.gitkeep
ADDED
|
File without changes
|
data/reference_answers.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"what is binary search?": "Binary search is an efficient searching algorithm for sorted arrays. It works by repeatedly dividing the search interval in half. Time complexity: O(log n), Space complexity: O(1). Requires the array to be sorted beforehand.",
|
| 3 |
+
"what is the time complexity of binary search?": "Binary search has a time complexity of O(log n) because it halves the search space with each step. Best case is O(1) when the middle element is the target. Space complexity is O(1) for iterative implementation.",
|
| 4 |
+
"what is the difference between stack and queue?": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations.",
|
| 5 |
+
"stack vs queue?": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations.",
|
| 6 |
+
"explain merge sort.": "Merge sort is a divide-and-conquer sorting algorithm. It splits the array in half, recursively sorts each half, then merges the two sorted halves. Time complexity: O(n log n) in all cases. Space complexity: O(n). It is a stable sorting algorithm, making it good for sorting linked lists.",
|
| 7 |
+
"what is quicksort?": "Quicksort is a divide-and-conquer algorithm that picks a pivot element and partitions the array into elements less than and greater than the pivot, then recursively sorts each partition. Average time: O(n log n), Worst case: O(n^2) when pivot is always smallest or largest element. Space: O(log n).",
|
| 8 |
+
"what is a linked list?": "A linked list is a linear data structure where elements are stored in nodes. Each node contains data and a pointer to the next node. Types: Singly (one direction), Doubly (both directions), Circular (last points to first). Advantage: dynamic size, O(1) insertion at head. Disadvantage: no random access, O(n) search.",
|
| 9 |
+
"what is a binary search tree?": "A Binary Search Tree (BST) is a binary tree where for every node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. This property enables O(log n) average search, insert, and delete operations. Worst case is O(n) for unbalanced trees.",
|
| 10 |
+
"what is dynamic programming?": "Dynamic programming solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation. Two approaches: Memoization (top-down, recursion with cache) and Tabulation (bottom-up, iterative). Requires optimal substructure and overlapping subproblems. Examples: Fibonacci, Knapsack, LCS, Coin Change.",
|
| 11 |
+
"what are react hooks?": "React hooks are functions that let you use state and lifecycle features in functional components, introduced in React 16.8. Key hooks: useState for state management, useEffect for side effects like API calls, useContext for consuming context, useRef for DOM references. Rules: only call hooks at top level, only in React functions.",
|
| 12 |
+
"what is usestate in react?": "useState is a React hook for managing local component state. Syntax: const [state, setState] = useState(initialValue). It returns the current state value and a setter function. Calling setState triggers a re-render with the new value. Example: const [count, setCount] = useState(0) then setCount(count + 1) to increment.",
|
| 13 |
+
"what is useeffect in react?": "useEffect is a React hook for handling side effects like API calls, subscriptions, and DOM updates. It runs after every render by default. The dependency array controls when it runs: empty array [] runs only on mount, [value] runs when value changes. Return a cleanup function to run on unmount. Syntax: useEffect(() => { effect }, [deps]).",
|
| 14 |
+
"what is a rest api?": "REST API (Representational State Transfer) is an architectural style for web services using standard HTTP methods. GET retrieves resources, POST creates new resources, PUT updates resources completely, PATCH partially updates, DELETE removes resources. REST is stateless meaning each request contains all needed information. JSON is the most common response format.",
|
| 15 |
+
"what http methods does rest use?": "REST uses five main HTTP methods: GET for reading/retrieving data, POST for creating new resources, PUT for completely updating an existing resource, PATCH for partially updating a resource, and DELETE for removing a resource. These map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE).",
|
| 16 |
+
"what does http status code 404 mean?": "HTTP status code 404 means Not Found. It indicates the server cannot find the requested resource. The URL may be wrong, the resource may have been deleted, or it may never have existed. Other important codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 500 (Internal Server Error).",
|
| 17 |
+
"what is css flexbox?": "CSS Flexbox is a layout model that makes it easy to arrange items in a container. Activated by display: flex on the container. Key properties: flex-direction (row or column), justify-content (main axis alignment: flex-start, center, space-between), align-items (cross axis: stretch, center, flex-start), flex-wrap (allow wrapping). Items can have flex-grow to fill available space.",
|
| 18 |
+
"what is a javascript promise?": "A JavaScript Promise represents an asynchronous operation with three states: pending (initial), fulfilled (success), rejected (failure). Created with new Promise((resolve, reject) => {}). Consumed with .then() for success and .catch() for errors. async/await is syntactic sugar that makes promises look synchronous. Promise.all() runs multiple promises in parallel.",
|
| 19 |
+
"what is inorder traversal?": "Inorder traversal visits tree nodes in Left, Root, Right order. For a Binary Search Tree, inorder traversal produces nodes in ascending sorted order. Algorithm: recursively visit left subtree, then process current node, then recursively visit right subtree. Time complexity: O(n) where n is number of nodes.",
|
| 20 |
+
"what is the difference between bfs and dfs?": "BFS (Breadth-First Search) explores level by level using a queue data structure. It finds shortest path in unweighted graphs. DFS (Depth-First Search) explores as deep as possible using a stack or recursion. BFS is better for shortest path problems; DFS is better for cycle detection, topological sort, and finding connected components. Both have O(V+E) time complexity.",
|
| 21 |
+
"what is memoization?": "Memoization is a top-down dynamic programming technique that caches results of expensive function calls and returns the cached result when the same inputs occur again. It uses recursion combined with a hash map or array to store computed values. For example, Fibonacci with memoization runs in O(n) instead of O(2^n). It trades space for time.",
|
| 22 |
+
"what is flex-grow in css?": "flex-grow is a CSS Flexbox property that defines how much a flex item grows relative to other items when extra space is available in the container. A value of 0 means the item will not grow. A value of 1 means the item takes equal share of available space. If one item has flex-grow: 2 and another has flex-grow: 1, the first gets twice as much extra space."
|
| 23 |
+
}
|
demo.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
import os
|
|
|
|
|
|
|
| 2 |
import streamlit as st
|
| 3 |
import streamlit.components.v1 as components
|
|
|
|
| 4 |
from system1_baseline import ask_baseline
|
| 5 |
from system2_rag import ask_rag, load_vectorstore, build_vectorstore
|
| 6 |
|
|
@@ -16,6 +19,7 @@ if "last_results" not in st.session_state: st.session_state.last_results
|
|
| 16 |
if "rating_submitted" not in st.session_state: st.session_state.rating_submitted = False
|
| 17 |
if "input_question" not in st.session_state: st.session_state.input_question = ""
|
| 18 |
if "auto_run" not in st.session_state: st.session_state.auto_run = False
|
|
|
|
| 19 |
|
| 20 |
# ── CSS ───────────────────────────────────────────────────────────────────────
|
| 21 |
st.markdown("""<style>
|
|
@@ -281,6 +285,20 @@ div[data-testid="stTextInput"] label {
|
|
| 281 |
.badge-grey { background: rgba(100,116,139,.1); color: #64748b; border: 1px solid rgba(100,116,139,.25); }
|
| 282 |
.metric-note { color: #334155; font-size: 11px; font-weight: 400; }
|
| 283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
/* ── Scrollbar ───────────────────────────────────────────────── */
|
| 285 |
::-webkit-scrollbar { width: 5px; height: 5px; }
|
| 286 |
::-webkit-scrollbar-track { background: #080d1a; }
|
|
@@ -320,6 +338,41 @@ def get_vectorstore():
|
|
| 320 |
|
| 321 |
vs = get_vectorstore()
|
| 322 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
# ── 3D Particle background ────────────────────────────────────────────────────
|
| 324 |
components.html("""
|
| 325 |
<script>
|
|
@@ -480,6 +533,11 @@ if (run_clicked or st.session_state.auto_run) and question:
|
|
| 480 |
"question": question,
|
| 481 |
"r1": r1, "r2": r2, "r3": r3,
|
| 482 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
|
| 484 |
# ── Render answers ────────────────────────────────────────────────────────────
|
| 485 |
if st.session_state.last_results:
|
|
@@ -495,6 +553,8 @@ if st.session_state.last_results:
|
|
| 495 |
|
| 496 |
col1, col2, col3 = st.columns(3)
|
| 497 |
|
|
|
|
|
|
|
| 498 |
with col1:
|
| 499 |
st.markdown(f"""
|
| 500 |
<div class="card card-blue">
|
|
@@ -509,6 +569,7 @@ if st.session_state.last_results:
|
|
| 509 |
<div class="card-answer">{r1['answer']}</div>
|
| 510 |
<div class="card-meta">
|
| 511 |
<span class="time-chip">⏱ {r1['response_time']}s</span>
|
|
|
|
| 512 |
</div>
|
| 513 |
</div>
|
| 514 |
</div>
|
|
@@ -532,6 +593,7 @@ if st.session_state.last_results:
|
|
| 532 |
</div>
|
| 533 |
<div class="card-meta">
|
| 534 |
<span class="time-chip">⏱ {r2['response_time']}s</span>
|
|
|
|
| 535 |
</div>
|
| 536 |
</div>
|
| 537 |
</div>
|
|
@@ -545,7 +607,7 @@ if st.session_state.last_results:
|
|
| 545 |
<span class="card-icon">🧠</span>
|
| 546 |
<div>
|
| 547 |
<div class="card-title">System 3 — Fine-Tuned Model</div>
|
| 548 |
-
<div class="card-subtitle">
|
| 549 |
</div>
|
| 550 |
</div>
|
| 551 |
<div class="card-body">
|
|
@@ -564,13 +626,14 @@ if st.session_state.last_results:
|
|
| 564 |
<span class="card-icon">🧠</span>
|
| 565 |
<div>
|
| 566 |
<div class="card-title">System 3 — Fine-Tuned Model</div>
|
| 567 |
-
<div class="card-subtitle">
|
| 568 |
</div>
|
| 569 |
</div>
|
| 570 |
<div class="card-body">
|
| 571 |
<div class="card-answer">{r3['answer']}</div>
|
| 572 |
<div class="card-meta">
|
| 573 |
<span class="time-chip">⏱ {r3['response_time']}s</span>
|
|
|
|
| 574 |
</div>
|
| 575 |
</div>
|
| 576 |
</div>
|
|
@@ -624,11 +687,11 @@ if st.session_state.last_results and not st.session_state.rating_submitted:
|
|
| 624 |
st.success(f"Rating saved! Total questions rated: {len(st.session_state.ratings)}")
|
| 625 |
st.rerun()
|
| 626 |
|
| 627 |
-
# ──
|
| 628 |
st.divider()
|
| 629 |
st.markdown("""
|
| 630 |
<div class="section-header">
|
| 631 |
-
<span class="section-header-text">
|
| 632 |
<div class="section-header-line"></div>
|
| 633 |
</div>
|
| 634 |
""", unsafe_allow_html=True)
|
|
@@ -668,6 +731,57 @@ else:
|
|
| 668 |
row_complete = (badge(f"{v_p1}/5", color_complete(v_p1)), badge(f"{v_p2}/5", color_complete(v_p2)), badge(f"{v_p3}/5", color_complete(v_p3)))
|
| 669 |
row_time = (badge(f"{v_t1}s", color_time(v_t1)), badge(f"{v_t2}s", color_time(v_t2)), badge(f"{v_t3}s", color_time(v_t3)))
|
| 670 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 671 |
if n == 0:
|
| 672 |
dash = '<span class="badge badge-grey">—</span>'
|
| 673 |
row_correct = row_halluc = row_complete = row_time = (dash, dash, dash)
|
|
@@ -722,5 +836,7 @@ with st.expander("About CodeSage"):
|
|
| 722 |
|--------|-------------|
|
| 723 |
| Baseline | Llama 3.1 8B via Groq · system prompt only |
|
| 724 |
| RAG | FAISS vector store + HuggingFace embeddings + Llama 3.1 8B via Groq |
|
| 725 |
-
| Fine-tuned |
|
|
|
|
|
|
|
| 726 |
""")
|
|
|
|
| 1 |
import os
|
| 2 |
+
import json
|
| 3 |
+
import numpy as np
|
| 4 |
import streamlit as st
|
| 5 |
import streamlit.components.v1 as components
|
| 6 |
+
import plotly.graph_objects as go
|
| 7 |
from system1_baseline import ask_baseline
|
| 8 |
from system2_rag import ask_rag, load_vectorstore, build_vectorstore
|
| 9 |
|
|
|
|
| 19 |
if "rating_submitted" not in st.session_state: st.session_state.rating_submitted = False
|
| 20 |
if "input_question" not in st.session_state: st.session_state.input_question = ""
|
| 21 |
if "auto_run" not in st.session_state: st.session_state.auto_run = False
|
| 22 |
+
if "auto_metrics" not in st.session_state: st.session_state.auto_metrics = {}
|
| 23 |
|
| 24 |
# ── CSS ───────────────────────────────────────────────────────────────────────
|
| 25 |
st.markdown("""<style>
|
|
|
|
| 285 |
.badge-grey { background: rgba(100,116,139,.1); color: #64748b; border: 1px solid rgba(100,116,139,.25); }
|
| 286 |
.metric-note { color: #334155; font-size: 11px; font-weight: 400; }
|
| 287 |
|
| 288 |
+
/* ── Auto-metric chips ───────────────────────────────────────── */
|
| 289 |
+
.rouge-chip { font-size:11px; font-weight:600; padding:3px 9px; border-radius:99px;
|
| 290 |
+
background:rgba(251,191,36,.08); color:#fbbf24; border:1px solid rgba(251,191,36,.2); }
|
| 291 |
+
.sim-chip { font-size:11px; font-weight:600; padding:3px 9px; border-radius:99px;
|
| 292 |
+
background:rgba(244,114,182,.08); color:#f472b6; border:1px solid rgba(244,114,182,.2); }
|
| 293 |
+
|
| 294 |
+
/* ── Tabs ────────────────────────────────────────────────────── */
|
| 295 |
+
div[data-testid="stTabs"] button[data-baseweb="tab"] {
|
| 296 |
+
font-weight: 600; font-size: 13px; color: #475569;
|
| 297 |
+
}
|
| 298 |
+
div[data-testid="stTabs"] button[data-baseweb="tab"][aria-selected="true"] {
|
| 299 |
+
color: #e2e8f0;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
/* ── Scrollbar ───────────────────────────────────────────────── */
|
| 303 |
::-webkit-scrollbar { width: 5px; height: 5px; }
|
| 304 |
::-webkit-scrollbar-track { background: #080d1a; }
|
|
|
|
| 338 |
|
| 339 |
vs = get_vectorstore()
|
| 340 |
|
| 341 |
+
# ── Reference answers + auto-metrics ─────────────────────────────────────────
|
| 342 |
+
@st.cache_data
|
| 343 |
+
def load_reference_answers():
|
| 344 |
+
path = "data/reference_answers.json"
|
| 345 |
+
if os.path.exists(path):
|
| 346 |
+
with open(path, encoding="utf-8") as f:
|
| 347 |
+
return json.load(f)
|
| 348 |
+
return {}
|
| 349 |
+
|
| 350 |
+
ref_answers = load_reference_answers()
|
| 351 |
+
|
| 352 |
+
def compute_auto_metrics(answer: str, question: str) -> dict:
|
| 353 |
+
ref = ref_answers.get(question.strip().lower())
|
| 354 |
+
if not ref or not answer:
|
| 355 |
+
return {}
|
| 356 |
+
try:
|
| 357 |
+
from rouge_score import rouge_scorer as rs
|
| 358 |
+
scorer = rs.RougeScorer(["rougeL"], use_stemmer=True)
|
| 359 |
+
rouge_l = round(scorer.score(ref, answer)["rougeL"].fmeasure, 3)
|
| 360 |
+
r_emb = np.array(vs.embeddings.embed_query(ref))
|
| 361 |
+
a_emb = np.array(vs.embeddings.embed_query(answer))
|
| 362 |
+
norm = np.linalg.norm(r_emb) * np.linalg.norm(a_emb)
|
| 363 |
+
sem_sim = round(float(np.dot(r_emb, a_emb) / (norm + 1e-8)), 3)
|
| 364 |
+
return {"rouge_l": rouge_l, "sem_sim": max(0.0, sem_sim)}
|
| 365 |
+
except Exception:
|
| 366 |
+
return {}
|
| 367 |
+
|
| 368 |
+
def metric_chips(m: dict) -> str:
|
| 369 |
+
if not m:
|
| 370 |
+
return ""
|
| 371 |
+
return (
|
| 372 |
+
f'<span class="rouge-chip">ROUGE-L {m["rouge_l"]:.3f}</span>'
|
| 373 |
+
f'<span class="sim-chip">Sem.Sim {m["sem_sim"]:.3f}</span>'
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
# ── 3D Particle background ────────────────────────────────────────────────────
|
| 377 |
components.html("""
|
| 378 |
<script>
|
|
|
|
| 533 |
"question": question,
|
| 534 |
"r1": r1, "r2": r2, "r3": r3,
|
| 535 |
}
|
| 536 |
+
st.session_state.auto_metrics = {
|
| 537 |
+
"r1": compute_auto_metrics(r1["answer"], question),
|
| 538 |
+
"r2": compute_auto_metrics(r2["answer"], question),
|
| 539 |
+
"r3": compute_auto_metrics(r3["answer"], question),
|
| 540 |
+
}
|
| 541 |
|
| 542 |
# ── Render answers ────────────────────────────────────────────────────────────
|
| 543 |
if st.session_state.last_results:
|
|
|
|
| 553 |
|
| 554 |
col1, col2, col3 = st.columns(3)
|
| 555 |
|
| 556 |
+
m = st.session_state.auto_metrics
|
| 557 |
+
|
| 558 |
with col1:
|
| 559 |
st.markdown(f"""
|
| 560 |
<div class="card card-blue">
|
|
|
|
| 569 |
<div class="card-answer">{r1['answer']}</div>
|
| 570 |
<div class="card-meta">
|
| 571 |
<span class="time-chip">⏱ {r1['response_time']}s</span>
|
| 572 |
+
{metric_chips(m.get('r1', {}))}
|
| 573 |
</div>
|
| 574 |
</div>
|
| 575 |
</div>
|
|
|
|
| 593 |
</div>
|
| 594 |
<div class="card-meta">
|
| 595 |
<span class="time-chip">⏱ {r2['response_time']}s</span>
|
| 596 |
+
{metric_chips(m.get('r2', {}))}
|
| 597 |
</div>
|
| 598 |
</div>
|
| 599 |
</div>
|
|
|
|
| 607 |
<span class="card-icon">🧠</span>
|
| 608 |
<div>
|
| 609 |
<div class="card-title">System 3 — Fine-Tuned Model</div>
|
| 610 |
+
<div class="card-subtitle">Qwen2.5-1.5B · LoRA fine-tuning</div>
|
| 611 |
</div>
|
| 612 |
</div>
|
| 613 |
<div class="card-body">
|
|
|
|
| 626 |
<span class="card-icon">🧠</span>
|
| 627 |
<div>
|
| 628 |
<div class="card-title">System 3 — Fine-Tuned Model</div>
|
| 629 |
+
<div class="card-subtitle">Qwen2.5-1.5B · LoRA on programming Q&A</div>
|
| 630 |
</div>
|
| 631 |
</div>
|
| 632 |
<div class="card-body">
|
| 633 |
<div class="card-answer">{r3['answer']}</div>
|
| 634 |
<div class="card-meta">
|
| 635 |
<span class="time-chip">⏱ {r3['response_time']}s</span>
|
| 636 |
+
{metric_chips(m.get('r3', {}))}
|
| 637 |
</div>
|
| 638 |
</div>
|
| 639 |
</div>
|
|
|
|
| 687 |
st.success(f"Rating saved! Total questions rated: {len(st.session_state.ratings)}")
|
| 688 |
st.rerun()
|
| 689 |
|
| 690 |
+
# ── Analytics tab ─────────────────────────────────────────────────────────────
|
| 691 |
st.divider()
|
| 692 |
st.markdown("""
|
| 693 |
<div class="section-header">
|
| 694 |
+
<span class="section-header-text">📊 Analytics & Evaluation</span>
|
| 695 |
<div class="section-header-line"></div>
|
| 696 |
</div>
|
| 697 |
""", unsafe_allow_html=True)
|
|
|
|
| 731 |
row_complete = (badge(f"{v_p1}/5", color_complete(v_p1)), badge(f"{v_p2}/5", color_complete(v_p2)), badge(f"{v_p3}/5", color_complete(v_p3)))
|
| 732 |
row_time = (badge(f"{v_t1}s", color_time(v_t1)), badge(f"{v_t2}s", color_time(v_t2)), badge(f"{v_t3}s", color_time(v_t3)))
|
| 733 |
|
| 734 |
+
# ── Plotly charts ──────────────────────────────────────────────────────────
|
| 735 |
+
_systems = ["Baseline LLM", "RAG Chatbot", "Fine-Tuned"]
|
| 736 |
+
_colors = ["#3b82f6", "#10b981", "#8b5cf6"]
|
| 737 |
+
_layout = dict(
|
| 738 |
+
paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
|
| 739 |
+
font=dict(color="#94a3b8", size=12),
|
| 740 |
+
legend=dict(bgcolor="rgba(0,0,0,0)", bordercolor="#1e293b", borderwidth=1),
|
| 741 |
+
xaxis=dict(gridcolor="#1a2540", linecolor="#1a2540"),
|
| 742 |
+
yaxis=dict(gridcolor="#1a2540", linecolor="#1a2540"),
|
| 743 |
+
margin=dict(l=20, r=20, t=48, b=20),
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
ch1, ch2 = st.columns(2)
|
| 747 |
+
|
| 748 |
+
with ch1:
|
| 749 |
+
fig1 = go.Figure()
|
| 750 |
+
fig1.add_trace(go.Bar(
|
| 751 |
+
name="Correctness", x=_systems, y=[v_c1, v_c2, v_c3],
|
| 752 |
+
marker_color=_colors, opacity=1.0,
|
| 753 |
+
text=[f"{v}/5" for v in [v_c1, v_c2, v_c3]], textposition="auto",
|
| 754 |
+
))
|
| 755 |
+
fig1.add_trace(go.Bar(
|
| 756 |
+
name="Completeness", x=_systems, y=[v_p1, v_p2, v_p3],
|
| 757 |
+
marker_color=_colors, opacity=0.55,
|
| 758 |
+
text=[f"{v}/5" for v in [v_p1, v_p2, v_p3]], textposition="auto",
|
| 759 |
+
))
|
| 760 |
+
fig1.update_layout(
|
| 761 |
+
**_layout, barmode="group", height=300,
|
| 762 |
+
title=dict(text="Quality Scores (out of 5)", font=dict(color="#e2e8f0", size=14)),
|
| 763 |
+
yaxis=dict(range=[0, 5], gridcolor="#1a2540", linecolor="#1a2540"),
|
| 764 |
+
)
|
| 765 |
+
st.plotly_chart(fig1, use_container_width=True)
|
| 766 |
+
|
| 767 |
+
with ch2:
|
| 768 |
+
fig2 = go.Figure()
|
| 769 |
+
fig2.add_trace(go.Bar(
|
| 770 |
+
name="Response Time", x=_systems, y=[v_t1, v_t2, v_t3],
|
| 771 |
+
marker_color=_colors,
|
| 772 |
+
text=[f"{v}s" for v in [v_t1, v_t2, v_t3]], textposition="auto",
|
| 773 |
+
))
|
| 774 |
+
fig2.add_trace(go.Bar(
|
| 775 |
+
name="Hallucination %", x=_systems, y=[v_h1, v_h2, v_h3],
|
| 776 |
+
marker_color=_colors, opacity=0.55,
|
| 777 |
+
text=[f"{v}%" for v in [v_h1, v_h2, v_h3]], textposition="auto",
|
| 778 |
+
))
|
| 779 |
+
fig2.update_layout(
|
| 780 |
+
**_layout, barmode="group", height=300,
|
| 781 |
+
title=dict(text="Response Time (s) & Hallucination (%)", font=dict(color="#e2e8f0", size=14)),
|
| 782 |
+
)
|
| 783 |
+
st.plotly_chart(fig2, use_container_width=True)
|
| 784 |
+
|
| 785 |
if n == 0:
|
| 786 |
dash = '<span class="badge badge-grey">—</span>'
|
| 787 |
row_correct = row_halluc = row_complete = row_time = (dash, dash, dash)
|
|
|
|
| 836 |
|--------|-------------|
|
| 837 |
| Baseline | Llama 3.1 8B via Groq · system prompt only |
|
| 838 |
| RAG | FAISS vector store + HuggingFace embeddings + Llama 3.1 8B via Groq |
|
| 839 |
+
| Fine-tuned | Qwen2.5-1.5B-Instruct fine-tuned via LoRA on 20 programming Q&A pairs |
|
| 840 |
+
|
| 841 |
+
Drop any PDF into `data/pdfs/` and delete `data/faiss_index/` to rebuild the RAG knowledge base with it included.
|
| 842 |
""")
|
requirements.txt
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
openai>=1.0.0
|
| 2 |
langchain>=0.2.0
|
| 3 |
langchain-community>=0.2.0
|
|
|
|
| 4 |
faiss-cpu>=1.7.4
|
| 5 |
sentence-transformers>=2.7.0
|
| 6 |
transformers>=4.40.0
|
|
@@ -11,3 +12,7 @@ streamlit>=1.35.0
|
|
| 11 |
pandas>=2.0.0
|
| 12 |
python-dotenv>=1.0.0
|
| 13 |
tqdm>=4.66.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
openai>=1.0.0
|
| 2 |
langchain>=0.2.0
|
| 3 |
langchain-community>=0.2.0
|
| 4 |
+
langchain-huggingface>=0.0.3
|
| 5 |
faiss-cpu>=1.7.4
|
| 6 |
sentence-transformers>=2.7.0
|
| 7 |
transformers>=4.40.0
|
|
|
|
| 12 |
pandas>=2.0.0
|
| 13 |
python-dotenv>=1.0.0
|
| 14 |
tqdm>=4.66.0
|
| 15 |
+
plotly>=5.0.0
|
| 16 |
+
rouge-score>=0.1.2
|
| 17 |
+
pypdf>=3.0.0
|
| 18 |
+
numpy>=1.24.0
|
system2_rag.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
import os
|
|
|
|
| 2 |
import time
|
| 3 |
from dotenv import load_dotenv
|
| 4 |
-
from langchain_community.document_loaders import DirectoryLoader, TextLoader
|
| 5 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 6 |
from langchain_community.vectorstores import FAISS
|
| 7 |
from langchain_huggingface import HuggingFaceEmbeddings
|
|
@@ -16,7 +17,8 @@ client = OpenAI(
|
|
| 16 |
|
| 17 |
MODEL = "llama-3.1-8b-instant"
|
| 18 |
|
| 19 |
-
DOCS_PATH
|
|
|
|
| 20 |
INDEX_PATH = "data/faiss_index"
|
| 21 |
EMBED_MODEL = "all-MiniLM-L6-v2"
|
| 22 |
|
|
@@ -27,11 +29,24 @@ SYSTEM_PROMPT = (
|
|
| 27 |
|
| 28 |
|
| 29 |
def build_vectorstore() -> FAISS:
|
| 30 |
-
|
|
|
|
| 31 |
loader = DirectoryLoader(DOCS_PATH, glob="**/*.txt", loader_cls=TextLoader,
|
| 32 |
loader_kwargs={"encoding": "utf-8"})
|
| 33 |
documents = loader.load()
|
| 34 |
-
print(f"Loaded {len(documents)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=60)
|
| 37 |
chunks = splitter.split_documents(documents)
|
|
@@ -52,7 +67,6 @@ def load_vectorstore() -> FAISS:
|
|
| 52 |
def ask_rag(question: str, vectorstore: FAISS) -> dict:
|
| 53 |
start = time.time()
|
| 54 |
|
| 55 |
-
# Retrieve top-3 relevant chunks
|
| 56 |
docs = vectorstore.similarity_search(question, k=3)
|
| 57 |
context = "\n\n".join([doc.page_content for doc in docs])
|
| 58 |
|
|
|
|
| 1 |
import os
|
| 2 |
+
import glob as glob_module
|
| 3 |
import time
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
+
from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader
|
| 6 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 7 |
from langchain_community.vectorstores import FAISS
|
| 8 |
from langchain_huggingface import HuggingFaceEmbeddings
|
|
|
|
| 17 |
|
| 18 |
MODEL = "llama-3.1-8b-instant"
|
| 19 |
|
| 20 |
+
DOCS_PATH = "data/docs"
|
| 21 |
+
PDFS_PATH = "data/pdfs"
|
| 22 |
INDEX_PATH = "data/faiss_index"
|
| 23 |
EMBED_MODEL = "all-MiniLM-L6-v2"
|
| 24 |
|
|
|
|
| 29 |
|
| 30 |
|
| 31 |
def build_vectorstore() -> FAISS:
|
| 32 |
+
# Load .txt documents
|
| 33 |
+
print("Loading text documents...")
|
| 34 |
loader = DirectoryLoader(DOCS_PATH, glob="**/*.txt", loader_cls=TextLoader,
|
| 35 |
loader_kwargs={"encoding": "utf-8"})
|
| 36 |
documents = loader.load()
|
| 37 |
+
print(f" Loaded {len(documents)} text files.")
|
| 38 |
+
|
| 39 |
+
# Load PDFs from data/pdfs/ if any exist
|
| 40 |
+
pdf_files = glob_module.glob(os.path.join(PDFS_PATH, "**/*.pdf"), recursive=True)
|
| 41 |
+
for pdf_path in pdf_files:
|
| 42 |
+
try:
|
| 43 |
+
pdf_loader = PyPDFLoader(pdf_path)
|
| 44 |
+
documents.extend(pdf_loader.load())
|
| 45 |
+
print(f" Loaded PDF: {os.path.basename(pdf_path)}")
|
| 46 |
+
except Exception as e:
|
| 47 |
+
print(f" Warning: Could not load {pdf_path}: {e}")
|
| 48 |
+
|
| 49 |
+
print(f"Total documents loaded: {len(documents)} ({len(pdf_files)} PDFs)")
|
| 50 |
|
| 51 |
splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=60)
|
| 52 |
chunks = splitter.split_documents(documents)
|
|
|
|
| 67 |
def ask_rag(question: str, vectorstore: FAISS) -> dict:
|
| 68 |
start = time.time()
|
| 69 |
|
|
|
|
| 70 |
docs = vectorstore.similarity_search(question, k=3)
|
| 71 |
context = "\n\n".join([doc.page_content for doc in docs])
|
| 72 |
|
system3_finetune_colab.ipynb
CHANGED
|
@@ -35,25 +35,7 @@
|
|
| 35 |
"metadata": {},
|
| 36 |
"outputs": [],
|
| 37 |
"source": [
|
| 38 |
-
"import json\n"
|
| 39 |
-
"import torch\n",
|
| 40 |
-
"from datasets import Dataset\n",
|
| 41 |
-
"from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling\n",
|
| 42 |
-
"from peft import LoraConfig, get_peft_model, TaskType\n",
|
| 43 |
-
"\n",
|
| 44 |
-
"# Load dataset\n",
|
| 45 |
-
"data = []\n",
|
| 46 |
-
"with open('finetune_data.jsonl') as f:\n",
|
| 47 |
-
" for line in f:\n",
|
| 48 |
-
" data.append(json.loads(line))\n",
|
| 49 |
-
"\n",
|
| 50 |
-
"print(f'Loaded {len(data)} training examples')\n",
|
| 51 |
-
"\n",
|
| 52 |
-
"def format_prompt(example):\n",
|
| 53 |
-
" return f\"### Instruction:\\n{example['instruction']}\\n\\n### Response:\\n{example['output']}\"\n",
|
| 54 |
-
"\n",
|
| 55 |
-
"dataset = Dataset.from_list([{'text': format_prompt(d)} for d in data])\n",
|
| 56 |
-
"print(dataset[0]['text'][:200])"
|
| 57 |
]
|
| 58 |
},
|
| 59 |
{
|
|
@@ -62,17 +44,7 @@
|
|
| 62 |
"metadata": {},
|
| 63 |
"outputs": [],
|
| 64 |
"source": [
|
| 65 |
-
"MODEL_NAME = '
|
| 66 |
-
"\n",
|
| 67 |
-
"tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
|
| 68 |
-
"tokenizer.pad_token = tokenizer.eos_token\n",
|
| 69 |
-
"\n",
|
| 70 |
-
"model = AutoModelForCausalLM.from_pretrained(\n",
|
| 71 |
-
" MODEL_NAME,\n",
|
| 72 |
-
" torch_dtype=torch.float16,\n",
|
| 73 |
-
" device_map='auto'\n",
|
| 74 |
-
")\n",
|
| 75 |
-
"print('Model loaded.')"
|
| 76 |
]
|
| 77 |
},
|
| 78 |
{
|
|
@@ -133,17 +105,7 @@
|
|
| 133 |
"metadata": {},
|
| 134 |
"outputs": [],
|
| 135 |
"source": [
|
| 136 |
-
"# Test the fine-tuned model\n
|
| 137 |
-
"def ask_finetuned(question):\n",
|
| 138 |
-
" prompt = f'### Instruction:\\n{question}\\n\\n### Response:\\n'\n",
|
| 139 |
-
" inputs = tokenizer(prompt, return_tensors='pt').to(model.device)\n",
|
| 140 |
-
" with torch.no_grad():\n",
|
| 141 |
-
" outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.7,\n",
|
| 142 |
-
" do_sample=True, pad_token_id=tokenizer.eos_token_id)\n",
|
| 143 |
-
" full = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
|
| 144 |
-
" return full.split('### Response:\\n')[-1].strip()\n",
|
| 145 |
-
"\n",
|
| 146 |
-
"print(ask_finetuned('What is binary search?'))"
|
| 147 |
]
|
| 148 |
},
|
| 149 |
{
|
|
@@ -173,4 +135,4 @@
|
|
| 173 |
},
|
| 174 |
"nbformat": 4,
|
| 175 |
"nbformat_minor": 5
|
| 176 |
-
}
|
|
|
|
| 35 |
"metadata": {},
|
| 36 |
"outputs": [],
|
| 37 |
"source": [
|
| 38 |
+
"import json\nimport torch\nfrom datasets import Dataset\nfrom transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling\nfrom peft import LoraConfig, get_peft_model, TaskType\n\n# Load dataset\ndata = []\nwith open('finetune_data.jsonl') as f:\n for line in f:\n data.append(json.loads(line))\n\nprint(f'Loaded {len(data)} training examples')\n\ndef format_prompt(example):\n return f\"### Instruction:\\n{example['instruction']}\\n\\n### Response:\\n{example['output']}\"\n\ndataset = Dataset.from_list([{'text': format_prompt(d)} for d in data])\nprint(dataset[0]['text'][:200])"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
]
|
| 40 |
},
|
| 41 |
{
|
|
|
|
| 44 |
"metadata": {},
|
| 45 |
"outputs": [],
|
| 46 |
"source": [
|
| 47 |
+
"MODEL_NAME = 'Qwen/Qwen2.5-1.5B-Instruct'\n\ntokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\ntokenizer.pad_token = tokenizer.eos_token\n\nmodel = AutoModelForCausalLM.from_pretrained(\n MODEL_NAME,\n torch_dtype=torch.float16,\n device_map='auto'\n)\nprint('Model loaded.')"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
]
|
| 49 |
},
|
| 50 |
{
|
|
|
|
| 105 |
"metadata": {},
|
| 106 |
"outputs": [],
|
| 107 |
"source": [
|
| 108 |
+
"# Test the fine-tuned model\ndef ask_finetuned(question):\n messages = [\n {'role': 'system', 'content': 'You are a programming tutor specializing in Data Structures, Algorithms, and Web Development.'},\n {'role': 'user', 'content': question}\n ]\n text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)\n inputs = tokenizer([text], return_tensors='pt').to(model.device)\n with torch.no_grad():\n outputs = model.generate(**inputs, max_new_tokens=200, do_sample=False,\n pad_token_id=tokenizer.eos_token_id)\n generated_ids = outputs[0][inputs.input_ids.shape[1]:]\n return tokenizer.decode(generated_ids, skip_special_tokens=True).strip()\n\nprint(ask_finetuned('What is binary search?'))"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
]
|
| 110 |
},
|
| 111 |
{
|
|
|
|
| 135 |
},
|
| 136 |
"nbformat": 4,
|
| 137 |
"nbformat_minor": 5
|
| 138 |
+
}
|
system3_inference.py
CHANGED
|
@@ -4,6 +4,8 @@ After running system3_finetune_colab.ipynb on Google Colab:
|
|
| 4 |
1. Download finetuned_model.zip from Colab
|
| 5 |
2. Unzip into this project root as finetuned_model/
|
| 6 |
3. Then run this file
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import time
|
|
@@ -11,7 +13,7 @@ import torch
|
|
| 11 |
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 12 |
from peft import PeftModel
|
| 13 |
|
| 14 |
-
BASE_MODEL = "
|
| 15 |
FINETUNED_PATH = "./finetuned_model"
|
| 16 |
|
| 17 |
_model = None
|
|
@@ -25,7 +27,10 @@ def load_model():
|
|
| 25 |
|
| 26 |
print("Loading fine-tuned model (this takes ~30s first time)...")
|
| 27 |
_tokenizer = AutoTokenizer.from_pretrained(FINETUNED_PATH)
|
| 28 |
-
base = AutoModelForCausalLM.from_pretrained(
|
|
|
|
|
|
|
|
|
|
| 29 |
_model = PeftModel.from_pretrained(base, FINETUNED_PATH)
|
| 30 |
_model.eval()
|
| 31 |
print("Fine-tuned model ready.")
|
|
@@ -44,19 +49,32 @@ def ask_finetuned(question: str) -> dict:
|
|
| 44 |
model, tokenizer = load_model()
|
| 45 |
start = time.time()
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
with torch.no_grad():
|
| 51 |
outputs = model.generate(
|
| 52 |
**inputs,
|
| 53 |
-
max_new_tokens=
|
| 54 |
do_sample=False,
|
| 55 |
pad_token_id=tokenizer.eos_token_id,
|
| 56 |
)
|
| 57 |
|
| 58 |
-
|
| 59 |
-
answer =
|
| 60 |
elapsed = round(time.time() - start, 2)
|
| 61 |
|
| 62 |
return {
|
|
|
|
| 4 |
1. Download finetuned_model.zip from Colab
|
| 5 |
2. Unzip into this project root as finetuned_model/
|
| 6 |
3. Then run this file
|
| 7 |
+
|
| 8 |
+
Base model: Qwen/Qwen2.5-1.5B-Instruct (upgraded from TinyLlama)
|
| 9 |
"""
|
| 10 |
import os
|
| 11 |
import time
|
|
|
|
| 13 |
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 14 |
from peft import PeftModel
|
| 15 |
|
| 16 |
+
BASE_MODEL = "Qwen/Qwen2.5-1.5B-Instruct"
|
| 17 |
FINETUNED_PATH = "./finetuned_model"
|
| 18 |
|
| 19 |
_model = None
|
|
|
|
| 27 |
|
| 28 |
print("Loading fine-tuned model (this takes ~30s first time)...")
|
| 29 |
_tokenizer = AutoTokenizer.from_pretrained(FINETUNED_PATH)
|
| 30 |
+
base = AutoModelForCausalLM.from_pretrained(
|
| 31 |
+
BASE_MODEL,
|
| 32 |
+
torch_dtype=torch.float32,
|
| 33 |
+
)
|
| 34 |
_model = PeftModel.from_pretrained(base, FINETUNED_PATH)
|
| 35 |
_model.eval()
|
| 36 |
print("Fine-tuned model ready.")
|
|
|
|
| 49 |
model, tokenizer = load_model()
|
| 50 |
start = time.time()
|
| 51 |
|
| 52 |
+
messages = [
|
| 53 |
+
{
|
| 54 |
+
"role": "system",
|
| 55 |
+
"content": (
|
| 56 |
+
"You are a programming tutor specializing in Data Structures, "
|
| 57 |
+
"Algorithms, and Web Development. Answer questions clearly and concisely."
|
| 58 |
+
),
|
| 59 |
+
},
|
| 60 |
+
{"role": "user", "content": question},
|
| 61 |
+
]
|
| 62 |
+
|
| 63 |
+
text = tokenizer.apply_chat_template(
|
| 64 |
+
messages, tokenize=False, add_generation_prompt=True
|
| 65 |
+
)
|
| 66 |
+
inputs = tokenizer([text], return_tensors="pt")
|
| 67 |
|
| 68 |
with torch.no_grad():
|
| 69 |
outputs = model.generate(
|
| 70 |
**inputs,
|
| 71 |
+
max_new_tokens=150,
|
| 72 |
do_sample=False,
|
| 73 |
pad_token_id=tokenizer.eos_token_id,
|
| 74 |
)
|
| 75 |
|
| 76 |
+
generated_ids = outputs[0][inputs.input_ids.shape[1]:]
|
| 77 |
+
answer = tokenizer.decode(generated_ids, skip_special_tokens=True).strip()
|
| 78 |
elapsed = round(time.time() - start, 2)
|
| 79 |
|
| 80 |
return {
|