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 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. You see each answer, the RAG system's retrieved context, and response times.
28
  4. You rate each answer (correctness, completeness, hallucination).
29
- 5. A live metrics table aggregates your ratings across all questions.
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 TinyLlama 1.1B
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. Represents what an off-the-shelf LLM can do.
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 17 hand-written `.txt` documents covering DSA topics and web dev concepts.
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
- **TinyLlama 1.1B** fine-tuned with **LoRA (Low-Rank Adaptation)** on ~20 curated programming Q&A pairs. Training is done on Google Colab (free GPU). The resulting adapter is loaded locally via the `peft` library for inference.
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 | TinyLlama/TinyLlama-1.1B-Chat-v1.0 |
 
 
77
  | Training platform | Google Colab |
78
- | Background animation | HTML5 Canvas (3D particle network via `components.html`) |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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) Load the fine-tuned model
 
 
 
105
  1. Open `system3_finetune_colab.ipynb` in Google Colab.
106
- 2. Run all cells — training takes ~10 min on a free T4 GPU.
107
  3. Download `finetuned_model.zip` and unzip it in the project root.
108
- 4. Restart the Streamlit app — System 3 will now return real answers.
109
 
110
  ---
111
 
112
  ## Knowledge Base
113
 
114
- The RAG system retrieves from 17 topic documents in `data/docs/`:
 
 
115
 
116
- `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`
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/ # 17 knowledge base documents
 
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">TinyLlama · LoRA fine-tuning</div>
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">TinyLlama 1.1B · LoRA on programming Q&A</div>
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
- # ── Metrics table ──────────────────────────────────────────────────────────────
628
  st.divider()
629
  st.markdown("""
630
  <div class="section-header">
631
- <span class="section-header-text">Evaluation metrics</span>
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 | TinyLlama 1.1B fine-tuned via LoRA on 20 programming Q&A pairs |
 
 
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 &amp; 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) &amp; 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 = "data/docs"
 
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
- print("Loading documents...")
 
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)} 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 = 'TinyLlama/TinyLlama-1.1B-Chat-v1.0'\n",
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 = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
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(BASE_MODEL, torch_dtype=torch.float32)
 
 
 
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
- prompt = f"### Instruction:\n{question}\n\n### Response:\n"
48
- inputs = tokenizer(prompt, return_tensors="pt")
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  with torch.no_grad():
51
  outputs = model.generate(
52
  **inputs,
53
- max_new_tokens=100,
54
  do_sample=False,
55
  pad_token_id=tokenizer.eos_token_id,
56
  )
57
 
58
- full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
59
- answer = full_output.split("### Response:\n")[-1].strip()
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 {