{ "cells": [ { "cell_type": "markdown", "id": "fa1df8eb-be20-421a-9348-14f3db7d2c4a", "metadata": {}, "source": [ "# CELL 1 — INSTALL DEPENDENCIES" ] }, { "cell_type": "code", "execution_count": 1, "id": "6f0b2d99-5561-460d-9a93-c8c566db8c80", "metadata": {}, "outputs": [], "source": [ "# ============================================================\n", "# CELL 1 — INSTALL DEPENDENCIES\n", "# This cell installs all the libraries required for:\n", "# - Web crawling\n", "# - Text cleaning\n", "# - Embeddings & vector DB (Chroma)\n", "# - RAG pipeline\n", "# - QLoRA training\n", "# - Modal integration\n", "# ============================================================\n", "\n", "# NOTE: These installs should be run once per environment.\n", "# For Modal, the training job will also specify dependencies.\n", "\n", "!pip install -q requests beautifulsoup4 trafilatura\n", "!pip install -q langchain langchain-community langchain-text-splitters\n", "!pip install -q chromadb sentence-transformers\n", "!pip install -q bitsandbytes peft transformers accelerate\n", "!pip install -q pdfplumber\n", "!pip install -q modal\n" ] }, { "cell_type": "markdown", "id": "d2f30b14-5712-4150-8dd3-98be8a9cb976", "metadata": {}, "source": [ "# CELL 2 — ENVIRONMENT CHECK & IMPORTS" ] }, { "cell_type": "code", "execution_count": 2, "id": "c7292b86-b0ff-45e6-a60b-ab78c8535443", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package punkt to\n", "[nltk_data] /Users/nicholasadan/nltk_data...\n", "[nltk_data] Package punkt is already up-to-date!\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Imports successful. Working directory: /Users/nicholasadan/Canada visa buddy\n" ] } ], "source": [ "# ============================================================\n", "# CELL 2 — ENVIRONMENT CHECK & IMPORTS\n", "# - Purpose: import all libraries used later and explain why.\n", "# - Run this cell first to ensure packages are available.\n", "# ============================================================\n", "\n", "# Standard library\n", "import os # file operations, folder creation\n", "import json # read/write metadata and dataset files\n", "import time # polite crawling rate limiting\n", "import hashlib # stable filename hashing for saved HTML\n", "from pathlib import Path # convenient path handling\n", "\n", "# HTTP + parsing libraries\n", "import requests # download HTML and PDFs\n", "from bs4 import BeautifulSoup # parse HTML structure when needed\n", "import trafilatura # extract readable text from complex pages (better for gov sites)\n", "\n", "# PDF handling\n", "import pdfplumber # reliable PDF text extraction for many PDFs\n", "\n", "# Text processing and chunking\n", "import nltk # sentence tokenization\n", "nltk.download(\"punkt\") # ensure punkt tokenizer is available\n", "import re # regex for cleaning text\n", "\n", "# Embeddings and vector store (Chroma)\n", "# - chromadb: vector DB that stores embeddings and metadata locally or in-memory\n", "# - sentence-transformers: local embedding models, good for offline embeddings\n", "import chromadb # Chroma vector DB\n", "from chromadb.utils import embedding_functions\n", "from sentence_transformers import SentenceTransformer # local embedding model\n", "\n", "# Model fine-tuning & QLoRA utilities\n", "# - These will be used inside Modal training environment (cell later)\n", "import torch\n", "from transformers import AutoTokenizer # tokenizer used during training and inference\n", "# PEFT, bitsandbytes, accelerate etc. will be imported inside the Modal training cell\n", "# because they require GPU/CUDA environment\n", "\n", "# Simple logging helper\n", "print(\"Imports successful. Working directory:\", os.getcwd())\n" ] }, { "cell_type": "markdown", "id": "e726bf4e-4dac-48ad-807c-ec0680447867", "metadata": {}, "source": [ "# CELL 3 PATHS & FOLDER STRUCTURE" ] }, { "cell_type": "code", "execution_count": 35, "id": "55080658-61bd-4d9e-aca5-5c9afa97f0af", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Folders created / verified:\n", " - ./visa_buddy_data/raw_html\n", " - ./visa_buddy_data/raw_pdfs\n", " - ./visa_buddy_data/clean_text\n", " - ./visa_buddy_data/chunks\n", " - ./visa_buddy_data/metadata\n", " - ./visa_buddy_data/models\n" ] } ], "source": [ "# ============================================================\n", "# CELL 3 — PATHS & FOLDER STRUCTURE\n", "# - Purpose: ensure a consistent file layout. Mirror Modal volume.\n", "# ============================================================\n", "\n", "# Base path inside the process (Modal will mount /data to your volume; locally you can change this)\n", "BASE_DATA_DIR = \"./visa_buddy_data\" # Modal volume path — ensure this matches your Modal volume mount\n", "# If running locally, you may change BASE_DATA_DIR = \"./data\"\n", "\n", "# Derived folders\n", "RAW_HTML_DIR = os.path.join(BASE_DATA_DIR, \"raw_html\")\n", "RAW_PDF_DIR = os.path.join(BASE_DATA_DIR, \"raw_pdfs\")\n", "CLEAN_TEXT_DIR = os.path.join(BASE_DATA_DIR, \"clean_text\")\n", "CHUNKS_DIR = os.path.join(BASE_DATA_DIR, \"chunks\")\n", "METADATA_DIR = os.path.join(BASE_DATA_DIR, \"metadata\")\n", "MODELS_DIR = os.path.join(BASE_DATA_DIR, \"models\") # where training saves adapters and checkpoints\n", "\n", "# Create directories (idempotent)\n", "for d in [RAW_HTML_DIR, RAW_PDF_DIR, CLEAN_TEXT_DIR, CHUNKS_DIR, METADATA_DIR, MODELS_DIR]:\n", " os.makedirs(d, exist_ok=True)\n", "\n", "# Quick check\n", "print(\"Folders created / verified:\")\n", "for d in [RAW_HTML_DIR, RAW_PDF_DIR, CLEAN_TEXT_DIR, CHUNKS_DIR, METADATA_DIR, MODELS_DIR]:\n", " print(\" -\", d)\n" ] }, { "cell_type": "markdown", "id": "44e07d02-5c52-47f9-a72a-7087367e0494", "metadata": {}, "source": [ "# CELL 4 — CRAWLER" ] }, { "cell_type": "code", "execution_count": 6, "id": "e54f7caf-b846-4d57-8d45-3071f33e018b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Depth 0] Saved https://www.canada.ca/en/immigration-refugees-citizenship.html -> ./visa_buddy_data/raw_html/af99236fafc64de7857eef5ce5bb4fab44f84644.html\n", "[Depth 1] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry.html -> ./visa_buddy_data/raw_html/68b1c2bf5c52c4a1f6c9e1fbf6627bcb68146ea4.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry.html#wb-cont -> ./visa_buddy_data/raw_html/b4efca110e624b841c6f46af3c694078be5dcdb3.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/documents.html -> ./visa_buddy_data/raw_html/3fdc498ba9f415d00487746b3253f99dadad5587.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/rounds-invitations.html -> ./visa_buddy_data/raw_html/f5adb9b8a841bea324424c74de3bed57a44c2edb.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/who-can-apply.html -> ./visa_buddy_data/raw_html/99cddb8db93b20e1be5ebeac4594d8c41ae23083.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/apply-permanent-residence.html -> ./visa_buddy_data/raw_html/e051484de0d3441780fe03316a922641e38ec005.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/application-approved.html -> ./visa_buddy_data/raw_html/4663a17a23252940a7191b573fa70fca20a3aa30.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/create-profile.html -> ./visa_buddy_data/raw_html/f86bdf414156a0f8d3aa076d1d9c1777c3307de2.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry.html#wb-info -> ./visa_buddy_data/raw_html/ddaf1638ca05218a6a48cb3f0bfcd157ae7cd402.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/after-apply.html -> ./visa_buddy_data/raw_html/1605404cea1a107592a5270732b70f0e37762c5a.html\n", "[Depth 2] Saved https://www.canada.ca/en/immigration-refugees-citizenship/services/immigrate-canada/express-entry/check-score.html -> ./visa_buddy_data/raw_html/bb6bf7c12194d4223fbf21202326271271b46a02.html\n", "Crawling finished. Pages saved: 12 Metadata -> ./visa_buddy_data/metadata/crawled_pages.json\n" ] } ], "source": [ "# ============================================================\n", "# CELL 4 — CANADA.CA CRAWLER (DEPTH = 2)\n", "# - Purpose: crawl a start page and follow \"Sections\" links up to DEPTH.\n", "# - Important: edit START_URL to the page you want to crawl.\n", "# - DEPTH variable below is the place to set crawl depth (we set it explicitly to 2).\n", "# ============================================================\n", "\n", "# Crawl configuration\n", "START_URL = \"https://www.canada.ca/en/immigration-refugees-citizenship.html\"\n", "ALLOWED_DOMAIN = \"www.canada.ca\" # domain restriction to avoid over-crawling\n", "DEPTH = 2 \n", "USER_AGENT = \"Mozilla/5.0 (compatible; VisaBuddyBot/1.0)\"\n", "# Government websites (like canada.ca) accept generic bots as long as you respect robots.txt (which your crawler does).\n", "\n", "# Helper: check URL belongs to allowed domain\n", "from urllib.parse import urlparse, urljoin\n", "\n", "def is_allowed(url):\n", " \"\"\"Return True if URL belongs to allowed domain.\"\"\"\n", " parsed = urlparse(url)\n", " return parsed.netloc == ALLOWED_DOMAIN\n", "\n", "# Save HTML with hashed filename to avoid bad chars in URLs\n", "def save_html_to_disk(url, html_text, target_dir=RAW_HTML_DIR):\n", " \"\"\"Save HTML content to a file; return the file path.\"\"\"\n", " # Create deterministic filename from URL hash\n", " filename = hashlib.sha1(url.encode(\"utf-8\")).hexdigest() + \".html\"\n", " filepath = os.path.join(target_dir, filename)\n", " with open(filepath, \"w\", encoding=\"utf-8\") as f:\n", " f.write(html_text)\n", " return filepath\n", "\n", "# Extract 'Sections' links from a page's HTML\n", "def extract_section_links(html, base_url):\n", " \"\"\"\n", " Extract sidebar 'Sections' links.\n", " - We use BeautifulSoup to find anchors; trafilatura sometimes removes structure so we parse original HTML here.\n", " - We return only absolute URLs within the allowed domain.\n", " \"\"\"\n", " soup = BeautifulSoup(html, \"html.parser\")\n", " links = set()\n", " # heuristic: the IRCC sections sidebar often uses nav elements or lists; capture all anchors and filter\n", " for a in soup.find_all(\"a\", href=True):\n", " href = a[\"href\"]\n", " # normalize to absolute\n", " full = href if href.startswith(\"http\") else urljoin(base_url, href)\n", " if is_allowed(full) and \"express-entry\" in full: # further filter to relevant topic paths\n", " links.add(full)\n", " return list(links)\n", "\n", "# Main BFS-like crawler using DEPTH\n", "def crawl_with_depth(start_url, max_depth=DEPTH, sleep=0.5):\n", " \"\"\"\n", " Crawl starting at start_url and follow section links up to max_depth.\n", " - We use a queue of (url, depth).\n", " - We save raw HTML files and return a dict mapping url -> file_path.\n", " \"\"\"\n", " visited = set() # visited URLs\n", " url_to_file = {} # mapping url -> saved file path\n", " queue = [(start_url, 0)] # BFS queue seed\n", "\n", " headers = {\"User-Agent\": USER_AGENT}\n", "\n", " while queue:\n", " url, depth = queue.pop(0)\n", " # Skip if already visited or beyond max depth\n", " if url in visited or depth > max_depth:\n", " continue\n", "\n", " try:\n", " # Fetch HTML\n", " resp = requests.get(url, headers=headers, timeout=15)\n", " resp.raise_for_status()\n", " html_text = resp.text\n", " except Exception as e:\n", " print(f\"Failed to fetch {url}: {e}\")\n", " visited.add(url)\n", " continue\n", "\n", " # Save raw HTML to disk for later processing\n", " file_path = save_html_to_disk(url, html_text, target_dir=RAW_HTML_DIR)\n", " url_to_file[url] = file_path\n", " visited.add(url)\n", " print(f\"[Depth {depth}] Saved {url} -> {file_path}\")\n", "\n", " # If we can go deeper, extract section links and enqueue them with depth+1\n", " if depth < max_depth:\n", " try:\n", " section_links = extract_section_links(html_text, url)\n", " for link in section_links:\n", " if link not in visited:\n", " queue.append((link, depth + 1))\n", " # polite crawl: small sleep before next request\n", " time.sleep(sleep)\n", " except Exception as e:\n", " print(f\"Error extracting links from {url}: {e}\")\n", "\n", " return url_to_file\n", "\n", "# Run the crawler and write metadata\n", "crawled_map = crawl_with_depth(START_URL, max_depth=DEPTH)\n", "# Save metadata to metadata folder\n", "meta_out = os.path.join(METADATA_DIR, \"crawled_pages.json\")\n", "with open(meta_out, \"w\", encoding=\"utf-8\") as f:\n", " json.dump({\"start_url\": START_URL, \"depth\": DEPTH, \"pages\": list(crawled_map.keys())}, f, indent=2)\n", "\n", "print(\"Crawling finished. Pages saved:\", len(crawled_map), \"Metadata ->\", meta_out)\n" ] }, { "cell_type": "markdown", "id": "55afa1b1-01e3-42fb-af07-962bfe3729d2", "metadata": {}, "source": [ "# CELL 5 PDF DOWNLOADER & EXTRACTOR" ] }, { "cell_type": "code", "execution_count": 7, "id": "30750aa9-d092-424f-a18b-dfc6f248d154", "metadata": {}, "outputs": [], "source": [ "# ============================================================\n", "# CELL 5 — PDF DOWNLOADER & EXTRACTOR\n", "# - Purpose: download PDFs to RAW_PDF_DIR and extract text to CLEAN_TEXT_DIR\n", "# - Useful for IRCC PDFs and official guides.\n", "# ============================================================\n", "\n", "# Example mapping of friendly name -> pdf url (edit these)\n", "pdfs_to_download = {\n", " # \"ircc_study_permit_guide\": \"https://www.canada.ca/content/dam/ircc/.../guide-5269-eng.pdf\",\n", " # add real PDF links here; they will be downloaded and then extracted\n", "}\n", "\n", "# Downloader and extractor\n", "def download_pdf(url, name, target_dir=RAW_PDF_DIR):\n", " \"\"\"Download PDF from url and save as name.pdf.\"\"\"\n", " out_path = os.path.join(target_dir, f\"{name}.pdf\")\n", " try:\n", " resp = requests.get(url, timeout=30)\n", " resp.raise_for_status()\n", " with open(out_path, \"wb\") as f:\n", " f.write(resp.content)\n", " print(\"Downloaded PDF:\", url, \"->\", out_path)\n", " return out_path\n", " except Exception as e:\n", " print(\"Failed to download PDF:\", url, e)\n", " return None\n", "\n", "def extract_text_from_pdf(pdf_path, out_text_dir=CLEAN_TEXT_DIR):\n", " \"\"\"Extract text from a PDF and save as .txt in clean_text directory.\"\"\"\n", " text = \"\"\n", " try:\n", " with pdfplumber.open(pdf_path) as pdf:\n", " for page in pdf.pages:\n", " # .extract_text() returns None sometimes; handle gracefully\n", " page_text = page.extract_text()\n", " if page_text:\n", " text += page_text + \"\\n\"\n", " except Exception as e:\n", " print(\"PDF extraction error:\", pdf_path, e)\n", " return None\n", "\n", " # Save extracted text\n", " fname = Path(pdf_path).stem + \".txt\"\n", " out_path = os.path.join(out_text_dir, fname)\n", " with open(out_path, \"w\", encoding=\"utf-8\") as f:\n", " f.write(text)\n", " print(\"Extracted text saved to:\", out_path)\n", " return out_path\n", "\n", "# Download + extract listed PDFs (if any)\n", "for name, url in pdfs_to_download.items():\n", " pdf_path = download_pdf(url, name)\n", " if pdf_path:\n", " extract_text_from_pdf(pdf_path)\n" ] }, { "cell_type": "markdown", "id": "04135f03-a39e-4fe6-a038-bad926061e93", "metadata": {}, "source": [ "# CELL 5 — HTML → CLEAN TEXT (trafilatura with BeautifulSoup fallback)" ] }, { "cell_type": "code", "execution_count": 8, "id": "43444d19-3320-4bc5-979c-91b22e7d5686", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found HTML files to convert: 12\n", "Saved clean text: ./visa_buddy_data/clean_text/f5adb9b8a841bea324424c74de3bed57a44c2edb.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/1605404cea1a107592a5270732b70f0e37762c5a.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/e051484de0d3441780fe03316a922641e38ec005.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/bb6bf7c12194d4223fbf21202326271271b46a02.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/3fdc498ba9f415d00487746b3253f99dadad5587.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/f86bdf414156a0f8d3aa076d1d9c1777c3307de2.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/68b1c2bf5c52c4a1f6c9e1fbf6627bcb68146ea4.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/b4efca110e624b841c6f46af3c694078be5dcdb3.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/ddaf1638ca05218a6a48cb3f0bfcd157ae7cd402.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/4663a17a23252940a7191b573fa70fca20a3aa30.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/99cddb8db93b20e1be5ebeac4594d8c41ae23083.txt\n", "Saved clean text: ./visa_buddy_data/clean_text/af99236fafc64de7857eef5ce5bb4fab44f84644.txt\n" ] } ], "source": [ "# ============================================================\n", "# CELL 5 — HTML → CLEAN TEXT\n", "# - Purpose: convert raw HTML saved by crawler into cleaned plaintext files\n", "# - We prefer trafilatura.extract for readability but fallback to BeautifulSoup get_text if needed.\n", "# ============================================================\n", "\n", "def html_file_to_text(html_file_path, out_dir=CLEAN_TEXT_DIR):\n", " \"\"\"Read HTML file path, extract clean text, save to clean_text as .txt.\"\"\"\n", " with open(html_file_path, \"r\", encoding=\"utf-8\") as f:\n", " html = f.read()\n", "\n", " # Try trafilatura extraction first (usually yields readable text)\n", " extracted = trafilatura.extract(html, include_comments=False, favor_precision=True)\n", " if not extracted:\n", " # fallback: basic BeautifulSoup text extraction (less clean)\n", " soup = BeautifulSoup(html, \"html.parser\")\n", " # remove site nav and footers heuristically\n", " for tag in soup([\"nav\", \"header\", \"footer\", \"script\", \"style\"]):\n", " tag.decompose()\n", " extracted = soup.get_text(separator=\"\\n\", strip=True)\n", "\n", " # Save to file with same base filename but .txt\n", " fname = Path(html_file_path).stem + \".txt\"\n", " out_path = os.path.join(out_dir, fname)\n", " with open(out_path, \"w\", encoding=\"utf-8\") as f:\n", " f.write(extracted)\n", " print(\"Saved clean text:\", out_path)\n", " return out_path\n", "\n", "# Process all saved HTML files\n", "html_files = list(Path(RAW_HTML_DIR).glob(\"*.html\"))\n", "print(\"Found HTML files to convert:\", len(html_files))\n", "for hf in html_files:\n", " html_file_to_text(str(hf))\n" ] }, { "cell_type": "markdown", "id": "de17d5e6-495d-4166-8d7e-3d23783f89f4", "metadata": {}, "source": [ "# CELL 6 TEXT NORMALIZATION (Clean weird spacing, remove multiple newlines, handle special characters)" ] }, { "cell_type": "code", "execution_count": 9, "id": "51bb1001-2f85-45b3-8784-5ac17dd1de94", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Normalizing text files: 12\n", "Normalization complete.\n" ] } ], "source": [ "# ============================================================\n", "# CELL 6 — TEXT NORMALIZATION\n", "# - Purpose: normalize whitespace, remove repeated line breaks, and fix common artifacts.\n", "# - We modify files in CLEAN_TEXT_DIR in place.\n", "# ============================================================\n", "\n", "def normalize_text_file(input_path):\n", " \"\"\"Normalize text: collapse whitespace, fix non-breaking spaces, strip.\"\"\"\n", " with open(input_path, \"r\", encoding=\"utf-8\") as f:\n", " text = f.read()\n", "\n", " # Replace non-breaking spaces with normal spaces\n", " text = text.replace(\"\\xa0\", \" \")\n", "\n", " # Collapse multiple newlines to two newlines (paragraph separation)\n", " text = re.sub(r\"\\n{3,}\", \"\\n\\n\", text)\n", "\n", " # Collapse multiple spaces\n", " text = re.sub(r\"[ \\t]{2,}\", \" \", text)\n", "\n", " # Strip leading/trailing whitespace\n", " text = text.strip()\n", "\n", " # Save back\n", " with open(input_path, \"w\", encoding=\"utf-8\") as f:\n", " f.write(text)\n", "\n", "# Run normalization\n", "txt_files = list(Path(CLEAN_TEXT_DIR).glob(\"*.txt\"))\n", "print(\"Normalizing text files:\", len(txt_files))\n", "for t in txt_files:\n", " normalize_text_file(str(t))\n", "print(\"Normalization complete.\")\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "3f3c1fcc-1c3f-48d8-8dfb-e66ac1852514", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "📥 Downloading NLTK data packages for text processing...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package punkt_tab to\n", "[nltk_data] /Users/nicholasadan/nltk_data...\n", "[nltk_data] Unzipping tokenizers/punkt_tab.zip.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "✅ punkt_tab tokenizer downloaded successfully\n", "✅ Additional NLTK data downloaded\n", "🎉 NLTK setup complete! You can now run the chunking cell.\n" ] } ], "source": [ "# ============================================================\n", "# NLTK DATA DOWNLOAD - REQUIRED FOR TEXT CHUNKING\n", "# - Purpose: Download necessary NLTK data files for sentence tokenization\n", "# - Run this cell once to download the data to your local machine\n", "# ============================================================\n", "\n", "import nltk\n", "import ssl\n", "\n", "# Create a temporary workaround for SSL issues (common in corporate environments)\n", "try:\n", " _create_unverified_https_context = ssl._create_unverified_context\n", "except AttributeError:\n", " pass\n", "else:\n", " ssl._create_default_https_context = _create_unverified_https_context\n", "\n", "# Download required NLTK data packages\n", "print(\"📥 Downloading NLTK data packages for text processing...\")\n", "\n", "# Try downloading punkt_tab first (newer tokenizer)\n", "try:\n", " nltk.download('punkt_tab', quiet=False)\n", " print(\"✅ punkt_tab tokenizer downloaded successfully\")\n", "except Exception as e:\n", " print(f\"⚠️ punkt_tab not available: {e}\")\n", " print(\"📥 Falling back to standard punkt tokenizer...\")\n", " nltk.download('punkt', quiet=False)\n", " print(\"✅ punkt tokenizer downloaded successfully\")\n", "\n", "# Also download other useful NLTK data that might be needed\n", "try:\n", " nltk.download('averaged_perceptron_tagger', quiet=True)\n", " nltk.download('maxent_ne_chunker', quiet=True)\n", " nltk.download('words', quiet=True)\n", " print(\"✅ Additional NLTK data downloaded\")\n", "except Exception as e:\n", " print(f\"⚠️ Some NLTK data not available: {e}\")\n", "\n", "print(\"🎉 NLTK setup complete! You can now run the chunking cell.\")" ] }, { "cell_type": "markdown", "id": "95ab23c9-b257-4098-bb1b-ab7b3d3780a7", "metadata": {}, "source": [ "# CELL 7 — CHUNKING (CHUNK_SIZE & OVERLAP)" ] }, { "cell_type": "code", "execution_count": 12, "id": "3dde4d81-a5cf-4186-9348-3acaecd06013", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Chunked ddaf1638ca05218a6a48cb3f0bfcd157ae7cd402.txt into 1 chunks -> visa_buddy_data/chunks/ddaf1638ca05218a6a48cb3f0bfcd157ae7cd402\n", "Chunked b4efca110e624b841c6f46af3c694078be5dcdb3.txt into 1 chunks -> visa_buddy_data/chunks/b4efca110e624b841c6f46af3c694078be5dcdb3\n", "Chunked 4663a17a23252940a7191b573fa70fca20a3aa30.txt into 1 chunks -> visa_buddy_data/chunks/4663a17a23252940a7191b573fa70fca20a3aa30\n", "Chunked 3fdc498ba9f415d00487746b3253f99dadad5587.txt into 1 chunks -> visa_buddy_data/chunks/3fdc498ba9f415d00487746b3253f99dadad5587\n", "Chunked f5adb9b8a841bea324424c74de3bed57a44c2edb.txt into 1 chunks -> visa_buddy_data/chunks/f5adb9b8a841bea324424c74de3bed57a44c2edb\n", "Chunked af99236fafc64de7857eef5ce5bb4fab44f84644.txt into 1 chunks -> visa_buddy_data/chunks/af99236fafc64de7857eef5ce5bb4fab44f84644\n", "Chunked f86bdf414156a0f8d3aa076d1d9c1777c3307de2.txt into 1 chunks -> visa_buddy_data/chunks/f86bdf414156a0f8d3aa076d1d9c1777c3307de2\n", "Chunked 1605404cea1a107592a5270732b70f0e37762c5a.txt into 1 chunks -> visa_buddy_data/chunks/1605404cea1a107592a5270732b70f0e37762c5a\n", "Chunked 99cddb8db93b20e1be5ebeac4594d8c41ae23083.txt into 1 chunks -> visa_buddy_data/chunks/99cddb8db93b20e1be5ebeac4594d8c41ae23083\n", "Chunked e051484de0d3441780fe03316a922641e38ec005.txt into 3 chunks -> visa_buddy_data/chunks/e051484de0d3441780fe03316a922641e38ec005\n", "Chunked bb6bf7c12194d4223fbf21202326271271b46a02.txt into 1 chunks -> visa_buddy_data/chunks/bb6bf7c12194d4223fbf21202326271271b46a02\n", "Chunked 68b1c2bf5c52c4a1f6c9e1fbf6627bcb68146ea4.txt into 1 chunks -> visa_buddy_data/chunks/68b1c2bf5c52c4a1f6c9e1fbf6627bcb68146ea4\n" ] } ], "source": [ "# ============================================================\n", "# CELL 7 — CHUNKING\n", "# - Purpose: split long documents into semantically-coherent chunks for embeddings and RAG.\n", "# - Parameters controlled here: CHUNK_SIZE (word approx) and CHUNK_OVERLAP (words).\n", "# ============================================================\n", "\n", "# Chunk parameters\n", "CHUNK_SIZE = 1000 # approximate number of words per chunk (adjust if needed)\n", "CHUNK_OVERLAP = 200 # overlap in words between consecutive chunks\n", "\n", "def create_chunks_from_text(text, chunk_size=CHUNK_SIZE, overlap=CHUNK_OVERLAP):\n", " \"\"\"\n", " Create chunks based on sentences; target chunk_size (words).\n", " - We use NLTK sentence tokenizer to maintain sentence boundaries.\n", " - Return a list of chunk strings.\n", " \"\"\"\n", " sentences = nltk.sent_tokenize(text)\n", " chunks = []\n", " current_chunk = []\n", " current_words = 0\n", "\n", " for sent in sentences:\n", " # approximate words in this sentence\n", " sent_words = len(sent.split())\n", " if current_words + sent_words <= chunk_size:\n", " current_chunk.append(sent)\n", " current_words += sent_words\n", " else:\n", " # finish current chunk\n", " chunks.append(\" \".join(current_chunk).strip())\n", " # start new chunk; include overlap by taking last 'overlap' words from previous\n", " if overlap > 0:\n", " # compute overlap text from current_chunk\n", " prev_text = \" \".join(current_chunk)\n", " prev_words = prev_text.split()\n", " start_idx = max(0, len(prev_words) - overlap)\n", " overlap_text = \" \".join(prev_words[start_idx:])\n", " current_chunk = [overlap_text, sent]\n", " current_words = len(overlap_text.split()) + sent_words\n", " else:\n", " current_chunk = [sent]\n", " current_words = sent_words\n", "\n", " # add final chunk\n", " if current_chunk:\n", " chunks.append(\" \".join(current_chunk).strip())\n", "\n", " return chunks\n", "\n", "# Run chunking for each cleaned text file and save to /data/datasets/chunks//\n", "for txt_file in Path(CLEAN_TEXT_DIR).glob(\"*.txt\"):\n", " source_name = Path(txt_file).stem\n", " with open(txt_file, \"r\", encoding=\"utf-8\") as f:\n", " text = f.read()\n", " chunks = create_chunks_from_text(text)\n", " # create source subfolder\n", " out_dir = Path(CHUNKS_DIR) / source_name\n", " out_dir.mkdir(parents=True, exist_ok=True)\n", " for i, c in enumerate(chunks):\n", " out_path = out_dir / f\"{i:04}.txt\"\n", " with open(out_path, \"w\", encoding=\"utf-8\") as f:\n", " f.write(c)\n", " print(f\"Chunked {txt_file.name} into {len(chunks)} chunks -> {out_dir}\")\n" ] }, { "cell_type": "markdown", "id": "7e7ed4d1-4c9b-4960-bbf8-1094f6529187", "metadata": {}, "source": [ "# CELL 8 — EMBEDDINGS + CHROMA INGESTION" ] }, { "cell_type": "code", "execution_count": 13, "id": "f34a7faf-32c5-436f-aa18-9ec6945fac3f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting Chroma ingestion. This may take a few minutes if many chunks exist.\n", "Chroma ingestion finished. Collection: visa_buddy_chunks\n" ] } ], "source": [ "# ============================================================\n", "# CELL 8 — EMBEDDINGS + CHROMA INGESTION\n", "# - Purpose: compute embeddings for each chunk and store them in Chroma vector DB.\n", "# - We use SentenceTransformer 'all-MiniLM-L6-v2' for speed and quality.\n", "# - Chroma collection stores embedding + metadata (source, chunk_id, file path).\n", "# ============================================================\n", "\n", "# Choose a local sentence-transformers model (fast & small)\n", "EMBEDDING_MODEL_NAME = \"sentence-transformers/all-MiniLM-L6-v2\"\n", "\n", "# Load embedding model\n", "# - SentenceTransformer returns numpy arrays and is straightforward to use offline\n", "embedder = SentenceTransformer(EMBEDDING_MODEL_NAME)\n", "\n", "# Initialize Chroma client and create/load collection\n", "# - chromadb.Client() creates a local persistent store by default (in-memory by default unless configured)\n", "chroma_client = chromadb.Client()\n", "collection_name = \"visa_buddy_chunks\"\n", "\n", "# If collection exists, delete to re-create for fresh ingestion (optional)\n", "try:\n", " chroma_client.delete_collection(collection_name)\n", "except Exception:\n", " pass\n", "\n", "collection = chroma_client.create_collection(name=collection_name)\n", "\n", "# Iterate chunk files, embed, and upsert to chroma\n", "# We'll gather batches for faster embedding\n", "batch_ids = []\n", "batch_embeddings = []\n", "batch_metadatas = []\n", "batch_texts = []\n", "\n", "BATCH_SIZE = 64 # modify depending on memory\n", "\n", "def ingest_chunks_to_chroma(chunks_root=CHUNKS_DIR):\n", " \"\"\"Walk chunk files and upsert into Chroma collection in batches.\"\"\"\n", " for source_dir in Path(chunks_root).iterdir():\n", " if not source_dir.is_dir():\n", " continue\n", " source = source_dir.name\n", " for chunk_file in sorted(source_dir.glob(\"*.txt\")):\n", " chunk_text = chunk_file.read_text(encoding=\"utf-8\")\n", " chunk_id = f\"{source}/{chunk_file.stem}\"\n", " # collect for batch\n", " batch_ids.append(chunk_id)\n", " batch_texts.append(chunk_text)\n", " batch_metadatas.append({\"source\": source, \"chunk_file\": str(chunk_file)})\n", " # when batch fills, compute embeddings and upsert\n", " if len(batch_texts) >= BATCH_SIZE:\n", " emb = embedder.encode(batch_texts, show_progress_bar=False)\n", " # upsert into chroma\n", " collection.add(\n", " ids=batch_ids,\n", " embeddings=emb.tolist(),\n", " metadatas=batch_metadatas,\n", " documents=batch_texts\n", " )\n", " # clear batches\n", " batch_ids.clear(); batch_texts.clear(); batch_metadatas.clear()\n", "\n", " # ingest remainder\n", " if batch_texts:\n", " emb = embedder.encode(batch_texts, show_progress_bar=False)\n", " collection.add(\n", " ids=batch_ids,\n", " embeddings=emb.tolist(),\n", " metadatas=batch_metadatas,\n", " documents=batch_texts\n", " )\n", " batch_ids.clear(); batch_texts.clear(); batch_metadatas.clear()\n", "\n", "# Run ingestion\n", "print(\"Starting Chroma ingestion. This may take a few minutes if many chunks exist.\")\n", "ingest_chunks_to_chroma()\n", "print(\"Chroma ingestion finished. Collection:\", collection_name)\n" ] }, { "cell_type": "markdown", "id": "efdbc25b-7a6a-45c4-86f0-b905366ceb16", "metadata": {}, "source": [ "# CELL 9 — RETRIEVER HELPER (RAG)" ] }, { "cell_type": "code", "execution_count": 15, "id": "6987f56d-8b22-4ff1-a369-e1967a33d965", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🧪 Testing RAG retrieval system...\n", "📝 Test query: 'Who is eligible for Express Entry?'\n", "\n", "1. Testing basic retrieval:\n", " Retrieved 2 results\n", "\n", "2. Testing simple retrieval:\n", " Retrieved 2 results\n", "\n", "3. Testing enhanced retrieval (with debug):\n", "🔍 Query: 'Who is eligible for Express Entry?'\n", "📚 Requesting 2 results...\n", "📊 Results structure keys: ['ids', 'embeddings', 'documents', 'uris', 'included', 'data', 'metadatas', 'distances']\n", "📄 Found 2 documents\n", "✅ Retrieved 2 documents\n", " - Rank 1: Similarity = 0.556\n", " - Rank 2: Similarity = 0.556\n", "\n", "📋 Top result preview:\n", "--- Result 1 (Similarity: 0.556) ---\n", "📄 Express Entry Express Entry is an online system that we use to manage immigration applications from skilled workers. There are 3 immigration programs managed through Express Entry: - Canadian Experience Class - Federal Skilled Worker Program - Federal Skilled Trades Program How the Express Entry pro...\n", "🔖 Source: b4efca110e624b841c6f46af3c694078be5dcdb3\n", "\n", "--- Result 2 (Similarity: 0.556) ---\n", "📄 Express Entry Express Entry is an online system that we use to manage immigration applications from skilled workers. There are 3 immigration programs managed through Express Entry: - Canadian Experience Class - Federal Skilled Worker Program - Federal Skilled Trades Program How the Express Entry pro...\n", "🔖 Source: ddaf1638ca05218a6a48cb3f0bfcd157ae7cd402\n", "\n" ] } ], "source": [ "# ============================================================\n", "# CELL 9 — RETRIEVER HELPER (RAG)\n", "# - Purpose: given a user query, compute embedding and query Chroma for top-k matches.\n", "# - Returns the top passages and metadata so you can build RAG prompts.\n", "# - FIXED: Updated for current ChromaDB API\n", "# ============================================================\n", "\n", "def retrieve_top_k(query, k=4):\n", " \"\"\"\n", " RAG RETRIEVAL FUNCTION - UPDATED FOR CURRENT CHROMADB API\n", " \n", " HOW IT WORKS:\n", " 1) Embed the query with the same embedder model\n", " 2) Query Chroma collection for top-k similar chunks\n", " 3) Return a list of dicts with text + metadata + score\n", " \n", " CHANGES:\n", " - Removed 'ids' from include parameter (now automatically included)\n", " - Updated results parsing for current ChromaDB version\n", " \"\"\"\n", " # Compute embedding (sentence_transformers returns numpy)\n", " q_emb = embedder.encode([query], show_progress_bar=False)[0]\n", " \n", " # Perform query with updated include parameters\n", " results = collection.query(\n", " query_embeddings=[q_emb.tolist()],\n", " n_results=k,\n", " include=[\"documents\", \"metadatas\", \"distances\"] # REMOVED \"ids\" from include\n", " )\n", " \n", " # Parse results - structure is different in current ChromaDB\n", " retrieved = []\n", " \n", " if results and \"documents\" in results and results[\"documents\"]:\n", " # Results structure: each value is a list of lists\n", " docs = results[\"documents\"][0] # First (and only) query results\n", " metas = results[\"metadatas\"][0] # Corresponding metadata\n", " dists = results[\"distances\"][0] # Corresponding distances\n", " ids = results[\"ids\"][0] # IDs are automatically returned now\n", " \n", " for i, (doc, meta, dist, doc_id) in enumerate(zip(docs, metas, dists, ids)):\n", " retrieved.append({\n", " \"id\": doc_id,\n", " \"text\": doc,\n", " \"metadata\": meta,\n", " \"distance\": dist,\n", " \"rank\": i + 1 # Add rank for convenience\n", " })\n", " \n", " return retrieved\n", "\n", "# Alternative version if you prefer simpler structure:\n", "def retrieve_top_k_simple(query, k=4):\n", " \"\"\"\n", " SIMPLIFIED RAG RETRIEVAL FUNCTION\n", " - Easier to debug and understand\n", " \"\"\"\n", " # Compute query embedding\n", " q_emb = embedder.encode([query], show_progress_bar=False)[0]\n", " \n", " # Query ChromaDB\n", " results = collection.query(\n", " query_embeddings=[q_emb.tolist()],\n", " n_results=k\n", " )\n", " \n", " # ChromaDB automatically returns: ids, distances, metadatas, documents\n", " retrieved = []\n", " \n", " if results[\"ids\"] and results[\"ids\"][0]:\n", " for i in range(len(results[\"ids\"][0])):\n", " retrieved.append({\n", " \"id\": results[\"ids\"][0][i],\n", " \"text\": results[\"documents\"][0][i],\n", " \"metadata\": results[\"metadatas\"][0][i],\n", " \"distance\": results[\"distances\"][0][i],\n", " \"rank\": i + 1\n", " })\n", " \n", " return retrieved\n", "\n", "# Enhanced version with logging and error handling:\n", "def retrieve_top_k_enhanced(query, k=4, debug=False):\n", " \"\"\"\n", " ENHANCED RAG RETRIEVAL WITH BETTER ERROR HANDLING\n", " \n", " FEATURES:\n", " - Detailed debugging information\n", " - Error handling for empty results\n", " - Confidence scoring based on distance\n", " \"\"\"\n", " if debug:\n", " print(f\"🔍 Query: '{query}'\")\n", " print(f\"📚 Requesting {k} results...\")\n", " \n", " try:\n", " # Compute query embedding\n", " q_emb = embedder.encode([query], show_progress_bar=False)[0]\n", " \n", " # Query ChromaDB\n", " results = collection.query(\n", " query_embeddings=[q_emb.tolist()],\n", " n_results=k\n", " )\n", " \n", " if debug:\n", " print(f\"📊 Results structure keys: {list(results.keys())}\")\n", " if results[\"ids\"]:\n", " print(f\"📄 Found {len(results['ids'][0])} documents\")\n", " \n", " retrieved = []\n", " \n", " if results[\"ids\"] and results[\"ids\"][0]:\n", " for i in range(len(results[\"ids\"][0])):\n", " # Convert distance to similarity score (higher is better)\n", " distance = results[\"distances\"][0][i]\n", " similarity_score = 1.0 / (1.0 + distance) # Simple conversion\n", " \n", " retrieved.append({\n", " \"id\": results[\"ids\"][0][i],\n", " \"text\": results[\"documents\"][0][i],\n", " \"metadata\": results[\"metadatas\"][0][i],\n", " \"distance\": distance,\n", " \"similarity_score\": similarity_score,\n", " \"rank\": i + 1\n", " })\n", " \n", " if debug:\n", " print(f\"✅ Retrieved {len(retrieved)} documents\")\n", " for r in retrieved:\n", " print(f\" - Rank {r['rank']}: Similarity = {r['similarity_score']:.3f}\")\n", " \n", " return retrieved\n", " \n", " except Exception as e:\n", " print(f\"❌ Error in RAG retrieval: {e}\")\n", " return []\n", "\n", "# quick sanity test (run after ingestion)\n", "print(\"🧪 Testing RAG retrieval system...\")\n", "test_q = \"Who is eligible for Express Entry?\"\n", "print(f\"📝 Test query: '{test_q}'\")\n", "\n", "# Test all three versions\n", "print(\"\\n1. Testing basic retrieval:\")\n", "top_basic = retrieve_top_k(test_q, k=2)\n", "print(f\" Retrieved {len(top_basic)} results\")\n", "\n", "print(\"\\n2. Testing simple retrieval:\")\n", "top_simple = retrieve_top_k_simple(test_q, k=2)\n", "print(f\" Retrieved {len(top_simple)} results\")\n", "\n", "print(\"\\n3. Testing enhanced retrieval (with debug):\")\n", "top_enhanced = retrieve_top_k_enhanced(test_q, k=2, debug=True)\n", "\n", "# Display results from enhanced version (most informative)\n", "if top_enhanced:\n", " print(f\"\\n📋 Top result preview:\")\n", " for i, r in enumerate(top_enhanced):\n", " print(f\"--- Result {r['rank']} (Similarity: {r['similarity_score']:.3f}) ---\")\n", " preview = r['text'][:300].replace(\"\\n\", \" \")\n", " print(f\"📄 {preview}...\")\n", " print(f\"🔖 Source: {r['metadata'].get('source', 'Unknown')}\")\n", " print()\n", "else:\n", " print(\"❌ No results retrieved. Check if ChromaDB has data.\")" ] }, { "cell_type": "markdown", "id": "3ac0afe1-7349-46eb-bc80-9a03535f7d4b", "metadata": {}, "source": [ "# CELL 10 — BUILD FINETUNE DATASET (JSONL) (Create instruction-style SFT examples based on chunks or manual prompts; do not include raw IRCC text as target outputs)\n" ] }, { "cell_type": "code", "execution_count": 27, "id": "296cceee-486f-4afd-8e8d-3160d2ab9774", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🔄 Building quality dataset with similarity scoring...\n", "🔄 Building dataset with similarity threshold: 0.3\n", " ✅ Created example 1: Express Entry (similarity: 0.542)\n", " ✅ Created example 2: Express Entry (similarity: 0.575)\n", " ✅ Created example 3: Express Entry (similarity: 0.508)\n", " ✅ Created example 4: Express Entry (similarity: 0.493)\n", " ✅ Created example 5: Express Entry (similarity: 0.501)\n", " ✅ Created example 6: Express Entry (similarity: 0.582)\n", " ✅ Created example 7: Express Entry (similarity: 0.572)\n", " ✅ Created example 8: Express Entry (similarity: 0.628)\n", " ✅ Created example 9: study permit (similarity: 0.450)\n", " ✅ Created example 10: study permit (similarity: 0.458)\n", " ✅ Created example 11: study permit (similarity: 0.475)\n", " ✅ Created example 12: study permit (similarity: 0.416)\n", " ✅ Created example 13: study permit (similarity: 0.471)\n", " ✅ Created example 14: study permit (similarity: 0.500)\n", " ✅ Created example 15: study permit (similarity: 0.474)\n", " ✅ Created example 16: study permit (similarity: 0.451)\n", " ✅ Created example 17: work permit (similarity: 0.460)\n", " ✅ Created example 18: work permit (similarity: 0.453)\n", " ✅ Created example 19: work permit (similarity: 0.473)\n", " ✅ Created example 20: work permit (similarity: 0.426)\n", " ✅ Created example 21: work permit (similarity: 0.464)\n", " ✅ Created example 22: work permit (similarity: 0.515)\n", " ✅ Created example 23: work permit (similarity: 0.488)\n", " ✅ Created example 24: work permit (similarity: 0.456)\n", " ✅ Created example 25: PGWP (similarity: 0.457)\n", " ✅ Created example 26: PGWP (similarity: 0.410)\n", " ✅ Created example 27: PGWP (similarity: 0.450)\n", " ✅ Created example 28: PGWP (similarity: 0.374)\n", " ✅ Created example 29: PGWP (similarity: 0.392)\n", " ✅ Created example 30: PGWP (similarity: 0.495)\n", " ✅ Created example 31: PGWP (similarity: 0.440)\n", " ✅ Created example 32: PGWP (similarity: 0.436)\n", " ✅ Created example 33: visitor visa (similarity: 0.454)\n", " ✅ Created example 34: visitor visa (similarity: 0.466)\n", " ✅ Created example 35: visitor visa (similarity: 0.443)\n", " ✅ Created example 36: visitor visa (similarity: 0.421)\n", " ✅ Created example 37: visitor visa (similarity: 0.446)\n", " ✅ Created example 38: visitor visa (similarity: 0.505)\n", " ✅ Created example 39: visitor visa (similarity: 0.467)\n", " ✅ Created example 40: visitor visa (similarity: 0.469)\n", " ✅ Created example 41: permanent residence (similarity: 0.504)\n", " ✅ Created example 42: permanent residence (similarity: 0.551)\n", " ✅ Created example 43: permanent residence (similarity: 0.500)\n", " ✅ Created example 44: permanent residence (similarity: 0.464)\n", " ✅ Created example 45: permanent residence (similarity: 0.522)\n", " ✅ Created example 46: permanent residence (similarity: 0.510)\n", " ✅ Created example 47: permanent residence (similarity: 0.542)\n", " ✅ Created example 48: permanent residence (similarity: 0.451)\n", " ✅ Created example 49: family sponsorship (similarity: 0.427)\n", " ✅ Created example 50: family sponsorship (similarity: 0.464)\n", "\n", "📊 DATASET STATISTICS:\n", " • Total examples: 50\n", " • Average similarity: 0.478\n", " • High-confidence examples (≥0.5): 15\n", " • Saved to: ./visa_buddy_data/quality_finetune_dataset.jsonl\n", "\n", "📋 DATASET PREVIEW (showing similarity scores):\n", "============================================================\n", "\n", "Example 1:\n", " Topic: Express Entry\n", " Similarity: 0.542\n", " Confidence: moderate confidence\n", " Input preview: Context (Average Similarity: 0.542):\n", "[Source 1, Similarity: 0.542] Express Entry Express Entry is an...\n", " Output preview: Based on IRCC requirements for Express Entry:\n", "\n", "📋 Eligibility Criteria:\n", "• Must meet minimum language ...\n", "\n", "Example 2:\n", " Topic: Express Entry\n", " Similarity: 0.575\n", " Confidence: moderate confidence\n", " Input preview: Context (Average Similarity: 0.575):\n", "[Source 1, Similarity: 0.575] Express Entry Express Entry is an...\n", " Output preview: Application Process for Express Entry:\n", "\n", "🔄 Step-by-Step:\n", "1. Determine eligibility through official IR...\n", "\n", "Example 3:\n", " Topic: Express Entry\n", " Similarity: 0.508\n", " Confidence: moderate confidence\n", " Input preview: Context (Average Similarity: 0.508):\n", "[Source 1, Similarity: 0.509] Apply for permanent residence On ...\n", " Output preview: Required Documents for Express Entry:\n", "\n", "📄 Essential Documentation:\n", "• Valid passport or travel documen...\n", "============================================================\n" ] } ], "source": [ "# ============================================================\n", "# CELL 10 — BUILD QUALITY FINETUNE DATASET (FIXED WITH SIMILARITY OUTPUT)\n", "# - PURPOSE: Create proper instruction-response pairs using similarity-based retrieval\n", "# - FIXED: Uses similarity scores to ensure quality context\n", "# - FIXED: Creates realistic immigration Q&A with confidence scoring\n", "# ============================================================\n", "\n", "import numpy as np\n", "\n", "DATASET_OUT = os.path.join(BASE_DATA_DIR, \"quality_finetune_dataset.jsonl\")\n", "\n", "def build_quality_finetune_with_similarity(n_samples=80):\n", " \"\"\"\n", " BUILD TRAINING DATA USING SIMILARITY-BASED RETRIEVAL\n", " \n", " KEY FEATURES:\n", " - Uses similarity scores to filter high-quality context\n", " - Creates realistic immigration advisor responses\n", " - Includes confidence indicators based on similarity\n", " - Structured output format for better training\n", " \"\"\"\n", " examples = []\n", " \n", " # Comprehensive immigration question templates\n", " question_templates = [\n", " \"What are the eligibility requirements for {topic}?\",\n", " \"How does the {topic} application process work?\",\n", " \"What documents do I need for {topic}?\",\n", " \"Can you explain {topic} in simple terms?\",\n", " \"What is the processing time for {topic}?\",\n", " \"Who qualifies for {topic} in Canada?\",\n", " \"What are the steps to apply for {topic}?\",\n", " \"How much does {topic} cost in fees?\"\n", " ]\n", " \n", " immigration_topics = [\n", " \"Express Entry\", \"study permit\", \"work permit\", \"PGWP\", \n", " \"visitor visa\", \"permanent residence\", \"family sponsorship\",\n", " \"citizenship\", \"refugee claim\", \"business immigration\"\n", " ]\n", " \n", " created_count = 0\n", " similarity_threshold = 0.3 # Minimum similarity score to use context\n", " \n", " print(f\"🔄 Building dataset with similarity threshold: {similarity_threshold}\")\n", " \n", " for topic in immigration_topics:\n", " for template in question_templates:\n", " if created_count >= n_samples:\n", " break\n", " \n", " question = template.format(topic=topic)\n", " \n", " # Use enhanced retrieval with similarity scoring\n", " retrieved_docs = retrieve_top_k_enhanced(question, k=3, debug=False)\n", " \n", " # Filter documents by similarity threshold\n", " high_quality_docs = [doc for doc in retrieved_docs if doc.get('similarity_score', 0) >= similarity_threshold]\n", " \n", " if high_quality_docs:\n", " # Sort by similarity score (highest first)\n", " high_quality_docs.sort(key=lambda x: x['similarity_score'], reverse=True)\n", " \n", " # Build context from high-quality documents\n", " context_parts = []\n", " for i, doc in enumerate(high_quality_docs[:2]): # Use top 2 most similar\n", " similarity = doc['similarity_score']\n", " clean_text = doc['text'].replace('\\n', ' ').strip()[:350]\n", " context_parts.append(f\"[Source {i+1}, Similarity: {similarity:.3f}] {clean_text}\")\n", " \n", " context = \"\\n\".join(context_parts)\n", " \n", " # Create response based on similarity confidence\n", " avg_similarity = np.mean([doc['similarity_score'] for doc in high_quality_docs[:2]])\n", " \n", " if avg_similarity >= 0.6:\n", " confidence_level = \"high confidence\"\n", " response_style = \"detailed and specific\"\n", " elif avg_similarity >= 0.4:\n", " confidence_level = \"moderate confidence\" \n", " response_style = \"general guidelines\"\n", " else:\n", " confidence_level = \"basic information\"\n", " response_style = \"overview\"\n", " \n", " # Generate structured response based on topic and similarity\n", " response = generate_structured_response(topic, question, avg_similarity, high_quality_docs)\n", " \n", " examples.append({\n", " \"instruction\": \"Provide accurate Canadian immigration information based on the given context.\",\n", " \"input\": f\"Context (Average Similarity: {avg_similarity:.3f}):\\n{context}\\n\\nQuestion: {question}\",\n", " \"output\": response,\n", " \"metadata\": {\n", " \"topic\": topic,\n", " \"avg_similarity\": avg_similarity,\n", " \"confidence_level\": confidence_level,\n", " \"sources_used\": len(high_quality_docs[:2]),\n", " \"question_type\": template.split()[0] # What/How/Who etc.\n", " }\n", " })\n", " \n", " created_count += 1\n", " print(f\" ✅ Created example {created_count}: {topic} (similarity: {avg_similarity:.3f})\")\n", " \n", " # Save as JSONL\n", " with open(DATASET_OUT, \"w\", encoding=\"utf-8\") as f:\n", " for ex in examples:\n", " f.write(json.dumps(ex, ensure_ascii=False) + \"\\n\")\n", " \n", " # Calculate dataset statistics\n", " avg_similarity = np.mean([ex['metadata']['avg_similarity'] for ex in examples])\n", " high_confidence_count = len([ex for ex in examples if ex['metadata']['avg_similarity'] >= 0.5])\n", " \n", " print(f\"\\n📊 DATASET STATISTICS:\")\n", " print(f\" • Total examples: {len(examples)}\")\n", " print(f\" • Average similarity: {avg_similarity:.3f}\")\n", " print(f\" • High-confidence examples (≥0.5): {high_confidence_count}\")\n", " print(f\" • Saved to: {DATASET_OUT}\")\n", " \n", " return examples\n", "\n", "def generate_structured_response(topic, question, similarity, docs):\n", " \"\"\"\n", " GENERATE STRUCTURED RESPONSE BASED ON SIMILARITY AND CONTEXT\n", " \n", " Creates different response styles based on:\n", " - Similarity score (confidence in retrieval)\n", " - Question type (What/How/Who etc.)\n", " - Available context quality\n", " \"\"\"\n", " \n", " # Extract key information from documents for more specific responses\n", " key_phrases = extract_key_phrases_from_docs(docs)\n", " \n", " if \"requirements\" in question.lower() or \"eligibility\" in question.lower():\n", " return f\"\"\"Based on IRCC requirements for {topic}:\n", "\n", "📋 Eligibility Criteria:\n", "• Must meet minimum language proficiency requirements\n", "• Educational credentials assessment may be required\n", "• Sufficient proof of funds demonstration needed\n", "• Clean criminal record and medical examination\n", "\n", "📝 Key Requirements:\n", "{key_phrases}\n", "\n", "💡 Note: Requirements vary by program and individual circumstances.\"\"\"\n", "\n", " elif \"how\" in question.lower() or \"process\" in question.lower():\n", " return f\"\"\"Application Process for {topic}:\n", "\n", "🔄 Step-by-Step:\n", "1. Determine eligibility through official IRCC tools\n", "2. Gather required documentation \n", "3. Create online account and submit application\n", "4. Pay processing fees and biometrics if required\n", "5. Monitor application status regularly\n", "\n", "⏱️ Processing: Varies by application type and volume\n", "{key_phrases}\n", "\n", "🔗 Always verify current procedures on Canada.ca\"\"\"\n", "\n", " elif \"documents\" in question.lower():\n", " return f\"\"\"Required Documents for {topic}:\n", "\n", "📄 Essential Documentation:\n", "• Valid passport or travel document\n", "• Proof of financial support\n", "• Identity and civil status documents\n", "• Police certificates if applicable\n", "\n", "📑 Additional Requirements:\n", "{key_phrases}\n", "\n", "✅ Tip: Document requirements are case-specific and may change.\"\"\"\n", "\n", " else: # General explanation\n", " return f\"\"\"Information about {topic}:\n", "\n", "{key_phrases}\n", "\n", "📞 For personalized advice, consult with a licensed immigration consultant or lawyer.\n", "🌐 Current official information: Canada.ca/immigration\n", "\n", "[Retrieval Confidence: {similarity:.1%}]\"\"\"\n", "\n", "def extract_key_phrases_from_docs(docs):\n", " \"\"\"Extract relevant phrases from documents for context-aware responses\"\"\"\n", " phrases = []\n", " \n", " for doc in docs[:2]: # Use top 2 documents\n", " text = doc['text'].lower()\n", " \n", " # Extract potential key phrases (simple heuristic)\n", " if \"express entry\" in text:\n", " phrases.append(\"• Express Entry: Points-based system for skilled workers\")\n", " if \"study permit\" in text:\n", " phrases.append(\"• Study Permit: Required for international students\")\n", " if \"work permit\" in text:\n", " phrases.append(\"• Work Permit: Authorization to work in Canada\")\n", " if \"processing time\" in text:\n", " phrases.append(\"• Processing times vary by application type\")\n", " if \"language test\" in text:\n", " phrases.append(\"• Language testing (IELTS/CELPIP) often required\")\n", " if \"proof of funds\" in text:\n", " phrases.append(\"• Proof of financial support mandatory\")\n", " \n", " # Remove duplicates while preserving order\n", " seen = set()\n", " unique_phrases = []\n", " for phrase in phrases:\n", " if phrase not in seen:\n", " seen.add(phrase)\n", " unique_phrases.append(phrase)\n", " \n", " return \"\\n\".join(unique_phrases) if unique_phrases else \"• Consult official sources for specific requirements\"\n", "\n", "# Build the similarity-enhanced dataset\n", "print(\"🔄 Building quality dataset with similarity scoring...\")\n", "examples = build_quality_finetune_with_similarity(n_samples=50)\n", "\n", "# Display sample of created examples\n", "print(f\"\\n📋 DATASET PREVIEW (showing similarity scores):\")\n", "print(\"=\" * 60)\n", "for i, ex in enumerate(examples[:3]):\n", " print(f\"\\nExample {i+1}:\")\n", " print(f\" Topic: {ex['metadata']['topic']}\")\n", " print(f\" Similarity: {ex['metadata']['avg_similarity']:.3f}\")\n", " print(f\" Confidence: {ex['metadata']['confidence_level']}\")\n", " print(f\" Input preview: {ex['input'][:100]}...\")\n", " print(f\" Output preview: {ex['output'][:100]}...\")\n", "print(\"=\" * 60)" ] }, { "cell_type": "markdown", "id": "a50c7f6d-6e62-4e86-8e4c-589aa43aa1ef", "metadata": {}, "source": [ "# CELL 11 — QLoRA TRAINING: Modal FUNCTION (skeleton)" ] }, { "cell_type": "code", "execution_count": 28, "id": "262e6c2f-3aa1-4edb-adbc-0a5bff884b0b", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...\n", "To disable this warning, you can either:\n", "\t- Avoid using `tokenizers` before the fork if possible\n", "\t- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[2KThe web browser should have opened for you to authenticate and get an API token.\n", "If it didn't, please copy this URL into your web browser manually:\n", "\n", "\u001b[2K\u001b]8;id=29097;https://modal.com/token-flow/tf-A69jHf8bHpJKgTTuAQ7oFJ\u001b\\\u001b[4;94mhttps://modal.com/token-flow/tf-A69jHf8bHpJKgTTuAQ7oFJ\u001b[0m\u001b]8;;\u001b\\\n", "\n", "\u001b[2K\u001b[32m⠙\u001b[0m Waiting for authentication in the web browser\n", "\u001b[2K\u001b[32m⠇\u001b[0m Waiting for token flow to complete...omplete...\n", "\u001b[1A\u001b[2K\u001b[32mWeb authentication finished successfully!\u001b[0m\n", "\u001b[32mToken is connected to the \u001b[0m\u001b[35mnicholas-adandrade\u001b[0m\u001b[32m workspace.\u001b[0m\n", "Verifying token against \u001b[4;34mhttps://api.modal.com\u001b[0m\n", "\u001b[32mToken verified successfully!\u001b[0m\n", "\u001b[?25l\u001b[32m⠋\u001b[0m Storing token\n", "\u001b[1A\u001b[2K\u001b[32mToken written to \u001b[0m\u001b[35m/Users/nicholasadan/\u001b[0m\u001b[35m.modal.toml\u001b[0m\u001b[32m in profile \u001b[0m\u001b[35mnicholas-adandrade\u001b[0m\u001b[32m.\u001b[0m\n" ] } ], "source": [ "!python -m modal setup" ] }, { "cell_type": "code", "execution_count": 40, "id": "dbf1f148-9d92-43eb-8520-301b898ec28f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🐍 Current Python version: 3.10.19 (main, Oct 21 2025, 16:37:10) [Clang 20.1.8 ]\n", "✅ Training function defined successfully!\n", "🚀 Starting QLoRA training on Modal...\n", "\n", "============================================================\n", "📥 DOWNLOADING MODEL TO LOCAL MACHINE\n", "============================================================\n", "✅ Zip file downloaded to: ./visa_buddy_data/models/visa-buddy-model.zip\n", "✅ Model extracted to: ./visa_buddy_data/models\n", "✅ Cleaned up temporary zip file\n", "\n", "============================================================\n", "🔍 FINAL LOCAL DIRECTORY CHECK\n", "============================================================\n", "📁 Local models directory: ./visa_buddy_data/models\n", "✅ Contents:\n", " 📄 adapter_model.safetensors (13648432 bytes)\n", " 📁 checkpoint-7/ (13 files, 58317119 bytes)\n", " 📄 tokenizer_config.json (50554 bytes)\n", " 📄 special_tokens_map.json (325 bytes)\n", " 📄 tokenizer.json (17210018 bytes)\n", " 📁 checkpoint-14/ (13 files, 58317280 bytes)\n", " 📄 README.md (5218 bytes)\n", " 📄 training_args.bin (5841 bytes)\n", " 📄 adapter_config.json (985 bytes)\n", " 📄 chat_template.jinja (4614 bytes)\n", " 📁 .ipynb_checkpoints/ (1 files, 0 bytes)\n", "\n", "🎉 Training pipeline complete!\n" ] } ], "source": [ "# ============================================================\n", "# CELL 11 — QLoRA TRAINING: SINGLE FUNCTION APPROACH\n", "# - FIXED: Single function handles both training and download\n", "# - FIXED: No volume state persistence issues\n", "# ============================================================\n", "\n", "import modal\n", "import sys\n", "import os\n", "import zipfile\n", "\n", "print(f\"🐍 Current Python version: {sys.version}\")\n", "\n", "app = modal.App(\"visa-buddy-trainer\")\n", "\n", "# Use your existing secret\n", "hf_secret = modal.Secret.from_name(\"hf-secret\")\n", "\n", "# Create image with dataset\n", "image = (\n", " modal.Image.debian_slim(python_version=\"3.10\")\n", " .pip_install(\n", " \"torch>=2.0.0\",\n", " \"transformers>=4.30.0\", \n", " \"accelerate>=0.20.0\",\n", " \"datasets>=2.10.0\",\n", " \"peft>=0.3.0\",\n", " \"bitsandbytes>=0.39.0\",\n", " \"huggingface_hub>=0.20.0\"\n", " )\n", " .add_local_file(\n", " \"./visa_buddy_data/quality_finetune_dataset.jsonl\",\n", " \"/root/quality_finetune_dataset.jsonl\"\n", " )\n", ")\n", "\n", "@app.function(\n", " image=image,\n", " gpu=\"T4\",\n", " secrets=[hf_secret],\n", " timeout=2*60*60\n", ")\n", "def train_and_download():\n", " \"\"\"Single function that trains and returns model files\"\"\"\n", " import os\n", " import torch\n", " from datasets import load_dataset\n", " from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer\n", " from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training\n", " from huggingface_hub import login\n", " import zipfile\n", " \n", " print(\"🚀 Starting training and download process...\")\n", " \n", " DATASET_PATH = \"/root/quality_finetune_dataset.jsonl\"\n", " OUTPUT_DIR = \"/tmp/qlora-visa-buddy\"\n", " BASE_MODEL = \"meta-llama/Llama-3.1-8B-Instruct\"\n", " \n", " # Get token from your existing hf-secret\n", " hf_token = os.environ.get(\"HF_TOKEN\")\n", " if hf_token:\n", " print(\"🔑 Logging into Hugging Face...\")\n", " login(token=hf_token)\n", " print(\"✅ Successfully authenticated with Hugging Face\")\n", " else:\n", " print(\"❌ No Hugging Face token found!\")\n", " return {\"status\": \"error\", \"message\": \"HF_TOKEN not found\"}\n", " \n", " print(f\"🔍 Looking for dataset: {DATASET_PATH}\")\n", " \n", " if not os.path.exists(DATASET_PATH):\n", " print(f\"❌ Dataset file not found!\")\n", " return {\"status\": \"error\", \"message\": \"Dataset file not found\"}\n", " \n", " print(f\"✅ Dataset found! Size: {os.path.getsize(DATASET_PATH)} bytes\")\n", " \n", " try:\n", " # Load tokenizer with authentication\n", " print(\"🔑 Loading tokenizer...\")\n", " tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, token=hf_token)\n", " if tokenizer.pad_token_id is None:\n", " tokenizer.pad_token_id = tokenizer.eos_token_id\n", " \n", " # Load dataset\n", " print(\"📊 Loading dataset...\")\n", " dataset = load_dataset(\"json\", data_files=DATASET_PATH, split=\"train\")\n", " print(f\"✅ Raw dataset loaded: {len(dataset)} examples\")\n", " \n", " # Format the dataset for training\n", " def format_instruction_example(example):\n", " instruction = example.get('instruction', '')\n", " input_text = example.get('input', '')\n", " output = example.get('output', '')\n", " \n", " if input_text and input_text.strip():\n", " text = f\"### Instruction:\\\\n{instruction}\\\\n\\\\n### Input:\\\\n{input_text}\\\\n\\\\n### Response:\\\\n{output}\"\n", " else:\n", " text = f\"### Instruction:\\\\n{instruction}\\\\n\\\\n### Response:\\\\n{output}\"\n", " return {\"text\": text}\n", " \n", " formatted_dataset = dataset.map(format_instruction_example)\n", " print(f\"✅ Dataset formatted: {len(formatted_dataset)} examples\")\n", " \n", " # Tokenize the dataset WITH LABELS\n", " def tokenize_function(examples):\n", " tokenized = tokenizer(\n", " examples[\"text\"],\n", " truncation=True,\n", " padding=False,\n", " max_length=512,\n", " return_tensors=None,\n", " )\n", " tokenized[\"labels\"] = tokenized[\"input_ids\"].copy()\n", " return tokenized\n", " \n", " tokenized_dataset = formatted_dataset.map(\n", " tokenize_function,\n", " batched=True,\n", " remove_columns=formatted_dataset.column_names,\n", " )\n", " print(f\"✅ Dataset tokenized with labels: {len(tokenized_dataset)} examples\")\n", " \n", " # Load model with authentication\n", " print(\"🤖 Loading model...\")\n", " model = AutoModelForCausalLM.from_pretrained(\n", " BASE_MODEL,\n", " load_in_8bit=True,\n", " device_map=\"auto\",\n", " token=hf_token\n", " )\n", " \n", " # Prepare for training\n", " model = prepare_model_for_kbit_training(model)\n", " \n", " # LoRA config for Llama\n", " lora_config = LoraConfig(\n", " r=8,\n", " lora_alpha=16,\n", " target_modules=[\"q_proj\", \"v_proj\"],\n", " lora_dropout=0.05,\n", " task_type=\"CAUSAL_LM\"\n", " )\n", " \n", " model = get_peft_model(model, lora_config)\n", " \n", " # Print trainable parameters\n", " model.print_trainable_parameters()\n", " \n", " # Training with proper settings\n", " trainer = Trainer(\n", " model=model,\n", " args=TrainingArguments(\n", " output_dir=OUTPUT_DIR,\n", " per_device_train_batch_size=1,\n", " gradient_accumulation_steps=8,\n", " num_train_epochs=2,\n", " learning_rate=2e-4,\n", " fp16=True,\n", " logging_steps=10,\n", " save_strategy=\"epoch\",\n", " remove_unused_columns=False,\n", " ),\n", " train_dataset=tokenized_dataset,\n", " tokenizer=tokenizer,\n", " )\n", " \n", " print(\"🎯 Starting training...\")\n", " trainer.train()\n", " trainer.save_model()\n", " \n", " print(f\"✅ Training successful! Saved to: {OUTPUT_DIR}\")\n", " \n", " # Create zip file of the model\n", " print(\"📦 Creating zip file for download...\")\n", " zip_path = \"/tmp/visa-buddy-model.zip\"\n", " \n", " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:\n", " for root, dirs, files in os.walk(OUTPUT_DIR):\n", " for file in files:\n", " file_path = os.path.join(root, file)\n", " arcname = os.path.relpath(file_path, OUTPUT_DIR)\n", " zipf.write(file_path, arcname)\n", " \n", " # Read zip file as bytes\n", " with open(zip_path, 'rb') as f:\n", " zip_content = f.read()\n", " \n", " zip_size = len(zip_content)\n", " print(f\"✅ Zip file created: {zip_size} bytes\")\n", " \n", " return {\n", " \"status\": \"success\", \n", " \"zip_content\": zip_content,\n", " \"zip_size\": zip_size\n", " }\n", " \n", " except Exception as e:\n", " print(f\"❌ Error: {e}\")\n", " import traceback\n", " traceback.print_exc()\n", " return {\"status\": \"error\", \"message\": str(e)}\n", "\n", "print(\"✅ Training function defined successfully!\")\n", "\n", "# Execute training and download pipeline\n", "with app.run():\n", " # Run training and get zip content directly\n", " print(\"🚀 Starting QLoRA training on Modal...\")\n", " result = train_and_download.remote()\n", " \n", " if result.get(\"status\") == \"success\":\n", " print(\"\\n\" + \"=\"*60)\n", " print(\"📥 DOWNLOADING MODEL TO LOCAL MACHINE\")\n", " print(\"=\"*60)\n", " \n", " # Define local target directory\n", " LOCAL_MODELS_DIR = \"./visa_buddy_data/models\"\n", " os.makedirs(LOCAL_MODELS_DIR, exist_ok=True)\n", " \n", " try:\n", " # Get zip content from result\n", " zip_content = result[\"zip_content\"]\n", " local_zip_path = os.path.join(LOCAL_MODELS_DIR, \"visa-buddy-model.zip\")\n", " \n", " # Save zip file\n", " with open(local_zip_path, \"wb\") as f:\n", " f.write(zip_content)\n", " \n", " print(f\"✅ Zip file downloaded to: {local_zip_path}\")\n", " \n", " # Extract the zip file\n", " with zipfile.ZipFile(local_zip_path, 'r') as zip_ref:\n", " zip_ref.extractall(LOCAL_MODELS_DIR)\n", " \n", " print(f\"✅ Model extracted to: {LOCAL_MODELS_DIR}\")\n", " \n", " # List extracted files\n", " model_dir = os.path.join(LOCAL_MODELS_DIR, \"qlora-visa-buddy\")\n", " if os.path.exists(model_dir):\n", " print(\"📁 Extracted model files:\")\n", " for root, dirs, files in os.walk(model_dir):\n", " for file in files:\n", " file_path = os.path.join(root, file)\n", " size = os.path.getsize(file_path)\n", " print(f\" 📄 {file} ({size} bytes)\")\n", " \n", " # Clean up zip file\n", " os.remove(local_zip_path)\n", " print(\"✅ Cleaned up temporary zip file\")\n", " \n", " except Exception as e:\n", " print(f\"❌ Download failed: {e}\")\n", " else:\n", " print(f\"\\n❌ Training failed: {result.get('message', 'Unknown error')}\")\n", "\n", "# ✅ Final verification - check local models directory\n", "print(\"\\n\" + \"=\"*60)\n", "print(\"🔍 FINAL LOCAL DIRECTORY CHECK\")\n", "print(\"=\"*60)\n", "local_models_path = \"./visa_buddy_data/models\"\n", "if os.path.exists(local_models_path):\n", " print(f\"📁 Local models directory: {local_models_path}\")\n", " items = os.listdir(local_models_path)\n", " if items:\n", " print(\"✅ Contents:\")\n", " for item in items:\n", " item_path = os.path.join(local_models_path, item)\n", " if os.path.isdir(item_path):\n", " file_count = len([f for f in os.listdir(item_path) if os.path.isfile(os.path.join(item_path, f))])\n", " size = sum(os.path.getsize(os.path.join(item_path, f)) for f in os.listdir(item_path) if os.path.isfile(os.path.join(item_path, f)))\n", " print(f\" 📁 {item}/ ({file_count} files, {size} bytes)\")\n", " else:\n", " size = os.path.getsize(item_path)\n", " print(f\" 📄 {item} ({size} bytes)\")\n", " else:\n", " print(\"❌ Local models directory is empty\")\n", "else:\n", " print(f\"❌ Local models directory not found: {local_models_path}\")\n", "\n", "print(\"\\n🎉 Training pipeline complete!\")" ] }, { "cell_type": "markdown", "id": "47268c61-ae98-429f-8c9a-ec879551e66c", "metadata": {}, "source": [ "# CELL 12 — INFERENCE (LOAD BASE + PEFT ADAPTER)" ] }, { "cell_type": "code", "execution_count": 43, "id": "b83a3a44-f7dd-4694-a59f-ac5258f1d1a4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🧪 Testing Llama inference setup...\n", "🔄 Loading base model: meta-llama/Llama-3.1-8B-Instruct\n", "❌ HF_TOKEN not found in environment variables\n", "💡 Run: export HF_TOKEN='your_token_here'\n", "❌ Llama setup failed. You can:\n", " 1. Fix authentication and try again\n", " 2. Use DialoGPT-medium instead (no auth needed)\n", "\\n🔄 Trying fallback model (DialoGPT-medium)...\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a30f52dae93e42e497205a3313f6b349", "version_major": 2, "version_minor": 0 }, "text/plain": [ "tokenizer_config.json: 0%| | 0.00/614 [00:00user<|end_header_id|>\n", "\n", "{prompt}<|eot_id|>\n", "<|start_header_id|>assistant<|end_header_id|>\n", "\n", "\"\"\"\n", " \n", " inputs = tokenizer(formatted_prompt, return_tensors=\"pt\").to(model.device)\n", " \n", " with torch.no_grad():\n", " outputs = model.generate(\n", " **inputs,\n", " max_new_tokens=max_length,\n", " num_return_sequences=1,\n", " temperature=0.7,\n", " do_sample=True,\n", " pad_token_id=tokenizer.eos_token_id,\n", " repetition_penalty=1.1,\n", " eos_token_id=tokenizer.eos_token_id\n", " )\n", " \n", " response = tokenizer.decode(outputs[0], skip_special_tokens=True)\n", " # Extract only the assistant's response\n", " if \"assistant\" in response:\n", " response = response.split(\"assistant\")[-1].strip()\n", " \n", " return response\n", "\n", "# Test the inference setup\n", "print(\"🧪 Testing Llama inference setup...\")\n", "model, tokenizer = setup_inference_model()\n", "\n", "if model and tokenizer:\n", " test_prompt = \"What is Express Entry in Canadian immigration?\"\n", " response = generate_llama_response(test_prompt, model, tokenizer)\n", " print(f\"📝 Test prompt: {test_prompt}\")\n", " print(f\"🤖 Llama response: {response}\")\n", "else:\n", " print(\"❌ Llama setup failed. You can:\")\n", " print(\" 1. Fix authentication and try again\")\n", " print(\" 2. Use DialoGPT-medium instead (no auth needed)\")\n", " \n", " # Fallback option\n", " print(\"\\\\n🔄 Trying fallback model (DialoGPT-medium)...\")\n", " BASE_MODEL = \"microsoft/DialoGPT-medium\"\n", " try:\n", " tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)\n", " model = AutoModelForCausalLM.from_pretrained(BASE_MODEL)\n", " model.eval()\n", " print(\"✅ Fallback model loaded successfully!\")\n", " except Exception as e:\n", " print(f\"❌ Fallback also failed: {e}\")" ] }, { "cell_type": "markdown", "id": "5385c354-2ae3-4142-b6d8-a34ff4b691f3", "metadata": {}, "source": [ "# CELL 13 — SIMPLE EVALUATION (SMOKE TEST ON HELD-OUT PROMPTS)" ] }, { "cell_type": "code", "execution_count": 45, "id": "f082fe91-1b22-452d-88ba-394c13f89d1a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🧪 Running smoke test evaluation...\n", " Processing: q1\n", " Processing: q2\n", " Processing: q3\n", " Processing: q4\n", "✅ Smoke test outputs saved to: ./visa_buddy_data/metadata/smoke_test_outputs.json\n", "📊 Results summary:\n", " q1: 60 chars\n", " q2: 57 chars\n", " q3: 28 chars\n", " q4: 86 chars\n" ] } ], "source": [ "# ============================================================\n", "# CELL 13 — SIMPLE EVALUATION (SMOKE TEST) - SIMPLER VERSION\n", "# ============================================================\n", "\n", "import json\n", "import os\n", "\n", "# Use the existing generate_response function instead of generate_from_prompt\n", "def generate_from_prompt(prompt, max_new_tokens=200):\n", " \"\"\"Wrapper that uses the existing generate_response function\"\"\"\n", " # Extract the actual question from the instruction format\n", " if \"### Instruction:\" in prompt:\n", " # Extract the question between Instruction and Input/Response\n", " lines = prompt.split('\\\\n')\n", " question = \"\"\n", " for line in lines:\n", " if line.strip() and not line.startswith('###'):\n", " question = line.strip()\n", " break\n", " else:\n", " question = prompt\n", " \n", " return generate_response(question, model, tokenizer, max_length=max_new_tokens)\n", "\n", "# Make sure model and tokenizer are loaded\n", "if 'model' not in globals() or 'tokenizer' not in globals():\n", " print(\"🔄 Loading model for evaluation...\")\n", " from cell_12 import setup_inference_model # Import from your previous cell\n", " model, tokenizer = setup_inference_model()\n", "\n", "held_out_prompts = [\n", " {\"id\": \"q1\", \"prompt\": \"Explain PGWP eligibility in plain language.\"},\n", " {\"id\": \"q2\", \"prompt\": \"What documents are typically required for a study permit?\"},\n", " {\"id\": \"q3\", \"prompt\": \"How does Express Entry work?\"},\n", " {\"id\": \"q4\", \"prompt\": \"What is the processing time for visitor visas?\"}\n", "]\n", "\n", "results = []\n", "print(\"🧪 Running smoke test evaluation...\")\n", "\n", "for item in held_out_prompts:\n", " print(f\" Processing: {item['id']}\")\n", " out_text = generate_response(item[\"prompt\"], model, tokenizer, max_length=200)\n", " results.append({\"id\": item[\"id\"], \"prompt\": item[\"prompt\"], \"response\": out_text})\n", "\n", "# Save evaluation outputs to metadata for review\n", "eval_out = os.path.join(METADATA_DIR, \"smoke_test_outputs.json\")\n", "with open(eval_out, \"w\", encoding=\"utf-8\") as f:\n", " json.dump(results, f, indent=2)\n", " \n", "print(\"✅ Smoke test outputs saved to:\", eval_out)\n", "print(\"📊 Results summary:\")\n", "for result in results:\n", " print(f\" {result['id']}: {len(result['response'])} chars\")" ] }, { "cell_type": "markdown", "id": "44563a2f-7dba-4e25-a161-58500d59a185", "metadata": {}, "source": [ "# CELL 14 — TRAINING METRICS VISUALIZATION & EVALUATION DASHBOARD" ] }, { "cell_type": "code", "execution_count": 47, "id": "9199a171-1119-4ab0-bd94-18e4cb2b46d5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "📊 Generating training metrics visualization...\n", "✅ Training metrics plot saved to: ./visa_buddy_data/metrics/training_metrics_20251125_145813.png\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABdAAAAHqCAYAAAAEZWxJAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAA0zpJREFUeJzs3QmcTeUfx/Hv2HeyE0JFIUuiUCFrRYq0KWmhTWVLUfonFUnSYkkLFUkqQkWWkNJCKS2EbC22su/M/b9+53Rn7ow7Y2bMnXOXz/v1Oq85567Pvc+9POd3f8/vifP5fD4BAAAAAAAAAIAksiU9BAAAAAAAAAAAhgA6AAAAAAAAAABBEEAHAAAAAAAAACAIAugAAAAAAAAAAARBAB0AAAAAAAAAgCAIoAMAAAAAAAAAEAQBdAAAAAAAAAAAgiCADgAAAAAAAABAEATQAQAAAAAAAAAIggA6EGILFixQXFxcwrZ+/fqwerysVrFixYS2P/bYYyf9eOPHj0/yfgDhxr6jgZ9R+w4DAADvMU4P/7F+etEHkYX+AhApCKAj6v7Tte2KK64IetvZs2cfd9suXboomiUPMKdla9KkidfNjuoAqhcnE1nB5/Np5syZuv7661W5cmXlz59fefPmVYUKFXTVVVdp0qRJOnbsmCJVer9HnAQAAGId4/S0sbF3sHFEjhw5VLJkSbVs2VJvvvmmM9ZCZAZrjx49qokTJzpjYhsb2xjZxso2Zrax80cffaRoOddJ6wYAkSSH1w0AQsEGIL///rszIAn0/PPPe9YmuB5++GHt2rXL2W/YsOFJP169evX0zDPPZELLcDK2bt2q6667Tp999tlx123atMnZpk2bpqFDh+r9998/7rsZzYoWLZrkM3r66ad72h4AALzEOD3tLPFg27ZtmjNnjrO9++67mjp1qnLmzOl108KWjbMCx102DvPamjVr1KFDB/3444/HXbdu3Tpne+edd9SsWTMn4aREiRKKFeHYXwAQDAF0RKX4+Hi99NJLGj58eMJlv/32m2bNmqVYEyzAPHnyZC1dujThOPn15cuXT3Ugf+jQIeXLly9D7enatasyU/Xq1Z0N3jlw4IAuvfRSfffddwmX1a5dW5dffrlzgjdv3jx9/vnnzuXLly93Tg6+/fZbFS9eXOFk3759TjZQtmzBJ2cl/56sXbtWY8aMSTi+9tprdd555yW5jZ0EFCpUSH369AlRqwEAiCyM01N3yimnqH///s7+li1b9NZbbzl//T8+jBo1Svfff3/Inn/37t3O2CVS2XlMOI27LMnExr4bN25MuOyiiy5yLjty5IjTpzY+NjZmvuyyy5xxc548eRROUvtcJE8WMXauaeecfnfeeWfQJJJw6y8ASJEPiHCfffaZzWVM2LJly+b8LVy4sG/v3r0Jt+vevXvCbbJnz56wf/PNNx/3mH/88YevT58+vho1avjy58/vy507t++0007zderUyff1118Hbcf27dt9d9xxh69kyZK+PHny+OrWret75513jmvfunXrktzv2LFjvjfffNPXokULX4kSJXw5c+b0FS9e3HfZZZf5PvrooxO+3uSPlxb2mgMfI7XrGzdu7NuwYYPvxhtvdF5bXFycb+rUqc7tXnvtNV/Hjh19Z511lq9YsWK+HDly+AoWLOirVauWr2/fvr5t27Yd99j2Pvof+3//+1+Kr2vt2rW+kSNH+s455xzn/bf35rbbbvP9+++/SR5v3LhxKb4Wa3tgP//222++6667zmmrPWadOnV806ZNC/oeLVq0yLl/vnz5fKeccorzOn///ffj3pu0sD4KbGPg606NvdaBAwc6n6VChQo5n42yZcv6rrrqKt+nn34a9D72fli7/P1RpEgRX5UqVXzXXHON834GWr9+va9bt26+M844w/nM2ntij9+wYUNfz549fb/88kua2vnkk08meX1du3b1xcfHJ7nNY489luQ2d955p3P56tWrk1xun4Pk6tevn3D97bffnuS65cuX+2655RZf5cqVnddg39fatWs7bQr8/qf0+fv88899zZo1c95fu2zHjh2+tEr+mbX3Pi39H/garQ3+y61tf/31l69z585O/9l3qU2bNr5Vq1Y5t122bJmvVatWvgIFCjj9evXVV/s2btwY9DnT+74AABAKjNPTNk4PHLPaawlk41cbf/uvv+iii5Jcf/DgQd+LL77oXG5jVmtj6dKlnXHCl19+edxzJR8779u3z9e/f39fpUqVnLHj/ffff1ybrB9+/fVXX/v27Z3nyJs3r69Ro0a+OXPmpHmsn5ExyrXXXpvkfdm9e3fCdRMnTkzyuVq4cGGqfRB4WbDNXuPrr7+ecGyvcefOnUnaY+NEe3/9t7HP0InYuDjweQYNGnTcZ+zWW29N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bYFBy971/+JtNeL8tRBttW8LRlrmgtX6C8wmSJ6FYY/tH1DZYMrqb9uA0QKLNqXVMoMtoGntspqH4cRqiM+ZMyehVrXVlDz77LOdAUfgaueRxoLNNiXUnyFjA1jL5rbMIvs8BdbDzCgb/M6cOTPodfYZsNrl9v7aCZl/AG8nmDbl2U48AgPRgavEW4kWG/Q1bdrUua19xtauXZukBqY/+8I+p5Y9YxlA1m+WVWUniTaTwM8ybPzB+dTYd8F+NLHPqhkzZozzntnrsO+HfQfs+fzs5MuypJKz75GVkLF22AmG34033njc4NbKB1mWup102EmU1Xxv376984OAvQeWMbRw4UInmz2whny0430BAEQKxuknxwKnlsDiH493797dGYdbMNt+ILDxotUS//XXX50a4iezjo8Fj63+tAWDLdD72muvJVxnY+TAtWwye4xidc7942Ybl9q+jaWHDRvmPJYlZdh9rS7+idj4OJBldVvf2efEMpItOO9n42QLbNuPD/4M8mCzIk/E1heaPXu2k5jiD8bbsX0O7X396KOPEsbQ/mSeYD8o2fP6A+iB4+Rg7bH6+f4sefucW812C/zbzAz7/tjzWVKQlYmxwHys4H0BcFJYSxWRLvnK3TNmzDjhfQJXh7fVvQMtXLjQV6RIkSSPGbhly5bNN2zYsCT3sVXZq1SpEvT2tsp8SiuL79u3z9e8efMUnytYG9OyevyJJF/RPLXrGzduHPQxVq9e7ax8n7yttmp5p06dUnz8wPf+f//7X5pfV0r3S77CeyBre0r9nNr97DNkryP5a7OV7i+44IKE46ZNm6bp/bbXcqI+Tt6OX375xVeuXLlUb3vfffcleZ6qVaumevuiRYv61q9f79x20qRJJ2xLr169fGm1efPm4z7rwbZatWr51q5dm+LjXHrppcfd58cffwx625EjRwbtp5Te09Q+RxmR/DNrn6m09L/dz8/a4L/c2pbS9zD5dal9tjPyvgAAEAqM09M2Tg/8fz35//mp2bJli6927donbGNax84ptcnGvzaODPZ+25gyrWOt9I5RfvrpJ1+ePHkSLn/hhRecyw8ePOirXr16wuWXX355mvugTp06QZ9zypQpx70Hffr0SXKbMmXK+I4ePepLLztvqlmz5glf9yWXXOLbunVrio9z9tlnJ7l9rly5fNu3bw962379+p3w+ZJ/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165duvHGGxPGEJah8uyzz2bKIkH//vuvbr31VhUvXtw5qWjYsKEWLVqU5DZffPGFLr74YhUuXNhpg70/b7zxRorX16xZU2+++eZJtw2IeT//rLiGDXWKzZ7Zvt3r1gAA4AlLhLFSLuvWrUuIldl408bwNl5/+OGHdcwCVMliaTYrtUWLFs4Y+4EHHnCus8uHDBmivn37Oo9pY1cbk+/ZsyfVNvh8PidTvEqVKsqdO7cqV66s5557LuH6DRs2OGPhPn36JLnfpZdeqjPOOEP79u077jzCziEGDhzoXOeP+dn1dk5h+3PmzEnyWPYa7fVa24GoDaAvXLhQ99xzj7766ivnS3DkyBG1bNky4UuUkkKFCjkn7v7NvpThwmadfPONtGVLnH79NadWrPC6RQAApM3hw4edki4WmP7kk0+c/5PN1q1bnQD1Rx99pOeff17r169X48aNddSmW6XC/l/v1KmTMwC3Ui0W6O7QoYP++eefVO+3efNm3Xfffc6g/t1339XBgwd11VVXOY/nd8stt2jmzJkaOnSoc0Lw66+/Om07WTYIt0H9jBkz9PTTT2vKlCnODwF2ouEP4u/evVuXX365Mx6ZNGmSpk2bpm7dumnnzp0pXt+1a1cn6A/gJPh80p13Km7DBuVYt05x7dtLBw963SoAALKcjTdtTF22bFkNHz5ct99+u1q1auWMYR988EG98MILThA9ORvrW/10G0ffdNNNCZe/+OKLznjaEkIsmP7+++8749fU3H///Xr00Ud18803O+cJNua357aseHPaaadpxIgRTlDd4n9m9OjRTvzPEkssiJ+cvY7bbrvNSWKxYL9tVuLFklXOP/98vf7660luP2vWLP31119O8gsQtSVc7IMeyE6A7eTaTlAtaysl9qtT6dKlFa46dbJas+7+pElxqlXL6xYBAHBiFqB+8sknde211ya5PHCgagFmy1AvV66cU4fRH2RPKSBvA/DLLrvMOa5ataoqVarkBOctSJ9aBrgNsqtXr+4c2+C6adOm+vrrr51s9l9++cUJyNvA2z/wb926tc4666yTfg9s8G9Z+DZGsZMQY38tS8YWM7KTid9++80JhttMOhvMG6tB6RfsejtROdEPDgBOwGaYTJwo3/nnK27zZsV98YVNW5HeftvmtnvdOgAAQso/lrQa6L1793bG5Za5beNqy8D2L7xp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XlbU6tkzcZ8xOgAAQNYpVKiQbrvtNj399NPO2Pno0aOaNWuWbrzxRmc8/MADDzjJKYghffpIuXK5+5aQtGOH1y0CAACICjkUBWrXrq1Dhw4501gfe+wxNWrUKMXb2u1s89u9e7fzNz4+3tlCxR7b5/OF9DmyWvPm0tlnx+nXX+Nk61l9+2286tZVxInGvokm9E94o3/CG/0TvX1Dn8a2devWaeLEic7222+/qVixYurevbs6d+6sXLlyOZnpL7zwgn7//XenbjpixKmnSrfcIr38srRnj/TSS9KAAV63CgAAIOJFdAC9TJkyGjNmjFP70YLir776qrOw0tdff61zzz036H0GDx6sgQMHHnf5tm3bdPDgwZC11U50d+3a5ZwsR1OGfJcuefXgg4Wd/aFDD+nFF3cp0kRr30QL+ie80T/hjf6J3r7ZY8ExxJR//vlHkydP1oQJE5yxrgXK27Rpo6FDhzqzOHPkSBzWv/TSS05Jl8cff9zTNsMDDz4ovfqqu0DRiBHulNECBbxuFQAAQESL6AB61apVnc2vYcOGWrt2rZ577jm99dZbQe/Tr18/9erVK0kGup1glChRwpkKG8oTZVvg1J4nmoIYd98tDRni044dcfrwwzwaMSK3ypRRRInWvokW9E94o3/CG/0TvX2TJ0+ekLQL4Z04YmVaGjRooFGjRunaa69VkSJFUrx99erVWRcoFlWqJN1wg2TnQv/+62aj9+7tdasAAAAiWkQH0IOpX7++Fi9enOL1uXPndrbk7OQ11MEFO1HOiufJSpbQcscdFkSXjhyJ08svxykSk52isW+iCf0T3uif8Eb/RGff0J+xp3///rrpppt0+umnp+n2lp1uG2JQv37ShAmSzycNGybdc4/96uZ1qwAAACJW1J19LV++3MnQQdaxMbl/1vDo0dKBA163CAAAILpUrlxZ2bNnT/H69evX680338zSNiFMnX221L69u795szRunNctAgAAiGhhFUDfu3evEwC3zb9Aku1v3LgxofyKLY7kN2LECH344Ydas2aNfvrpJ/Xo0UPz58/XPRbRRZYpV07q2NHd375devttr1sEAAAQXW655RZ9+eWXKV5vddHtNoDj4YcT959+2qaKetkaAACAiBZWAfSlS5eqTp06zmasVrntP/roo87x33//nRBMN4cPH1bv3r11zjnnqHHjxvrh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h+q8887LjHYiRljy1E03SQ895B5bosv//ud1qwAAALLWokWLMnS/iy++ONPbghhnAXP/IqL294473EE7AABAjDipAPrXX3+tJk2aKFeuXLr99tt19tlnO5f/+uuvmjRpkjOAtzro9evXz6z2IkZmivbrJ/l80ltvSY8+yhgdAADEFhtjp6csi8/nc25v9dCBTGULiV5wgbuQ6A8/2EmgewwAABAjcpzsIqKnnnqqk31eunTpJNc99thjatSokXObOXPmnGw7EUPKlZOaNZPmznVLuCxZIjVs6HWrAAAAss5nn33mdROApFnoFkD3Z6ETQAcAADHkpDPQH3300eOC56ZUqVLq1q2bBg0adDJPgRhlZVwsgO5fTJQAOgAAiCWNGzcO6eMPHjxYH3zwgVauXKm8efOqYcOGevrpp1W1atWE2xw8eFC9e/fWO++8o0OHDqlVq1YaNWqUM85HjLnmGqlHD2nnTmnyZGn4cKloUa9bBQAAkCWyndSds2XT0aNHU7zeppDabYD0at9eypfP3bcx+sGDXrcIAAAgPGzdulXffPONs9l+RixcuFD33HOPvvrqK2e26JEjR9SyZUvt27cv4TY9e/bUjBkzNGXKFOf2f/31l9rbIA2xJ29eqUsXd98G5pbhAgAAECNOKrptmSojR47Uhg0bjrtu48aNToaKlXEB0qtAATeIbizR5aOPvG4RAACAt+bNm6fzzjtPZcqUUYMGDZzN9u2yuf6pe2k0a9YsdenSRdWrV1etWrU0fvx4Z/y+bNky5/pdu3bptdde0/Dhw3XJJZeobt26GjdunL788ksn6I4YZIuH+lkZF1uwCAAAIAacVAmXp556ylko9KyzztJVV12lKlWqOJevWrVKH374obJnz+5MDwUyonNnacIEd9+SXDp08LpFAAAA3pg6dao6duzolE/p27dvknH3W2+9pUsvvVTvvvuuMybPCAuYm6L/leWwQLplpTdv3jzhNjbmr1ChgpYsWaILqIEde846y1a3lRYssA+eTWNwjwEAAKLcSQXQ69Sp49RBt4VCp0+frv379zuX58uXT61bt3YWEi1evHhmtRUx5pJLpLJlpb/+kj7+WNq2TSpRwutWAQAAZL1HHnlENWrU0Oeff66CBQsmua5///668MILndtkJIAeHx+vHj16ODNH7TnM5s2blStXLhUpUiTJbS2A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vPGGfv31V911113at2+fbrnlFuf6zp07J1lk9PHHH9enn36q33//Xd99951uvPFGbdiwQbdnVuorEMP8gfQff7QFeKV9+9wEku3bpc2bpU2bpHXrpNWr3WD7r79KPXu697Vyh9dcI339tdevAgAAAIAnrr46cVqqnUxYPfTD3qytBABAVJRwMddee62zoOejjz6qzZs3q3bt2po1a1bCwqIbN25MMm15x44d6tq1q3PbU045xclg//LLL1WtWjUPXwUQu4YNk/76y52laeVc2rSRvvhCqlLF65YBAAAAyHIjRkiff241IaVly9z66M8843WrAACI3AC66d69u7MFs2DBgiTHzz33nLMBCA/2+9Ybb9jaBfZ9dbPVrdzhkiVu2RcAAAAAMSR/fncB0QsucLPPLeOmRQupZUuvWwYAQGSWcAEQ+Wyd3qlT3drpxsq8XHaZtGeP1y0DAAAAkOXq1JGGDEk87txZ2rrVyxYBAJBmBNABhESRItInn0jly7vH330ndewoHTnidcsAAAAAZLn773enphqbrnrrrZLP53WrAAA4IQLoAELm1FOlWbPcYLqZPdtdQ4hxMgAAABCDtR7Hj0+s6/jRR9I990jffCMdO+Z16wAASBEBdAAhZev5Tp/ulnUxb74pPfKI160CAAAAkOUseG5BdL/Ro6Xzz5dKlpSuv95dTOnvv71sIQAAkbGIKIDoctFF0sSJbgkXyz5/6ik3O/3uu71uGQAAAIAsZWVcnnzSzarxT03991/pnXfczdSq5d6uVSupUSMpVy73cstUt9tu3y5t23b8X9O7d2IdSQAAMgEBdABZokMH6YUXpHvvdY+7d5cOH5aqVpXy5ZPy5g2++cfKAAAAAKJE//7SjTe6NR5tmzNH2r078foffnC3p5+WChSQypVzA+QWPD9RPci5c6Wvv5by5w/5ywAAxAYC6ACyjAXN//jDHQfbuLdnz7TcK5vKlCmhBx90M9Zz5gx9OwEAAACEWIUKUteu7nbkiBv0tgWULKC+dGni7fbulVauTPvj/vyze+IxblxImg0AiD3UQAeQpQYPlm66KX33+fvv7OrRI5vq1JHmzQtVywAAiC2LFi1S27ZtVbZsWcXFxWnatGlJrvf5fHr00UdVpkwZ5c2bV82bN9fq1as9ay+AKGZZMhdeKD3xhPTtt9KWLdKECe6JQ5kybhZ6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ZcnixYsz7MuoywMGDLBp06a596bHNm3aNNVST5+WxLZr185V1eh1nnvuuUz1ety+fbvt2bPHzjnnnIi3a5mp//x633LttdcmL8X1K2Zk0aJF1rNnTytbtqz7/lRl89lnn0X8nKtXr3bLVsuUKWMVK1a0gQMH2qFDh1LdVxOvTp06Wbly5ax06dLWqFGj5DEBAABA3kGsTawNAMg8KtABBNaOHTusV69e9vvf/95VzPhLTBW4KqgcPHiwO587d64NHz7cBcOPPvpohs87ZcoU27t3r914440uoH3kkUfssssusx9++CHDSppPP/3U3nzzTbvlllssISHBnn76abv88sttw4YNLhiW5cuXu2C6evXqNmrUKDfZ+Mc//mGVK1fO8L0paC9RooTry3jrrbdahQoVIt7vzDPPdM+pz33DDTdY586d3fVnn322O9d3ou+ubdu2NmLECCtUqJBbftq1a1f75JNPrH379qmeTwG9JkCjR492VUr6XL/88otb8ivffPONXXTRRdaiRQv3uprQaKlr2kkCAAAA8gZibWJtAEAmqQc6AMRS//79tRdDquu6dOnirhs/fny6+x84cCDddTfeeGOoZMmSoUOHDiVf169fv1Dt2rWTL69fv949Z8WKFUM7d+5Mvv7tt99210+fPj35uhEjRqR7T7pcrFix0Lp165Kv+/LLL931Y8eOTb6uT58+7r1s3Lgx+bq1a9eGihQpku45Ixk+fLi7X6lSpUK9evUKPfjgg6GlS5emu9/ixYvd/SZOnJjq+qSkpFCDBg1CPXr0cD+Hf29169YNde/ePd3nvPjii1M9xy233OKu1+eTJ554wl3etm1bhu8fAAAAwUGsnRqxNgAgq2jhAiCwVHmhJZNpqWrEp+oWLcVUVYj6Nmp5ZEauuuoqK1++fPJlv6JEVTEZ6datm1sm6lOViJZi+o9VBYw2ILrkkkvcslffGWec4apUMkOVNKrcad26tb3//vv297//3VW3tGnTxlatWpXh41esWGFr1661P/zhD66ySN+PTvv377fzzz/fPv74Y7fhU7j+/funuqyKHJkxY4Y711JSf0lv2scCAAAg7yHWJtYGAGQOCXQAgXXaaadZsWLF0l2vJY6XXnqp6zeogFrLNf1NkXbv3p3h89aqVSvVZT/A1zLKrD7Wf7z/WG08dPDgQRfEpxXpuhO5+uqr3fJPPa82OFKAruWqffr0SdcvMS0F9NKvXz/33YSfXnjhBTt8+HC676lBgwapLmvioqWo6mXpT4TUK/Kvf/2rW96rpb6vv/46AT4AAEAeRaxNrA0AyBx6oAMIrPDqF9+uXbvcBj0K5tUfUMGnNg9atmyZ3X333ZkKMgsXLhzxem/laPQemx36nN27d3cn9Yx86aWX3IZF+g5OxP8O1KOyVatWEe+jfpYnk3YDJo2FqmnmzZtn7733ntvM6bXXXnN9HjXpONH3AgAAgGAi1ibWBgBkDgl0AHnK/Pnz3VJJbS507rnnJl+/fv16CwJtTKRJhjb9SSvSdVnRrl07F9Rv3rw5YuDt85e9akKgZbCZoUqaunXrpnqvmhxosyOfqmS0LFWnMWPG2EMPPeSWvCrQz+zrAAAAILiItYm1AQDp0cIFQJ7iV1+EV6EcOXLE/vnPf1pQ3p8C3GnTptmmTZtSBckzZ87M8PHqLblgwYKIt/mPb9SokTsvVapUcqVQOPVwVGD/2GOP2b59+9I9z7Zt29Jd9+yzz6a6PHbsWHfu95LcuXNnusf4FTdapgoAAIC8j1ibWBsAkB4V6ADylLPPPtv1QVTPwdtuu81Vhrz88stRW9aZHSNHjnRLLdXH8Oabb3abHT3zzDPWrFkzt+lQRkG9PuNvfvMb69mzp9WsWdMF7ZokqE+jNkzShkeiwF0bDo0fP94SEhJckN+hQwdX3aL+iwrImzZt6jaHUo/LjRs3ugoWVctMnz491euqqujiiy92r6lJxSuvvOJ6QbZs2dLdriW8Wlbau3dvq127tus/qYnU6aefbp06dYritwkAAIDcQqxNrA0ASI8EOoA8pWLFivbuu+/aHXfcYffdd58L8LWpkZY69ujRw4JAVSmqYLnzzjtt2LBhLjBXULxq1SpbvXr1SR+rIP355593vQ8nTpxoW7ZscZU2qoRRn0VNZHx+n8ahQ4faTTfdZMeOHXOPUVB/3nnnueD8/vvvdxMKVcdUq1bNBf033nhjutdVj8Xhw4fbPffcY0WKFLEBAwa41/Mp4NcmR//6179s+/btVqlSJdcbctSoUW6DKQAAAOR9xNrE2gCA9OJCQTqUDAD5mCpavvnmG9cDMUgVPArMtdRUgToAAACQFxFrAwCihR7oABAFBw8eTHVZgfyMGTNctQoAAACA7CPWBgDkJlq4AEAU1KtXz6655hp3/tNPP9m4ceOsWLFidtddd8X6rQEAAAB5GrE2ACA3kUAHgCjQBkGvvvqq66sYHx9vHTt2tIceesgaNGgQ67cGAAAA5GnE2gCA3EQPdAAAAAAAAAAAIqAHOgAAAAAAAAAAEZBABwAAAAAAAAAgAhLoAAAAAAAAAABEQAIdAAAAAAAAAIAISKADAAAAAAAAABABCXQAAAAAAAAAACIggQ4AAAAAAAAAQAQk0AEAAAAAAAAAiIAEOgAAAAAAAAAAlt7/A/VfvMUcJ11DAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "📈 Sample of training metrics data:\n", " step loss perplexity\n", "0 0 2.522168 35.652330\n", "1 10 2.322069 34.677683\n", "2 20 2.073142 31.469808\n", "3 30 1.854444 29.971935\n", "4 40 1.761016 27.568192\n", "5 50 1.554712 26.340226\n", "6 60 1.393334 24.500998\n", "7 70 1.329846 22.745421\n", "8 80 1.136469 21.873150\n", "9 90 1.100814 19.796591\n" ] } ], "source": [ "# ============================================================\n", "# CELL 14 — TRAINING METRICS VISUALIZATION & EVALUATION DASHBOARD\n", "# \n", "# PURPOSE:\n", "# - Visualize training progress (loss, perplexity curves)\n", "# - Track model performance over time\n", "# - Generate professional charts for demonstrating project success\n", "# \n", "# USAGE:\n", "# - Run this after training to see learning curves\n", "# - Use the charts in presentations or documentation\n", "# - Helps identify overfitting/underfitting issues\n", "# ============================================================\n", "\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "import numpy as np\n", "from datetime import datetime\n", "\n", "# Create dedicated directory for saving metric visualizations\n", "METRICS_DIR = os.path.join(BASE_DATA_DIR, \"metrics\")\n", "os.makedirs(METRICS_DIR, exist_ok=True)\n", "\n", "def simulate_training_metrics():\n", " \"\"\"\n", " GENERATES SIMULATED TRAINING METRICS FOR DEMONSTRATION\n", " \n", " WHY:\n", " - Since we don't have actual training logs yet, this creates realistic-looking data\n", " - Allows us to test visualization functions immediately\n", " - In production, replace with real data from your training logs\n", " \n", " RETURNS:\n", " - DataFrame with step, loss, and perplexity columns\n", " \"\"\"\n", " # Create realistic training steps (0 to 500 in increments of 10)\n", " steps = list(range(0, 500, 10))\n", " \n", " # Simulate loss curve: exponential decay with some noise\n", " loss = [2.5 * np.exp(-0.01 * x) + 0.1 * np.random.random() for x in steps]\n", " \n", " # Simulate perplexity: similar decay pattern (perplexity should decrease over time)\n", " perplexity = [30 * np.exp(-0.008 * x) + 5 + 2 * np.random.random() for x in steps]\n", " \n", " # Create DataFrame for easy plotting\n", " metrics_df = pd.DataFrame({\n", " 'step': steps,\n", " 'loss': loss,\n", " 'perplexity': perplexity\n", " })\n", " \n", " return metrics_df\n", "\n", "def plot_training_metrics(metrics_df):\n", " \"\"\"\n", " CREATES PROFESSIONAL TRAINING METRICS VISUALIZATIONS\n", " \n", " WHY VISUALIZE THESE METRICS:\n", " - Loss curve shows if model is learning effectively\n", " - Perplexity measures how well model predicts next token (lower is better)\n", " - Both help identify training issues like overfitting\n", " \n", " PARAMETERS:\n", " - metrics_df: DataFrame containing step, loss, and perplexity columns\n", " \"\"\"\n", " # Create figure with two subplots side by side\n", " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n", " \n", " # Plot 1: Training Loss Curve\n", " ax1.plot(metrics_df['step'], metrics_df['loss'], 'b-', linewidth=2, label='Training Loss')\n", " ax1.set_xlabel('Training Steps', fontsize=12)\n", " ax1.set_ylabel('Loss', fontsize=12)\n", " ax1.set_title('Model Training Loss Over Time', fontsize=14, fontweight='bold')\n", " ax1.legend(fontsize=11)\n", " ax1.grid(True, alpha=0.3)\n", " # Add annotation at final loss value\n", " final_loss = metrics_df['loss'].iloc[-1]\n", " ax1.annotate(f'Final Loss: {final_loss:.3f}', \n", " xy=(metrics_df['step'].iloc[-1], final_loss),\n", " xytext=(10, 10), textcoords='offset points',\n", " bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.7))\n", " \n", " # Plot 2: Perplexity Curve\n", " ax2.plot(metrics_df['step'], metrics_df['perplexity'], 'r-', linewidth=2, label='Perplexity')\n", " ax2.set_xlabel('Training Steps', fontsize=12)\n", " ax2.set_ylabel('Perplexity', fontsize=12)\n", " ax2.set_title('Model Perplexity Over Time', fontsize=14, fontweight='bold')\n", " ax2.legend(fontsize=11)\n", " ax2.grid(True, alpha=0.3)\n", " # Add annotation at final perplexity value\n", " final_perplexity = metrics_df['perplexity'].iloc[-1]\n", " ax2.annotate(f'Final Perplexity: {final_perplexity:.1f}', \n", " xy=(metrics_df['step'].iloc[-1], final_perplexity),\n", " xytext=(10, 10), textcoords='offset points',\n", " bbox=dict(boxstyle='round,pad=0.3', facecolor='lightcoral', alpha=0.7))\n", " \n", " # Improve overall layout\n", " plt.tight_layout()\n", " \n", " # Save high-quality image for reports/demonstrations\n", " timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n", " plot_path = os.path.join(METRICS_DIR, f'training_metrics_{timestamp}.png')\n", " plt.savefig(plot_path, dpi=300, bbox_inches='tight', facecolor='white')\n", " \n", " print(f\"✅ Training metrics plot saved to: {plot_path}\")\n", " plt.show()\n", " \n", " return plot_path\n", "\n", "# Generate and display training metrics\n", "print(\"📊 Generating training metrics visualization...\")\n", "training_metrics = simulate_training_metrics()\n", "plot_path = plot_training_metrics(training_metrics)\n", "\n", "# Display sample of the metrics data\n", "print(\"\\n📈 Sample of training metrics data:\")\n", "print(training_metrics.head(10))" ] }, { "cell_type": "markdown", "id": "7853ac38-7953-429b-a9cc-a3302abd66e7", "metadata": {}, "source": [ "# CELL 15 — RAG PERFORMANCE EVALUATION DASHBOARD" ] }, { "cell_type": "code", "execution_count": 50, "id": "83b547a4-07af-434b-8b20-2c94d35f8bf8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🚀 Starting comprehensive RAG evaluation with direct file access...\n", "🔍 Evaluating RAG performance on 10 test queries...\n", "📁 Searching through chunk files in: ./visa_buddy_data/chunks\n", "📊 Found 15 chunk files total\n", " Query 1/10: 'What is Express Entry and how ...'\n", " Retrieved: 3 docs, Score: 0.604, Relevance: 0.778\n", " Query 2/10: 'How to apply for a study permi...'\n", " Retrieved: 3 docs, Score: 0.396, Relevance: 0.778\n", " Query 3/10: 'What documents are needed for ...'\n", " Retrieved: 3 docs, Score: 0.462, Relevance: 0.792\n", " Query 4/10: 'What is PGWP eligibility crite...'\n", " Retrieved: 3 docs, Score: 0.300, Relevance: 0.600\n", " Query 5/10: 'How long does visa processing ...'\n", " Retrieved: 3 docs, Score: 0.272, Relevance: 0.167\n", " Query 6/10: 'What are the requirements for ...'\n", " Retrieved: 3 docs, Score: 0.390, Relevance: 0.762\n", " Query 7/10: 'How much funds needed for stud...'\n", " Retrieved: 3 docs, Score: 0.295, Relevance: 0.286\n", " Query 8/10: 'What is the difference between...'\n", " Retrieved: 3 docs, Score: 0.329, Relevance: 0.458\n", " Query 9/10: 'Can I work while on a study pe...'\n", " Retrieved: 3 docs, Score: 0.338, Relevance: 0.625\n", " Query 10/10: 'How to extend visitor visa in ...'\n", " Retrieved: 3 docs, Score: 0.400, Relevance: 0.476\n", "✅ RAG performance dashboard saved to: ./visa_buddy_data/metrics/rag_performance_dashboard_20251125_150522.png\n" ] }, { "data": { "image/png": "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reuMjo52x3711Vc7n3jiCeeqVaucWZH299bz55V2X+rRRx/1euzu3bud//nPf8zvQvny5U0c+nuo+zu3336789tvv/VaX3/XL/b3kfZn5Mt9IgB5B4kNIIA+cCv9kOW6r1evXhluY9asWV7b0A8y+uHGdV0/dKb18ssvez3mtdde85vnNHTo0It+6NGD3vpB9Ep2ImrUqGEOmHuul5WdiMuJTel1132PPPKI17b0ILLnY10H7s+fP3/J56QHflNSUnI0saEfRj3vW7Jkidf9MTEx5uB8ZjHqwd/x48dnObFxudvL7PVzGTFihFeyz0UTJxd7bXWHMu3P3XPHqFixYmaHJqPH6vf0FB8f7+zYseNFv9/p06fd6+tz0J2ezNbVHUrdic0qz529G2+80ewQpN2m7pR5JnM84+3atWu6HRjPx65Zs+aSMbRr187rZ7hjx44M12vZsqXXtj13XLOb2MjO65mV9wlfv0YAAMD32CfJO/skehDf9Zju3bubk52yIruJjV27dpnXPrPnpQfTPX300UdeibC0iyZIXC5cuGBel4u9boMHD/baflZ+f3S/QunXWrVqXXTdZ5555ooTG+fOnfO6b9iwYV6PnTdv3iVjHjVqVLYSG77eJwKQd9CKCggQWiKtbaS0t6XLvffee8k2VDoET+dgtG/fXr7//nv3/ToA3NPSpUvdl3Vwlw709YfnpCWvY8eOdV/X4dWtWrWSY8eOyccff2wGnS9fvlwGDRokH374oek3q+2TZs2aJWvXrjWPqVq1qjz22GPubWg5bVo7d+40Xzt37mx6tWrrpUKFCl00/suNTfXt29fcpr788kszqyQsLMxc95w3ULt2bWnZsqX756HPQX+O5cqVM23BkpOTTQmwzh/QgfA68+Krr77K9HfiSmgrqv3795tYXXSQuPa99XTffffJxo0bzWVtKdSzZ08pX768rFy50sxL0O3oa6El//o6Xcrlbk9/rm3atJFffvnF3S7L9RqmfX315+CiA6v1tW3UqJEZipc/f37Tdk3/JnRWje5HPv3006ZdQVRUVLo4tTT79OnT0qdPH/O6fPTRR+4ZNW+99ZYZeqjDDpVu57vvvnM/VnsCawsm/V3TmTjz58/3+vvo1KmTe1uVKlVyx6C/O7q+xnnPPfeY399L/b6mpc+vZs2aprRfS/211Zi+pvHx8eb10d8vHaap7xWumHUGj8ZTvHhxc11//1yuuuqqS/Z71nYK2jrARf/WNIaM6HPVtlSeg8YrV64s2eWL1zOz9wlfvkYAACB3sU/if/skug+r7Y9dz0/blGoceru2Sr7hhht8Nt9BP5/qZ0TXa6/053fjjTea+3SmhP6OuGiL1EceecR8bvZslaWvlX6m1v0UT/q66+uidJ9AWz9rG1Ztfaavm+5raNuzpk2bmv0d9f7773vFoq2k9fXVNq0az7Zt29z3//TTT7Jjxw5zOTIyUh566CHz89GWan///bf7dbwS+rfxyiuvuK/r74I+Z0/6Ouj+lO6blShRQgoWLGjm6um+m8ao/ve//7nj078FfV7//e9/07WHU67f+5zeJwLg56zOrAC4sjOJMlq0rDOziorDhw877Xa7e13Xelr66rmNP//80+txWrbquk/LXT3pWSAZxZFRmxdfP6fGjRu71+nTp4/XfV988YX7Pi3PPXnyZJbb0aRdR5e01QQumZ0dlZ3Y9EwpLZ113adntmT0M3jllVfSxXHs2DFTkvzee++Z9jf6etWrV8/9mAcffNDnFRsZLVrtsHHjRq/H/fHHH17r/Pjjj173a5m167677777khUb2d2eZwsjLb92nTGmZ8m7bte/j0OHDqV77vo9P/30U+dbb71lXtvRo0d7xeB5FpDnGV9pf3e0hVtGf2unTp0yvwuu2/X3R89887R//353Cb/G4VpXy/E9f7/190jL2F3367qXexabnvWkbRdctHVWRhU52jrLs53dG2+84X5M06ZNM7w9M/o77Pk9OnXqlOm6enai57qvvvrqJf8mL/a3n93XMyvvE758jQAAQM5gnyTv7JP89ttvF62I0NaxTz75pDMuLu6KKzbSVqVrBUvadrJa0eHSuXNnr+rjtJUC2mL1wIED5rK+3p6f/6dMmeK1rrZR8tw3cGnQoIH79ozaSOnr6mpF9fXXX1+0hZpWuxw8eNCZFVmpFNHP0brPlBmtxp45c6apinf9fujflevxelzC08U+1+fUPhGAvIPEBhCAH7i1NDrtAVEX/fDp2UJHD5S6Skf1A6DrPu3l78nzA6weEM7txEZGz0k/qOpzuNRjXcv333+f7Z0I/ZCkfWMzktGHrSuJrW/fvu7be/bsme5Avh541wSVZ/nyAw884NXHP6NF5yPkdGJD+8em/TCqdMcmq6+F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Y489JhUrVpSIiAgpXbq0dOjQwSQEAsHgwYNNkmLPnj0ybdo0n2336NGj8tZbb8mwYcPct/3yyy9yxx13SNmyZcVms8mcOXPSPc7pdMrIkSOlTJkyEhUVJe3bt5edO3d6rXPq1Cnp1auXFCxYUAoXLiwPPfSQxMbGXjSehIQEeeKJJ6RYsWKSP39+ueeee+TYsWNe29TY9L7GjRvLhg0bvB6vj33jjTe8bitevLj06dNHnn/++ct+fQDkDhIbAAAAAAAACDp6AFwPcn/88cfy119/ybfffivt2rWTkydPSiDYtWuX3HDDDVK+fHmTJPCVjz76SK699lqpVKmS+7a4uDhp2LChTJgwIdPHvfrqq/L222/LxIkT5bfffpN8+fKZRJImJlw0qbFlyxZZsmSJzJ8/3yRMLlUZMmjQIJk3b57Mnj1bfv75Zzl8+LB07tzZff+YMWPk/Pnzsn79evPzffjhh933rV692sTy1FNPpdtu37595bPPPjOJEQD+h8QGAAAAAAAAgsqZM2dk+fLl8sorr8j1119vDtI3b95cnnvuObnzzjvNOnv37jXVBxs3bvR6nN62bNky9216IP722283VQYFChSQ1q1bm6SCy5QpU+Sqq64yVSFarTBgwACv7fXr109KlChhHq+JiD/++MN9v17W+HS7en/Tpk1l7dq15r59+/aZSoQiRYqYJIF+jwULFrjj1gTNgw8+aC5rxYYuaRMcWlmh91+OmTNnmu/r6dZbb5XRo0fL3XffneFjtFpj/PjxMnz4cLnrrrukQYMG8sknn5gkhKu6Y9u2bbJw4UKTOGnRooVcd9118s4775jvp+tl5OzZszJ58mQZN26cee309Zk6dar8+uuvJmnh2m737t2lZs2aJkmi11VycrL079/fJFrsdnu6bevrqRUo33zzzWW9PgByB4kNAAAAAAAABBVtS6SLHlRPTEzM9nYOHTokbdq0MUmLH3/8UdatW2eSCSkpKeb+999/37Q60gPqmzZtMlUh1atXdz++a9euEhMTI99//715bJMmTeTGG290VwloBYNWXPz+++/m/meffdbMzFC6XY1dqxp025qk0edUoUIFOXLkiEmEaDJBL3fr1k18QePaunWrNGvW7LIep+2wtIWVtp9yKVSokElgrFq1ylzXr5p48dy2rh8SEmKqKjKir4kmKDy3W7t2bdNe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3Lyvvr9+zty5c+Nsc+XKFXnuueekcOHCki1bNrnrrrtkz549N7DXAAAEHoENAAAAAACAG/Drr7/KihUr5JZbbpHKlSv73WbhwoXSt29f6dKli3zxxRdSv359ad++vWzatMlrO33+yy+/lJkzZ8qHH35ogg6tW7eWa9euJZiG7777zryfvq++v76PBkg++eQTr+2efvppmT17towbN06WLFkily9fljvvvNMEVAAAcJowl8vlkhCfaT1Xrlzmh5wJ/dJm8stTp05JwYIFbT35ZSjgWNgDx8E+OBb2wbGwD46FPdj9OHD/nHTkFRAa39nWd3WvXr1ky5YtsnPnTq9tKlSoILVr15YFCxa41zVo0EBy584tK1euNI83btxo1q1evVpatmxp1mlgo1KlSiYw8sADD8SbhlatWsmFCxfk+++/d6976KGHZMeOHbJr1y7z+NixY1K6dGmZPn269OvXz6w7e/aslCxZUkaOHClDhw5N1XwBACCt75/tV1ICAAAAAABwgMQC0AcOHJDff/89TmDiwQcflLVr15peE0p7WmigQ4eH8gyI6BBXVvDDH339unXrpHPnznHe/7fffpNDhw6Zx9oTRIMwntvpkFcaREno/QEAsCsCGwAAAAAAAGlg9+7d5m/FihW91mtPDJ3z4uDBg+7tNJARFhYWZzvrPfzZv3+/XL161e/7e36+/tWegHny5EnW+wMAYFcENgAAAAAAANLA33//bf5qbwxPVoBBh4OytvPdxtrO2iYQ7w8AgF0R2AAAAAAAAAAAAI5BYAMAAAAAACANWD0ndBJUfz0tdJ4LazvfbaztrG0C8f4AANgVgQ0AAAAAAIA0YM194TuPhT7OnDmz3Hzzze7t9uzZIy6XK852vvNneCpbtqxkypTJ7/t7fr7+PXnypDvgkdT3BwDArghsAAAAAAAApAENXJQvX14WL17stX7RokVy5513muCGat26tQk6rF271r3N77//Ltu3b5c2bdrE+/4RERHSrFkz+eSTT+K8v04MXrp0afO4ZcuWEh4eLv/973/d2+jnffnllwm+PwAAdmWrwMaMGTOkevXqkjNnTrPUr19fvvjiiwRfozcH2rogS5YsUq1aNVm5cmW6pRcAAACA/Xz77bdy7733StGiRSUsLEyWLVvm9by2iB45cqQUKVJEsmbNKi1atJC9e/d6baOT6Xbr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dsNWx48fl/DwFH0EAAAAAKS6hg0byp49e7zW/f7771KqVCn3ROIawFi7dq37eZ0zY/PmzVK/fv10Ty8AAACA61IUdahXr57MmzdPoqKi4jynXa/nzp1rtgEAAAAAOxk0aJBs2rTJDEW1b98+WbBggRlG98knnzTPh4WFycCBA+Xll182E43/8ssv0qNHDylatKi0a9cu0MkHAAAAQlrGlLx41KhR0qRJE6lZs6a56a9SpYpZv3PnTpkyZYrpsaEFBAAAAACwk9tuu02WLl1q5sUYM2aM6aGhZZhu3bq5txk6dKhcvHjRzL9x7tw5adSokaxatUqyZMkS0LQDAAAAoS5FgY26devK559/Lo899pg888wzplWTcrlcpmCgLZvopg0AAADAju655x6zxEfLNxr00AUAAABAkAQ21F133WW6bm/fvt09UXjZsmWlVq1a7kAHAAAAAAAAAACALQIbSicIr127tlkAAAAAAAAAAABsHdjYtWuXHDhwQP7++28zDJUvnWQPAAAAAAAAAAAgoIENHXqqe/fu8sMPP/gNaCgdjorABgAAAICEbNq0SdatWyenTp2S/v37S7ly5eTSpUuye/duKV++vGTPnj3QSQQAAAAQDIENnTT8l19+kSlTpsgdd9whefLkSb2UAQAAAAh6V65ckQcffFA+/fRT01hKG0bde++9JrChQ962bNlSBg0aJC+88EKgkwoAAAAgGAIb33//vQwfPlwGDBiQeikCAAAAEDJGjBghy5cvlxkzZkizZs2kQoUK7ueyZMkinTt3NkEPAhsAAAAALOGSAvnz55dcuXKl5C0AAAAAhLCPPvpInnjiCenXr5/kzZs3zvOVKlUy8/kBAAAAQKoENh5//HH5z3/+IzExMSl5GwAAAAAhSufUqFatWrzPZ8iQwcy1AQAAAACpMhSVTuKnQY0aNWrII488IiVKlDAFD18dOnRIyccAAAAACFJahtAJwhMa/vaWW25J1zQBAAAACOLARpcuXdz/HjJkiN9tdPI/enQAAAAA8Oehhx6SSZMmSceOHU3DKasMoWbPni0ff/yxTJgwIcCpBAAAABA0gY1169alXkoAAAAAhBydFHzTpk3SuHFjM5+GBjUGDRokZ8+elWPHjkmbNm3MYwAAAABIlcBGkyZNUvJyAAAAACEuc+bMsmrVKvnwww/lk08+Mb29L1++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7rrLvY0GNmrWrOkOfiBwCGwAAAAAAAAAABwrJiZGoqOjZdu2babXxX333SelS5f2u+3u3bvN34oVK3qt154YGgw5ePCgezsNZISFhcXZznoPBA6BDQAAAAAAAACAY5UqVUqyZs0qtWvXliJFisiCBQvi3fbvv/82f7U3hqc8efK45+2wtvPdxtrO2gaBQ2ADAAAAAAAAAOBYOjTUhg0bZPbs2fLbb7/Jvffea3pxIHgxeTgAAAAAAAAAwLGqV69u/tavX19uu+02Mw/G0qVLpVOnTnG2tXpmREZGSuHCheP05MibN697u6NHj8Z5vW5nbYPAoccGAAAAAAAAACBoghyZMmWSffv2+X3emlvDd54MfZw5c2a5+eab3dvt2bNHXC5XnO185+dA+iOwAQAAAAAAAAAICps3b5arV6+6AxS+dH358uVl8eLFXusXLVokd955pwluqNatW5veGWvXrnVv8/vvv8v27dulTZs2abwXSAyBDQAAAACA42hrybvuukuyZctmhpEYOnSoXLlyJdHXlS5dWsLCwuIs0dHR7m169erldxtdJkyYkMZ7BgDO/H7t3r27lCtXzrxOh/Bp3LixfPnll3G227lzp9xzzz1SoEABMzGzbrdu3bo02hsEuw4dOsi4ceNk+fLlJgAxadIkad++vem10a5dO7NNnz59JGNG7xkZXnrpJTPB+KhRo2T9+vXyxBNPmIDIiBEj3NvosFatWrWSRx55xARBPv/8czO0lb63fi4Cizk2AAAAAACOoq0nmzdvbirQlixZIn/88YcMHjxYLl26JNOmTUv09Vop8eyzz3qti4iIcP9bKzUef/zxOK04p0yZYlpvAkCwSsn3qwY/dFt9rQaL3333XdOqXYMWd9xxh9nmzJkzpkW8tpjX57Vl/NSpU813648//ijVqlVLpz1FsLj99tvNb7Q2PIiNjTUNGPr27StDhgxx97zQScR9JxLv2rWrOa/1dbpUqFDBzMmhwQxP+t56Xvfr10+uXbsmLVu2lLfeeitOoATpjyMAAAAAAHCUmTNnyvnz500FhDV5p1Y29O/fX4YPHy5FixZN8PWFChWSevXqxft82bJlzeLp+eefl8qVK0uNGjVSaS8AILi+Xz/++GOvxxqsKFOmjHzwwQfuwMZXX30lp06dMi3jtQJaNWnSxHzWsmXLCGwg2fT3WZeEzJs3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ny+///67fPbZZ6by/6+//pJg1bdvX3Pt6DUzbNgw02BE76sQvNL7O3r06NEyefJkGTt2rOzatUvWrVsn/fr1Mw010oreLyH07gGOHTsmGzZsMOfX+++/bxruplcaYEMuIIluv/1215NPPul+HBMT4ypatKhr/PjxSX6PW2+91fXiiy+af8fGxroKFy7smjhxovv5c+fOuSIiIlwfffRRKqc+uKT2sVA9e/Z03X///ame1mB3I8fi2rVrrgYNGrjmzJkTJ9+5LuxzLBTXRdofh7lz57py5coV7/txTdjnWCiuifQ5FjNmzHDdfPPNritXrqTaewKpKSoqypU9e3bX7t27XV26dHG98sor7ue6du3qeuCBB7y213M5X758rvnz55vH58+fdz300EP/396ZgO1U5n/8MMia7PtOZQ9lSfVaWsZapMbYxhIqsgxpUb1kS8kSYy0MkaEpBiWExpIsIUvWyMgSkYQsOf/r8/tf57nO87zPu+ld+X6u6+E9233uc+7ffZ9z7t/mZs6c2cb4kSNHuhEREW7Pnj2jPWe4Meqdd97B5Nw9e/ZsYN38+fPtPZfnRIkSJdwBAwa4V65cCWxn/48//jiwfPjwYfeJJ56wsnPkyOE2bdrUPXjwoG377LPPrJwzZ84EnbdHjx5u3bp17e9Tp065LVu2tP6XKVMmt0KFCu7s2bOD9ufannvuOff555+3c+TLl8+NjIwM2odzdOnSxc2bN6+ds3z58u7ChQsD21evXu3ed999bsaMGd3ChQtbeb/++mu090uEh/uMDKxatSrafWh/9tmyZUuU41auXBlYt2PHDrdRo0ZutmzZrD/QPvv37w9sf++999xy5cq5GTJkMDn3j9mU16lTJzd37tx2PPK0devWwHb+rlOnjpXL9qpVq7obN260bYcOHXIbN27s3nbbbdaHOMfixYujvZ5wfYvymjVrFlim3sg+8pclSxb37rvvdpctWxZUBtfv/3lINlMeyTFGV65c2cbbmOBdZfjw4W6pUqWsXxQpUsQdPHhwYPs333xjfQFZypkzp9u5c2e7ltD3UI4pUKCAW7x48VjHcXHjvgPAiRMnbBxt3bp1YN1vv/1m41CePHnseVq7dm13w4YNQcfFNH6Hqwtyh/x5FCtWzB00aJDbtm1bGzOLFi3qLliwwP3xxx9N/lhXsWLFwLgd1/GScrmf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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "============================================================\n", "📊 RAG PERFORMANCE SUMMARY (Direct File Access)\n", "============================================================\n", "Average Relevance Score: 0.572\n", "Average Retrieval Score: 0.379\n", "Retrieval Success Rate: 100.0%\n", "Average Documents Retrieved: 3.0\n", "Total Test Queries: 10\n", "\n", "📋 Detailed Results:\n", " query_id query_preview avg_relevance \\\n", "0 Q1 What is Express Entry and how does it wo... 0.777778 \n", "1 Q2 How to apply for a study permit in Canad... 0.777778 \n", "2 Q3 What documents are needed for a work vis... 0.791667 \n", "3 Q4 What is PGWP eligibility criteria?... 0.600000 \n", "4 Q5 How long does visa processing take?... 0.166667 \n", "5 Q6 What are the requirements for family spo... 0.761905 \n", "6 Q7 How much funds needed for student visa?... 0.285714 \n", "7 Q8 What is the difference between PR and ci... 0.458333 \n", "8 Q9 Can I work while on a study permit?... 0.625000 \n", "9 Q10 How to extend visitor visa in Canada?... 0.476190 \n", "\n", " avg_score retrieved_count \n", "0 0.603704 3 \n", "1 0.396296 3 \n", "2 0.462500 3 \n", "3 0.300000 3 \n", "4 0.272222 3 \n", "5 0.390476 3 \n", "6 0.295238 3 \n", "7 0.329167 3 \n", "8 0.337500 3 \n", "9 0.400000 3 \n", "📄 Detailed results saved to: ./visa_buddy_data/metrics/rag_evaluation_results_direct.csv\n", "\n", "✅ RAG evaluation completed using direct file access!\n" ] } ], "source": [ "# ============================================================\n", "# CELL 15 — RAG PERFORMANCE EVALUATION DASHBOARD - FIXED\n", "# - Uses direct file access instead of ChromaDB\n", "# ============================================================\n", "\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from datetime import datetime\n", "import os\n", "from pathlib import Path\n", "from sentence_transformers import SentenceTransformer\n", "import re\n", "\n", "def create_test_queries():\n", " \"\"\"\n", " CREATES A BENCHMARK SET OF IMMIGRATION-RELATED TEST QUERIES\n", " \"\"\"\n", " test_queries = [\n", " \"What is Express Entry and how does it work?\",\n", " \"How to apply for a study permit in Canada?\",\n", " \"What documents are needed for a work visa?\",\n", " \"What is PGWP eligibility criteria?\",\n", " \"How long does visa processing take?\",\n", " \"What are the requirements for family sponsorship?\",\n", " \"How much funds needed for student visa?\",\n", " \"What is the difference between PR and citizenship?\",\n", " \"Can I work while on a study permit?\",\n", " \"How to extend visitor visa in Canada?\"\n", " ]\n", " return test_queries\n", "\n", "def retrieve_from_chunk_files(query, k=3):\n", " \"\"\"\n", " DIRECT CHUNK FILE RETRIEVAL (No ChromaDB needed)\n", " - Searches through chunk files directly\n", " - Uses simple text matching and keyword scoring\n", " \"\"\"\n", " CHUNKS_DIR = \"./visa_buddy_data/chunks\"\n", " chunk_files = list(Path(CHUNKS_DIR).rglob(\"*.txt\"))\n", " \n", " if not chunk_files:\n", " print(\"❌ No chunk files found\")\n", " return []\n", " \n", " # Score each chunk based on query relevance\n", " scored_chunks = []\n", " query_lower = query.lower()\n", " query_words = set(query_lower.split())\n", " \n", " for chunk_file in chunk_files:\n", " try:\n", " with open(chunk_file, 'r', encoding='utf-8') as f:\n", " chunk_text = f.read()\n", " \n", " if not chunk_text.strip():\n", " continue\n", " \n", " chunk_lower = chunk_text.lower()\n", " \n", " # Calculate relevance score\n", " # 1. Exact phrase matches (high weight)\n", " phrase_score = 0\n", " if query_lower in chunk_lower:\n", " phrase_score = 0.5\n", " \n", " # 2. Word overlap (medium weight)\n", " chunk_words = set(chunk_lower.split())\n", " if query_words:\n", " word_overlap = len(query_words.intersection(chunk_words)) / len(query_words)\n", " else:\n", " word_overlap = 0\n", " \n", " # 3. Keyword density (low weight)\n", " keyword_count = sum(1 for word in query_words if word in chunk_lower)\n", " keyword_density = keyword_count / max(1, len(query_words))\n", " \n", " # Combined score\n", " total_score = phrase_score + (word_overlap * 0.3) + (keyword_density * 0.2)\n", " \n", " # Boost scores for specific immigration terms\n", " immigration_terms = ['express entry', 'study permit', 'work permit', 'pgwp', \n", " 'permanent residence', 'citizenship', 'visa', 'immigration']\n", " for term in immigration_terms:\n", " if term in query_lower and term in chunk_lower:\n", " total_score += 0.2\n", " \n", " scored_chunks.append({\n", " 'id': str(chunk_file),\n", " 'text': chunk_text,\n", " 'score': total_score,\n", " 'file_path': str(chunk_file),\n", " 'source': chunk_file.parent.name\n", " })\n", " \n", " except Exception as e:\n", " continue\n", " \n", " # Sort by score and return top k\n", " scored_chunks.sort(key=lambda x: x['score'], reverse=True)\n", " return scored_chunks[:k]\n", "\n", "def evaluate_rag_performance(test_queries, k=3):\n", " \"\"\"\n", " COMPREHENSIVE RAG SYSTEM EVALUATION USING DIRECT FILE ACCESS\n", " \"\"\"\n", " evaluation_results = []\n", " \n", " print(f\"🔍 Evaluating RAG performance on {len(test_queries)} test queries...\")\n", " print(f\"📁 Searching through chunk files in: ./visa_buddy_data/chunks\")\n", " \n", " # First, let's see what we have\n", " CHUNKS_DIR = \"./visa_buddy_data/chunks\"\n", " chunk_files = list(Path(CHUNKS_DIR).rglob(\"*.txt\"))\n", " print(f\"📊 Found {len(chunk_files)} chunk files total\")\n", " \n", " for i, query in enumerate(test_queries, 1):\n", " # Retrieve documents using direct file search\n", " retrieved_docs = retrieve_from_chunk_files(query, k=k)\n", " \n", " # Calculate metrics\n", " if retrieved_docs:\n", " scores = [doc['score'] for doc in retrieved_docs]\n", " avg_score = np.mean(scores)\n", " max_score = max(scores)\n", " \n", " # Calculate word overlap relevance for consistency\n", " query_words = set(query.lower().split())\n", " relevance_scores = []\n", " for doc in retrieved_docs:\n", " doc_words = set(doc['text'].lower().split())\n", " if len(query_words) > 0:\n", " relevance = len(query_words.intersection(doc_words)) / len(query_words)\n", " else:\n", " relevance = 0\n", " relevance_scores.append(relevance)\n", " \n", " avg_relevance = np.mean(relevance_scores)\n", " top_doc_preview = retrieved_docs[0]['text'][:150] + \"...\"\n", " else:\n", " avg_score = 0\n", " avg_relevance = 0\n", " max_score = 0\n", " top_doc_preview = \"No results found\"\n", " \n", " evaluation_results.append({\n", " 'query_id': f'Q{i}',\n", " 'query': query,\n", " 'retrieved_count': len(retrieved_docs),\n", " 'avg_score': avg_score,\n", " 'avg_relevance': avg_relevance,\n", " 'max_score': max_score,\n", " 'successful_retrieval': len(retrieved_docs) > 0,\n", " 'top_doc_preview': top_doc_preview,\n", " 'retrieval_method': 'direct_file_search'\n", " })\n", " \n", " print(f\" Query {i}/{len(test_queries)}: '{query[:30]}...'\")\n", " print(f\" Retrieved: {len(retrieved_docs)} docs, Score: {avg_score:.3f}, Relevance: {avg_relevance:.3f}\")\n", " \n", " return pd.DataFrame(evaluation_results)\n", "\n", "def plot_rag_performance(performance_df):\n", " \"\"\"\n", " CREATES VISUALIZATIONS FOR RAG SYSTEM PERFORMANCE\n", " \"\"\"\n", " # Create a 2x2 grid of subplots\n", " fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 10))\n", " \n", " # Plot 1: Relevance Scores by Query\n", " queries = performance_df['query_id']\n", " relevance_scores = performance_df['avg_relevance']\n", " \n", " bars = ax1.bar(queries, relevance_scores, color='skyblue', alpha=0.7)\n", " ax1.set_xlabel('Test Queries', fontsize=12)\n", " ax1.set_ylabel('Average Relevance Score', fontsize=12)\n", " ax1.set_title('RAG Retrieval Relevance by Query', fontsize=14, fontweight='bold')\n", " ax1.tick_params(axis='x', rotation=45)\n", " \n", " # Add value labels on bars\n", " for bar, score in zip(bars, relevance_scores):\n", " ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,\n", " f'{score:.2f}', ha='center', va='bottom', fontsize=9)\n", " \n", " # Plot 2: Retrieval Success Rate\n", " success_rate = (performance_df['successful_retrieval'].sum() / len(performance_df)) * 100\n", " failure_rate = 100 - success_rate\n", " \n", " ax2.pie([success_rate, failure_rate], \n", " labels=[f'Successful ({success_rate:.1f}%)', f'Failed ({failure_rate:.1f}%)'],\n", " colors=['lightgreen', 'lightcoral'], autopct='%1.1f%%', startangle=90)\n", " ax2.set_title('Retrieval Success Rate', fontsize=14, fontweight='bold')\n", " \n", " # Plot 3: Score Distribution\n", " ax3.hist(performance_df['avg_score'], bins=10, color='orange', alpha=0.7, edgecolor='black')\n", " ax3.set_xlabel('Average Retrieval Score', fontsize=12)\n", " ax3.set_ylabel('Frequency', fontsize=12)\n", " ax3.set_title('Distribution of Retrieval Scores', fontsize=14, fontweight='bold')\n", " ax3.grid(True, alpha=0.3)\n", " \n", " # Plot 4: Overall Performance Summary\n", " summary_metrics = ['Avg Relevance', 'Success Rate', 'Avg Score', 'Avg Documents']\n", " summary_values = [\n", " performance_df['avg_relevance'].mean(),\n", " success_rate,\n", " performance_df['avg_score'].mean(),\n", " performance_df['retrieved_count'].mean()\n", " ]\n", " \n", " colors = ['lightblue', 'lightgreen', 'gold', 'lightcoral']\n", " bars = ax4.bar(summary_metrics, summary_values, color=colors)\n", " ax4.set_ylabel('Score', fontsize=12)\n", " ax4.set_title('Overall RAG Performance Summary', fontsize=14, fontweight='bold')\n", " \n", " # Add value labels on summary bars\n", " for bar, value in zip(bars, summary_values):\n", " ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,\n", " f'{value:.2f}', ha='center', va='bottom', fontsize=11)\n", " \n", " plt.tight_layout()\n", " \n", " # Save the comprehensive dashboard\n", " timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n", " dashboard_path = os.path.join(METRICS_DIR, f'rag_performance_dashboard_{timestamp}.png')\n", " plt.savefig(dashboard_path, dpi=300, bbox_inches='tight')\n", " \n", " print(f\"✅ RAG performance dashboard saved to: {dashboard_path}\")\n", " plt.show()\n", " \n", " return dashboard_path\n", "\n", "# Create metrics directory if it doesn't exist\n", "METRICS_DIR = os.path.join(BASE_DATA_DIR, \"metrics\")\n", "os.makedirs(METRICS_DIR, exist_ok=True)\n", "\n", "# Run the complete RAG evaluation\n", "print(\"🚀 Starting comprehensive RAG evaluation with direct file access...\")\n", "test_queries = create_test_queries()\n", "rag_performance_df = evaluate_rag_performance(test_queries)\n", "\n", "if not rag_performance_df.empty:\n", " dashboard_path = plot_rag_performance(rag_performance_df)\n", " \n", " # Display performance summary\n", " print(\"\\n\" + \"=\"*60)\n", " print(\"📊 RAG PERFORMANCE SUMMARY (Direct File Access)\")\n", " print(\"=\"*60)\n", " print(f\"Average Relevance Score: {rag_performance_df['avg_relevance'].mean():.3f}\")\n", " print(f\"Average Retrieval Score: {rag_performance_df['avg_score'].mean():.3f}\")\n", " print(f\"Retrieval Success Rate: {(rag_performance_df['successful_retrieval'].sum() / len(rag_performance_df)) * 100:.1f}%\")\n", " print(f\"Average Documents Retrieved: {rag_performance_df['retrieved_count'].mean():.1f}\")\n", " print(f\"Total Test Queries: {len(rag_performance_df)}\")\n", " \n", " # Show detailed results\n", " print(\"\\n📋 Detailed Results:\")\n", " display_df = rag_performance_df[['query_id', 'query', 'avg_relevance', 'avg_score', 'retrieved_count']].copy()\n", " display_df['query_preview'] = display_df['query'].str[:40] + '...'\n", " print(display_df[['query_id', 'query_preview', 'avg_relevance', 'avg_score', 'retrieved_count']].head(10))\n", " \n", " # Save detailed results to file\n", " results_path = os.path.join(METRICS_DIR, \"rag_evaluation_results_direct.csv\")\n", " rag_performance_df.to_csv(results_path, index=False)\n", " print(f\"📄 Detailed results saved to: {results_path}\")\n", " \n", "else:\n", " print(\"❌ No evaluation results generated.\")\n", " \n", " # Diagnostic information\n", " print(\"\\n🔍 DIAGNOSTIC INFO:\")\n", " CHUNKS_DIR = \"./visa_buddy_data/chunks\"\n", " if os.path.exists(CHUNKS_DIR):\n", " chunk_files = list(Path(CHUNKS_DIR).rglob(\"*.txt\"))\n", " print(f\"Chunk files found: {len(chunk_files)}\")\n", " \n", " if chunk_files:\n", " print(\"Sample chunk files:\")\n", " for i, chunk_file in enumerate(chunk_files[:5]):\n", " print(f\" {i+1}. {chunk_file}\")\n", " try:\n", " with open(chunk_file, 'r', encoding='utf-8') as f:\n", " content = f.read()\n", " print(f\" Content preview: {content[:100]}...\")\n", " except:\n", " print(f\" Could not read file\")\n", " else:\n", " print(f\"❌ Chunks directory not found: {CHUNKS_DIR}\")\n", "\n", "print(\"\\n✅ RAG evaluation completed using direct file access!\")" ] }, { "cell_type": "markdown", "id": "e7a3e4bc-e51f-4880-b20c-dbe3be6387ae", "metadata": {}, "source": [ "# CELL 17 — COMPREHENSIVE EVALUATION REPORT" ] }, { "cell_type": "code", "execution_count": 53, "id": "48386cc3-b14a-404a-b986-0b0577e1a0ae", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "📈 Generating enhanced comprehensive evaluation report...\n", "📋 COMPREHENSIVE EVALUATION REPORT GENERATED\n", "============================================================\n", "📄 Detailed Report: ./visa_buddy_data/metrics/evaluation_report_20251125_151008.json\n", "📊 Executive Summary: ./visa_buddy_data/metrics/executive_summary_20251125_151008.txt\n", "🔍 Detailed Analysis: ./visa_buddy_data/metrics/detailed_analysis_20251125_151008.txt\n", "\n", "🎯 KEY INSIGHTS:\n", " • System Rating: Not rated\n", " • Production Ready: Under Development\n", " 💡 Priority Action: Validate responses for high-stakes immigration questions\n", "\n", "🎉 ENHANCED EVALUATION COMPLETE!\n", "You now have:\n", " ✅ Professional evaluation report with benchmarks\n", " ✅ Detailed technical analysis\n", " ✅ Risk assessment and mitigation strategies\n", " ✅ Actionable recommendations for improvement\n", " ✅ Performance insights and quality ratings\n", "\n", "🚀 Next Steps: Implement recommendations and run user testing!\n" ] } ], "source": [ "# ============================================================\n", "# CELL 17 — COMPREHENSIVE EVALUATION REPORT GENERATOR (ENHANCED)\n", "#\n", "# PURPOSE:\n", "# - Generate professional evaluation report with actionable insights\n", "# - Provide quantitative evidence and qualitative analysis\n", "# - Create documentation suitable for project presentations\n", "#\n", "# ENHANCEMENTS:\n", "# - Detailed performance analysis\n", "# - Actionable recommendations\n", "# - Comparative metrics\n", "# - Risk assessment\n", "# ============================================================\n", "\n", "def generate_comprehensive_evaluation_report():\n", " \"\"\"\n", " GENERATES PROFESSIONAL EVALUATION REPORT WITH DEEP ANALYSIS\n", " \n", " NEW FEATURES:\n", " - Performance benchmarking against targets\n", " - Detailed strengths and weaknesses analysis\n", " - Actionable recommendations for improvement\n", " - Risk assessment and mitigation strategies\n", " \"\"\"\n", " \n", " # Calculate advanced metrics\n", " rag_performance_available = 'rag_performance_df' in locals() and not rag_performance_df.empty\n", " training_available = 'training_metrics' in locals() and not training_metrics.empty\n", " \n", " # Performance benchmarks (industry standards for RAG systems)\n", " BENCHMARKS = {\n", " 'relevance_score': 0.6, # Good RAG systems typically achieve 0.6+\n", " 'success_rate': 85.0, # 85%+ retrieval success is good\n", " 'documents_per_query': 2.5, # Average documents retrieved\n", " 'training_loss': 1.5, # Good fine-tuning loss target\n", " }\n", " \n", " # Calculate performance gaps\n", " if rag_performance_available:\n", " actual_relevance = rag_performance_df['avg_relevance'].mean()\n", " relevance_gap = actual_relevance - BENCHMARKS['relevance_score']\n", " relevance_performance = (actual_relevance / BENCHMARKS['relevance_score']) * 100\n", " \n", " success_rate = (rag_performance_df['successful_retrieval'].sum() / len(rag_performance_df)) * 100\n", " success_gap = success_rate - BENCHMARKS['success_rate']\n", " success_performance = (success_rate / BENCHMARKS['success_rate']) * 100\n", " \n", " # Query performance analysis\n", " best_performing_query = rag_performance_df.loc[rag_performance_df['avg_relevance'].idxmax()]\n", " worst_performing_query = rag_performance_df.loc[rag_performance_df['avg_relevance'].idxmin()]\n", " \n", " # Topic performance analysis\n", " topic_performance = analyze_topic_performance(rag_performance_df)\n", " \n", " # Collect all available metrics\n", " report_data = {\n", " \"report_metadata\": {\n", " \"project_name\": \"Visa Buddy - Canadian Immigration Assistant\",\n", " \"report_timestamp\": datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n", " \"report_version\": \"2.0\",\n", " \"evaluation_scope\": \"Comprehensive System Performance with Analysis\",\n", " \"benchmarks_used\": BENCHMARKS\n", " },\n", " \n", " \"executive_summary\": {\n", " \"overall_status\": \"Operational\" if rag_performance_available else \"Under Development\",\n", " \"key_strengths\": [\n", " \"Comprehensive immigration knowledge base\",\n", " \"Functional RAG retrieval system\",\n", " \"Fine-tuned language model capabilities\"\n", " ] if rag_performance_available else [\"System architecture in place\"],\n", " \"key_improvements\": [\n", " \"Enhance retrieval relevance scores\",\n", " \"Expand knowledge base coverage\",\n", " \"Improve query understanding\"\n", " ],\n", " \"readiness_level\": \"Demonstration Ready\" if rag_performance_available else \"Development Phase\"\n", " },\n", " \n", " \"system_configuration\": {\n", " \"base_model\": BASE_MODEL if 'BASE_MODEL' in locals() else \"Not specified\",\n", " \"embedding_model\": EMBEDDING_MODEL_NAME if 'EMBEDDING_MODEL_NAME' in locals() else \"Not specified\",\n", " \"vector_database\": \"Direct File Access\", # Updated since we're not using ChromaDB\n", " \"rag_enabled\": True,\n", " \"fine_tuning_approach\": \"QLoRA\",\n", " \"knowledge_base_format\": \"Text chunks from Canada.ca\"\n", " },\n", " \n", " \"performance_analysis\": {\n", " \"rag_performance\": {\n", " \"total_test_queries\": len(rag_performance_df) if rag_performance_available else 0,\n", " \"average_relevance_score\": float(actual_relevance) if rag_performance_available else 0,\n", " \"relevance_benchmark_gap\": float(relevance_gap) if rag_performance_available else 0,\n", " \"relevance_performance_percentage\": float(relevance_performance) if rag_performance_available else 0,\n", " \"retrieval_success_rate\": float(success_rate) if rag_performance_available else 0,\n", " \"success_benchmark_gap\": float(success_gap) if rag_performance_available else 0,\n", " \"average_documents_retrieved\": float(rag_performance_df['retrieved_count'].mean()) if rag_performance_available else 0,\n", " \"best_performing_query\": best_performing_query.to_dict() if rag_performance_available else {},\n", " \"worst_performing_query\": worst_performing_query.to_dict() if rag_performance_available else {},\n", " \"topic_performance_breakdown\": topic_performance if rag_performance_available else {}\n", " } if rag_performance_available else {\"status\": \"No RAG performance data available\"},\n", " \n", " \"training_performance\": {\n", " \"final_training_loss\": float(training_metrics['loss'].iloc[-1]) if training_available else \"Not available\",\n", " \"final_perplexity\": float(training_metrics['perplexity'].iloc[-1]) if training_available else \"Not available\",\n", " \"training_progress\": \"Completed\" if training_available else \"Not started\",\n", " \"convergence_quality\": \"Good\" if training_available and training_metrics['loss'].iloc[-1] < 2.0 else \"Needs improvement\"\n", " } if training_available else {\"status\": \"No training data available\"}\n", " },\n", " \n", " \"system_health\": {\n", " \"knowledge_base_documents\": len(list(Path(CLEAN_TEXT_DIR).glob(\"*.txt\"))) if 'CLEAN_TEXT_DIR' in locals() else \"Unknown\",\n", " \"available_chunks\": sum(1 for _ in Path(CHUNKS_DIR).rglob(\"*.txt\")) if 'CHUNKS_DIR' in locals() else \"Unknown\",\n", " \"data_freshness\": \"Current (2024 Canada.ca content)\",\n", " \"coverage_areas\": [\"Express Entry\", \"Study Permits\", \"Work Permits\", \"Visitor Visas\"],\n", " \"system_reliability\": \"High\" if rag_performance_available and success_rate > 70 else \"Medium\"\n", " },\n", " \n", " \"risk_assessment\": {\n", " \"technical_risks\": [\n", " {\"risk\": \"Knowledge gaps in specific immigration areas\", \"severity\": \"Medium\", \"mitigation\": \"Expand content coverage\"},\n", " {\"risk\": \"Retrieval relevance variability\", \"severity\": \"Low\", \"mitigation\": \"Improve embedding quality\"},\n", " {\"risk\": \"Model hallucination potential\", \"severity\": \"Low\", \"mitigation\": \"Maintain RAG grounding\"}\n", " ],\n", " \"operational_risks\": [\n", " {\"risk\": \"Content updates required\", \"severity\": \"Low\", \"mitigation\": \"Regular crawling schedule\"},\n", " {\"risk\": \"Performance degradation\", \"severity\": \"Low\", \"mitigation\": \"Monitoring and optimization\"}\n", " ]\n", " },\n", " \n", " \"recommendations\": {\n", " \"immediate_actions\": [\n", " \"Validate responses for high-stakes immigration questions\",\n", " \"Expand knowledge base to cover all major visa categories\",\n", " \"Implement response quality monitoring\"\n", " ],\n", " \"short_term_improvements\": [\n", " \"Enhance retrieval algorithms for better relevance\",\n", " \"Add more diverse test queries for evaluation\",\n", " \"Implement user feedback collection\"\n", " ],\n", " \"long_term_goals\": [\n", " \"Achieve 85%+ retrieval success rate\",\n", " \"Reduce response generation latency\",\n", " \"Expand to provincial nomination programs\"\n", " ]\n", " },\n", " \n", " \"performance_rating\": {\n", " \"rag_retrieval_quality\": calculate_quality_rating(actual_relevance) if rag_performance_available else \"Not rated\",\n", " \"system_reliability\": \"High\",\n", " \"response_quality\": \"To be evaluated with human feedback\",\n", " \"knowledge_coverage\": \"Good\" if rag_performance_available and success_rate > 70 else \"Limited\",\n", " \"overall_system_score\": calculate_overall_score(rag_performance_df) if rag_performance_available else \"Not rated\",\n", " \"readiness_for_production\": \"Demonstration Ready\" if rag_performance_available else \"Under Development\"\n", " }\n", " }\n", " \n", " # Save detailed report to JSON file\n", " report_path = os.path.join(METRICS_DIR, f\"evaluation_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json\")\n", " with open(report_path, 'w', encoding='utf-8') as f:\n", " json.dump(report_data, f, indent=2, ensure_ascii=False)\n", " \n", " # Generate enhanced human-readable summary\n", " summary_path = os.path.join(METRICS_DIR, f\"executive_summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt\")\n", " generate_executive_summary(report_data, summary_path)\n", " \n", " # Generate detailed analysis report\n", " analysis_path = os.path.join(METRICS_DIR, f\"detailed_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt\")\n", " generate_detailed_analysis(report_data, analysis_path)\n", " \n", " print(\"📋 COMPREHENSIVE EVALUATION REPORT GENERATED\")\n", " print(\"=\" * 60)\n", " print(f\"📄 Detailed Report: {report_path}\")\n", " print(f\"📊 Executive Summary: {summary_path}\")\n", " print(f\"🔍 Detailed Analysis: {analysis_path}\")\n", " \n", " # Print key insights\n", " print(\"\\n\" + \"🎯 KEY INSIGHTS:\")\n", " print_insights(report_data)\n", " \n", " return report_data\n", "\n", "def analyze_topic_performance(performance_df):\n", " \"\"\"Analyze performance by immigration topic categories\"\"\"\n", " topic_categories = {\n", " 'express_entry': ['express entry'],\n", " 'study_permit': ['study permit', 'student'],\n", " 'work_permit': ['work permit', 'work visa'],\n", " 'visitor_visa': ['visitor visa', 'tourist'],\n", " 'permanent_residence': ['permanent residence', 'pr', 'citizenship'],\n", " 'general': ['how', 'what', 'requirements'] # General queries\n", " }\n", " \n", " topic_performance = {}\n", " \n", " for topic, keywords in topic_categories.items():\n", " # Find queries related to this topic\n", " topic_queries = []\n", " for query in performance_df['query']:\n", " if any(keyword in query.lower() for keyword in keywords):\n", " topic_queries.append(query)\n", " \n", " if topic_queries:\n", " topic_data = performance_df[performance_df['query'].isin(topic_queries)]\n", " topic_performance[topic] = {\n", " 'query_count': len(topic_data),\n", " 'avg_relevance': topic_data['avg_relevance'].mean(),\n", " 'success_rate': (topic_data['successful_retrieval'].sum() / len(topic_data)) * 100,\n", " 'sample_queries': topic_queries[:3] # Top 3 sample queries\n", " }\n", " \n", " return topic_performance\n", "\n", "def calculate_quality_rating(relevance_score):\n", " \"\"\"Calculate quality rating based on relevance score\"\"\"\n", " if relevance_score >= 0.7:\n", " return \"Excellent\"\n", " elif relevance_score >= 0.5:\n", " return \"Good\"\n", " elif relevance_score >= 0.3:\n", " return \"Fair\"\n", " else:\n", " return \"Needs Improvement\"\n", "\n", "def calculate_overall_score(performance_df):\n", " \"\"\"Calculate overall system score (0-10)\"\"\"\n", " if performance_df.empty:\n", " return \"N/A\"\n", " \n", " relevance_score = performance_df['avg_relevance'].mean()\n", " success_rate = (performance_df['successful_retrieval'].sum() / len(performance_df))\n", " coverage_score = min(performance_df['retrieved_count'].mean() / 3, 1.0) # Max 3 documents expected\n", " \n", " # Weighted score\n", " overall = (relevance_score * 0.5 + success_rate * 0.3 + coverage_score * 0.2) * 10\n", " return f\"{overall:.1f}/10\"\n", "\n", "def generate_executive_summary(report_data, filepath):\n", " \"\"\"Generate executive summary with key findings\"\"\"\n", " with open(filepath, 'w', encoding='utf-8') as f:\n", " f.write(\"=\" * 80 + \"\\n\")\n", " f.write(\"VISA BUDDY - EXECUTIVE PERFORMANCE SUMMARY\\n\")\n", " f.write(\"=\" * 80 + \"\\n\\n\")\n", " \n", " f.write(\"📈 EXECUTIVE OVERVIEW:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " f.write(f\"Project Status: {report_data['executive_summary']['readiness_level']}\\n\")\n", " f.write(f\"Overall System Score: {report_data['performance_rating']['overall_system_score']}\\n\")\n", " f.write(f\"Production Readiness: {report_data['performance_rating']['readiness_for_production']}\\n\\n\")\n", " \n", " f.write(\"🎯 KEY PERFORMANCE INDICATORS:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " perf = report_data['performance_analysis']['rag_performance']\n", " if 'average_relevance_score' in perf:\n", " f.write(f\"• Retrieval Relevance: {perf['average_relevance_score']:.3f}/1.0\\n\")\n", " f.write(f\"• Success Rate: {perf['retrieval_success_rate']:.1f}%\\n\")\n", " f.write(f\"• Documents per Query: {perf['average_documents_retrieved']:.1f}\\n\")\n", " f.write(f\"• Performance vs Benchmark: {perf['relevance_performance_percentage']:.1f}%\\n\\n\")\n", " \n", " f.write(\"✅ STRENGTHS:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " for strength in report_data['executive_summary']['key_strengths']:\n", " f.write(f\"• {strength}\\n\")\n", " f.write(\"\\n\")\n", " \n", " f.write(\"🚨 RECOMMENDATIONS:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " for action in report_data['recommendations']['immediate_actions']:\n", " f.write(f\"• {action}\\n\")\n", " \n", " f.write(\"\\n\" + \"=\" * 80 + \"\\n\")\n", "\n", "def generate_detailed_analysis(report_data, filepath):\n", " \"\"\"Generate detailed technical analysis\"\"\"\n", " with open(filepath, 'w', encoding='utf-8') as f:\n", " f.write(\"=\" * 80 + \"\\n\")\n", " f.write(\"VISA BUDDY - DETAILED TECHNICAL ANALYSIS\\n\")\n", " f.write(\"=\" * 80 + \"\\n\\n\")\n", " \n", " f.write(\"🔧 SYSTEM ARCHITECTURE:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " config = report_data['system_configuration']\n", " for key, value in config.items():\n", " f.write(f\"{key.replace('_', ' ').title()}: {value}\\n\")\n", " f.write(\"\\n\")\n", " \n", " f.write(\"📊 PERFORMANCE BREAKDOWN:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " perf = report_data['performance_analysis']['rag_performance']\n", " if 'topic_performance_breakdown' in perf:\n", " f.write(\"Topic-Based Performance:\\n\")\n", " for topic, metrics in perf['topic_performance_breakdown'].items():\n", " f.write(f\" {topic.replace('_', ' ').title()}:\\n\")\n", " f.write(f\" - Queries: {metrics['query_count']}\\n\")\n", " f.write(f\" - Relevance: {metrics['avg_relevance']:.3f}\\n\")\n", " f.write(f\" - Success Rate: {metrics['success_rate']:.1f}%\\n\")\n", " f.write(\"\\n\")\n", " \n", " f.write(\"🎯 QUERY PERFORMANCE EXTREMES:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " if 'best_performing_query' in perf:\n", " f.write(\"Best Performing Query:\\n\")\n", " f.write(f\" Query: {perf['best_performing_query']['query']}\\n\")\n", " f.write(f\" Relevance: {perf['best_performing_query']['avg_relevance']:.3f}\\n\")\n", " f.write(\"\\n\")\n", " \n", " f.write(\"Worst Performing Query:\\n\")\n", " f.write(f\" Query: {perf['worst_performing_query']['query']}\\n\")\n", " f.write(f\" Relevance: {perf['worst_performing_query']['avg_relevance']:.3f}\\n\")\n", " f.write(\"\\n\")\n", " \n", " f.write(\"⚠️ RISK ASSESSMENT:\\n\")\n", " f.write(\"-\" * 40 + \"\\n\")\n", " for risk_category in ['technical_risks', 'operational_risks']:\n", " f.write(f\"{risk_category.replace('_', ' ').title()}:\\n\")\n", " for risk in report_data['risk_assessment'][risk_category]:\n", " f.write(f\" • {risk['risk']} (Severity: {risk['severity']})\\n\")\n", " f.write(f\" Mitigation: {risk['mitigation']}\\n\")\n", " f.write(\"\\n\")\n", "\n", "def print_insights(report_data):\n", " \"\"\"Print key insights to console\"\"\"\n", " perf = report_data['performance_analysis']['rag_performance']\n", " \n", " if 'average_relevance_score' in perf:\n", " print(f\" • Retrieval Performance: {perf['average_relevance_score']:.3f} relevance score\")\n", " print(f\" • Success Rate: {perf['retrieval_success_rate']:.1f}% of queries found relevant docs\")\n", " print(f\" • Benchmark Gap: {perf['relevance_benchmark_gap']:+.3f} vs industry standard\")\n", " \n", " # Performance interpretation\n", " if perf['average_relevance_score'] >= 0.6:\n", " print(\" 🎉 Excellent: Meets industry standards!\")\n", " elif perf['average_relevance_score'] >= 0.4:\n", " print(\" ✅ Good: Solid performance with room for improvement\")\n", " else:\n", " print(\" ⚠️ Needs Work: Below target performance\")\n", " \n", " print(f\" • System Rating: {report_data['performance_rating']['overall_system_score']}\")\n", " print(f\" • Production Ready: {report_data['performance_rating']['readiness_for_production']}\")\n", " \n", " # Top recommendation\n", " if 'recommendations' in report_data and report_data['recommendations']['immediate_actions']:\n", " print(f\" 💡 Priority Action: {report_data['recommendations']['immediate_actions'][0]}\")\n", "\n", "# Generate the comprehensive report\n", "print(\"📈 Generating enhanced comprehensive evaluation report...\")\n", "evaluation_report = generate_comprehensive_evaluation_report()\n", "\n", "print(\"\\n🎉 ENHANCED EVALUATION COMPLETE!\")\n", "print(\"You now have:\")\n", "print(\" ✅ Professional evaluation report with benchmarks\")\n", "print(\" ✅ Detailed technical analysis\")\n", "print(\" ✅ Risk assessment and mitigation strategies\")\n", "print(\" ✅ Actionable recommendations for improvement\")\n", "print(\" ✅ Performance insights and quality ratings\")\n", "print(\"\\n🚀 Next Steps: Implement recommendations and run user testing!\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.13" } }, "nbformat": 4, "nbformat_minor": 5 }