broadfield-dev commited on
Commit
35f8e95
·
verified ·
1 Parent(s): 08c0e4e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -55
app.py CHANGED
@@ -1,80 +1,87 @@
1
  import sys
2
  import subprocess
3
  from flask import Flask, render_template, request, flash, redirect, url_for
4
- from datasets import load_dataset
5
  import torch
6
  from transformers import AutoTokenizer, AutoModel
7
- import numpy as np
8
  import os
 
 
9
 
10
  # --- 1. Initialize Flask App ---
11
  app = Flask(__name__)
12
- # A secret key is needed for flashing messages to the user's session
13
  app.secret_key = os.urandom(24)
14
 
15
  # --- 2. Configuration & Resource Loading ---
16
  print("Starting application...")
17
 
18
- # Point this to the Hugging Face Dataset repository you want to create/use.
19
- # This MUST match the DATASET_REPO in build_rag.py
20
- DATASET_REPO = "broadfield-dev/bible-rag-dataset-gemma"
21
- MODEL_NAME = "google/embeddinggemma-300m" # Use a consistent model for embedding and searching
 
22
 
23
- # Global variables for the dataset and models
24
- rag_dataset = None
25
  tokenizer = None
26
  embedding_model = None
27
 
28
  def load_resources():
29
  """
30
- Attempts to load the dataset and models from the Hugging Face Hub.
31
- Returns True on success, False on failure.
32
  """
33
- global rag_dataset, tokenizer, embedding_model
34
- if rag_dataset:
35
  return True
36
 
37
- print(f"Attempting to load resources: {DATASET_REPO} and {MODEL_NAME}")
38
  try:
39
- # Load the pre-built dataset with the FAISS index
40
- rag_dataset = load_dataset(DATASET_REPO)['train']
41
-
42
- # Load the Gemma model and tokenizer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
44
  embedding_model = AutoModel.from_pretrained(MODEL_NAME)
 
45
 
46
- print("Models and dataset loaded successfully!")
47
  return True
48
  except Exception as e:
49
- print(f"Could not load RAG dataset from '{DATASET_REPO}'. It may not exist yet.")
50
  print(f"Error: {e}")
51
- # Reset globals to ensure a clean state
52
- rag_dataset = None
53
- tokenizer = None
54
- embedding_model = None
55
  return False
56
 
57
- # Try to load resources on startup. The app can still run if this fails.
58
  resources_loaded = load_resources()
59
 
60
- # --- 3. Define App Routes ---
61
-
62
  @app.route('/')
63
  def home():
64
  if not resources_loaded:
65
- flash(f"Welcome! The required RAG dataset '{DATASET_REPO}' is not loaded. Please use the 'Build RAG Dataset' button to create and upload it.", "warning")
66
  return render_template('index.html')
67
 
68
  @app.route('/build-rag', methods=['POST'])
69
  def build_rag_route():
70
- """
71
- Triggers the build_rag.py script as a background process.
72
- NOTE: This requires a Hugging Face token with 'write' permissions
73
- to be saved as a secret named HF_TOKEN in the Space settings.
74
- """
75
- print("RAG build process requested.")
76
  try:
77
- # Use Popen to run the script in the background without blocking the app.
78
  process = subprocess.Popen(
79
  [sys.executable, "build_rag.py"],
80
  stdout=subprocess.PIPE,
@@ -82,51 +89,47 @@ def build_rag_route():
82
  text=True
83
  )
84
  print(f"Started build process with PID: {process.pid}")
85
- flash("RAG build process initiated! This will run in the background and can take several minutes. Please check the Space logs for progress. Once complete, you can start searching.", "info")
86
  except Exception as e:
87
  print(f"Failed to start build process: {e}")
88
- flash(f"An error occurred while trying to start the build process: {e}", "error")
89
-
90
  return redirect(url_for('home'))
91
 
92
  @app.route('/search', methods=['POST'])
93
  def search():
94
  global resources_loaded
95
- # If resources weren't loaded, try again in case the build just finished.
96
  if not resources_loaded:
97
- print("Resources not loaded. Attempting to reload for search...")
98
  resources_loaded = load_resources()
99
  if not resources_loaded:
100
- flash("The RAG dataset is not ready yet. Please wait for the build process to complete or check the logs for errors.", "error")
101
  return redirect(url_for('home'))
102
 
103
  user_query = request.form['query']
104
  if not user_query:
105
  return render_template('index.html', results=[])
106
 
107
- # --- Create embedding for the user's query ---
108
  inputs = tokenizer(user_query, return_tensors="pt")
109
  with torch.no_grad():
110
  outputs = embedding_model(**inputs)
111
  query_embedding = outputs.last_hidden_state.mean(dim=1).cpu().numpy()
112
-
113
- query_embedding = np.float32(query_embedding)
114
 
115
- # --- Search the FAISS index ---
116
- scores, retrieved_examples = rag_dataset.get_nearest_examples(
117
- 'embeddings',
118
- query_embedding,
119
- k=10 # Get top 10 results
120
  )
121
 
122
- # --- Format results for display ---
123
  results_list = []
124
- for i in range(len(scores)):
 
 
 
 
125
  results_list.append({
126
- 'score': scores[i],
127
- 'text': retrieved_examples['text'][i],
128
- 'reference': retrieved_examples['reference'][i],
129
- 'version': retrieved_examples['version'][i]
130
  })
131
 
132
  return render_template('index.html', results=results_list, query=user_query)
 
1
  import sys
2
  import subprocess
3
  from flask import Flask, render_template, request, flash, redirect, url_for
 
4
  import torch
5
  from transformers import AutoTokenizer, AutoModel
 
6
  import os
7
+ import chromadb
8
+ from huggingface_hub import snapshot_download
9
 
10
  # --- 1. Initialize Flask App ---
11
  app = Flask(__name__)
 
12
  app.secret_key = os.urandom(24)
13
 
14
  # --- 2. Configuration & Resource Loading ---
15
  print("Starting application...")
16
 
17
+ # --- Configuration (Must match build_rag.py) ---
18
+ CHROMA_PATH = "chroma_db"
19
+ COLLECTION_NAME = "bible_verses"
20
+ MODEL_NAME = "google/embeddinggemma-300m"
21
+ DATASET_REPO = "broadfield-dev/bible-chromadb-gemma"
22
 
23
+ # --- Global variables for resources ---
24
+ chroma_collection = None
25
  tokenizer = None
26
  embedding_model = None
27
 
28
  def load_resources():
29
  """
30
+ Downloads the DB from the Hub if not present, then loads it and the model.
 
31
  """
32
+ global chroma_collection, tokenizer, embedding_model
33
+ if chroma_collection and embedding_model:
34
  return True
35
 
36
+ print("Attempting to load resources...")
37
  try:
38
+ # 1. Download the ChromaDB files from the Hugging Face Hub
39
+ # This will only download if the folder doesn't already exist.
40
+ print(f"Ensuring database is available locally from '{DATASET_REPO}'...")
41
+ snapshot_download(
42
+ repo_id=DATASET_REPO,
43
+ repo_type="dataset",
44
+ local_dir=CHROMA_PATH,
45
+ local_dir_use_symlinks=False # Recommended for Spaces
46
+ )
47
+ print("Database files are present locally.")
48
+
49
+ # 2. Initialize ChromaDB client from the downloaded files
50
+ client = chromadb.PersistentClient(path=CHROMA_PATH)
51
+ collection = client.get_collection(name=COLLECTION_NAME)
52
+
53
+ if collection.count() == 0:
54
+ print(f"Warning: Database collection is empty.")
55
+ return False
56
+
57
+ chroma_collection = collection
58
+ print(f"Successfully connected to DB with {collection.count()} items.")
59
+
60
+ # 3. Load the embedding model
61
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
62
  embedding_model = AutoModel.from_pretrained(MODEL_NAME)
63
+ print(f"Embedding model '{MODEL_NAME}' loaded successfully.")
64
 
 
65
  return True
66
  except Exception as e:
67
+ print(f"Could not load resources. The database may not be built yet.")
68
  print(f"Error: {e}")
 
 
 
 
69
  return False
70
 
71
+ # Try to load resources on startup.
72
  resources_loaded = load_resources()
73
 
74
+ # --- 3. Define App Routes (Unchanged from previous ChromaDB version) ---
 
75
  @app.route('/')
76
  def home():
77
  if not resources_loaded:
78
+ flash(f"Welcome! Database not ready. Use the admin panel to build it.", "warning")
79
  return render_template('index.html')
80
 
81
  @app.route('/build-rag', methods=['POST'])
82
  def build_rag_route():
83
+ print("Vector database build process requested.")
 
 
 
 
 
84
  try:
 
85
  process = subprocess.Popen(
86
  [sys.executable, "build_rag.py"],
87
  stdout=subprocess.PIPE,
 
89
  text=True
90
  )
91
  print(f"Started build process with PID: {process.pid}")
92
+ flash("Database build & push initiated! This can take several minutes. Check logs for progress. The app will be ready when it completes.", "info")
93
  except Exception as e:
94
  print(f"Failed to start build process: {e}")
95
+ flash(f"An error occurred: {e}", "error")
 
96
  return redirect(url_for('home'))
97
 
98
  @app.route('/search', methods=['POST'])
99
  def search():
100
  global resources_loaded
 
101
  if not resources_loaded:
102
+ print("Reloading resources for search...")
103
  resources_loaded = load_resources()
104
  if not resources_loaded:
105
+ flash("Database not ready. Please wait for the build process to finish.", "error")
106
  return redirect(url_for('home'))
107
 
108
  user_query = request.form['query']
109
  if not user_query:
110
  return render_template('index.html', results=[])
111
 
 
112
  inputs = tokenizer(user_query, return_tensors="pt")
113
  with torch.no_grad():
114
  outputs = embedding_model(**inputs)
115
  query_embedding = outputs.last_hidden_state.mean(dim=1).cpu().numpy()
 
 
116
 
117
+ search_results = chroma_collection.query(
118
+ query_embeddings=query_embedding.tolist(),
119
+ n_results=10
 
 
120
  )
121
 
 
122
  results_list = []
123
+ documents = search_results['documents'][0]
124
+ metadatas = search_results['metadatas'][0]
125
+ distances = search_results['distances'][0]
126
+
127
+ for i in range(len(documents)):
128
  results_list.append({
129
+ 'score': distances[i],
130
+ 'text': documents[i],
131
+ 'reference': metadatas[i].get('reference', 'N/A'),
132
+ 'version': metadatas[i].get('version', 'N/A')
133
  })
134
 
135
  return render_template('index.html', results=results_list, query=user_query)