Yoel125 commited on
Commit
b8c3f18
·
verified ·
1 Parent(s): 894c97b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -64
app.py CHANGED
@@ -10,93 +10,102 @@ import faiss
10
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
11
  import spaces # <--- Import the Hugging Face spaces library for Free GPU
12
 
13
- # --- 1. LOAD GENERATIVE AI COMPONENT ---
14
- print("Loading GenAI Component on CPU safely for ZeroGPU startup...")
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- # Load the model strictly on CPU at the top level to prevent ZeroGPU startup crashes.
17
- gen_tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
18
- gen_model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- gen_model.eval()
21
 
22
- def generate_sales_pitch(user_query, company_name, sector, theme, description):
23
- # THE PITCH FIX:
24
- # To stop the AI from generating generic corporate boilerplate that accidentally
25
- # matches the synthetic dataset descriptions, we force it to answer a specific Question.
26
  prompt = (
27
- f"Context: {company_name} is a leading {theme} company.\n"
28
- f"Question: Why is {company_name} a brilliant and highly profitable investment for someone searching for '{user_query}'?\n"
29
- f"Answer:"
 
 
30
  )
31
 
32
  try:
33
  device = "cuda" if torch.cuda.is_available() else "cpu"
34
- gen_model.to(device)
35
- inputs = gen_tokenizer(prompt, return_tensors="pt", max_length=256, truncation=True).to(device)
36
 
37
  torch.manual_seed(42)
38
 
39
  with torch.no_grad():
40
- outputs = gen_model.generate(
41
  **inputs,
42
- max_new_tokens=60,
43
  do_sample=False,
44
  num_beams=4,
45
  repetition_penalty=1.5,
46
  early_stopping=True
47
  )
48
- pitch = gen_tokenizer.decode(outputs[0], skip_special_tokens=True)
49
 
50
- pitch = pitch.replace("Answer:", "").strip()
51
-
52
- # =========================================================================
53
- # THE ULTIMATE ANTI-COPYING SAFETY NET
54
- # If the small AI model generates a sentence that starts the exact same way
55
- # as the company overview, we intercept it and replace it with a beautiful,
56
- # highly customized dynamic pitch so they NEVER match!
57
- # =========================================================================
58
- desc_words = description.lower().split()
59
- pitch_words = pitch.lower().split()
60
-
61
- if len(pitch_words) < 5 or pitch_words[:4] == desc_words[:4] or pitch.lower() in description.lower():
62
- pitch = f"Investing in {company_name} is a brilliant strategic move for '{user_query}', as their cutting-edge focus on {theme} perfectly captures the massive growth potential in this sector."
63
 
 
 
 
 
64
  if pitch:
65
  pitch = pitch[0].upper() + pitch[1:]
66
-
67
  return pitch
68
 
69
  except Exception:
70
- return f"{company_name} offers exceptional strategic exposure to {theme}, perfectly aligning with '{user_query}'."
71
-
72
 
73
- # --- 2. AUTOMATIC SAFETY AUTO-LOADER (Dataset & Embeddings) ---
74
- if 'df' not in globals():
75
- print("Auto-loading dataset 'Yoel125/synthetic-companies-12k' from Hugging Face...")
76
- df = pd.DataFrame(load_dataset('Yoel125/synthetic-companies-12k', split='train'))
77
- df['full_text'] = df['sector'] + " - " + df['theme'] + ": " + df['description']
78
-
79
- if 'embedding_model' not in globals():
80
- print("Auto-loading embedding model 'paraphrase-MiniLM-L3-v2' strictly on CPU...")
81
- # Explicitly lock SentenceTransformer to CPU so it doesn't crash the ZeroGPU bootloader!
82
- embedding_model = SentenceTransformer('paraphrase-MiniLM-L3-v2', device='cpu')
83
-
84
- if 'faiss_index' not in globals():
85
- try:
86
- print("Loading saved embeddings from company_embeddings.npy...")
87
- embeddings = np.load('company_embeddings.npy')
88
- faiss_index = faiss.IndexFlatL2(embeddings.shape[1])
89
- faiss_index.add(np.array(embeddings).astype('float32'))
90
- except FileNotFoundError:
91
- print("Saved embeddings not found! Generating them now (this may take 30 seconds)...")
92
- embeddings = embedding_model.encode(df['full_text'].tolist(), show_progress_bar=False)
93
- faiss_index = faiss.IndexFlatL2(embeddings.shape[1])
94
- faiss_index.add(np.array(embeddings).astype('float32'))
95
-
96
-
97
- # --- 3. RECOMMENDATION ENGINE LOGIC ---
98
- all_sectors = ["All Sectors"] + sorted(list(df['sector'].unique()))
99
 
 
100
  @spaces.GPU # <--- Hugging Face ZeroGPU Decorator!
101
  def recommend_investment(user_query, selected_sector, top_k=3):
102
  if (not user_query or not str(user_query).strip()) and selected_sector == "All Sectors":
@@ -112,7 +121,13 @@ def recommend_investment(user_query, selected_sector, top_k=3):
112
  if not user_query or not str(user_query).strip():
113
  user_query = f"innovative {selected_sector} companies"
114
 
115
- yield "🔍 Searching for matching companies and generating AI insights... this can take a few seconds."
 
 
 
 
 
 
116
 
117
  if selected_sector != "All Sectors":
118
  enriched_query = f"{selected_sector} industry B2B company specializing in: {user_query}"
@@ -156,9 +171,9 @@ def recommend_investment(user_query, selected_sector, top_k=3):
156
  desc = row['description']
157
 
158
  try:
159
- pitch = generate_sales_pitch(user_query, c_name, sector, theme, desc)
160
  except Exception:
161
- pitch = f"An exceptional strategic match for {user_query} within the {sector} space."
162
 
163
  tier_label = match_labels[rank - 1] if rank <= len(match_labels) else "Match"
164
 
@@ -171,7 +186,7 @@ def recommend_investment(user_query, selected_sector, top_k=3):
171
  yield output_markdown
172
 
173
 
174
- # --- 4. GRADIO USER INTERFACE ---
175
  custom_theme = gr.themes.Base(
176
  primary_hue="emerald",
177
  neutral_hue="slate"
 
10
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
11
  import spaces # <--- Import the Hugging Face spaces library for Free GPU
12
 
13
+ # =========================================================================
14
+ # 1. LAZY LOADING ARCHITECTURE (The Ultimate Fix for Smart AI)
15
+ # To get a truly "smart" pitch, we MUST use the heavier 'flan-t5-base' model.
16
+ # But loading heavy models during startup crashes Hugging Face.
17
+ # The solution: We load the app instantly, and only load the heavy AI into
18
+ # memory when you click Search for the first time!
19
+ # =========================================================================
20
+
21
+ # Load the dataset globally so the UI Dropdown knows what sectors exist
22
+ print("Loading Dataset...")
23
+ df = pd.DataFrame(load_dataset('Yoel125/synthetic-companies-12k', split='train'))
24
+ df['full_text'] = df['sector'] + " - " + df['theme'] + ": " + df['description']
25
+ all_sectors = ["All Sectors"] + sorted(list(df['sector'].unique()))
26
 
27
+ # Global caches for the heavy AI models
28
+ embedding_model_cache = None
29
+ faiss_index_cache = None
30
+ gen_tokenizer_cache = None
31
+ gen_model_cache = None
32
+
33
+ def get_ai_models():
34
+ global embedding_model_cache, faiss_index_cache, gen_tokenizer_cache, gen_model_cache
35
+
36
+ if embedding_model_cache is None:
37
+ print("Lazy-loading Embedding Model...")
38
+ embedding_model_cache = SentenceTransformer('paraphrase-MiniLM-L3-v2', device='cpu')
39
+
40
+ if faiss_index_cache is None:
41
+ try:
42
+ print("Loading FAISS index...")
43
+ embeddings = np.load('company_embeddings.npy')
44
+ faiss_index_cache = faiss.IndexFlatL2(embeddings.shape[1])
45
+ faiss_index_cache.add(np.array(embeddings).astype('float32'))
46
+ except FileNotFoundError:
47
+ print("Generating new FAISS index...")
48
+ embeddings = embedding_model_cache.encode(df['full_text'].tolist(), show_progress_bar=False)
49
+ faiss_index_cache = faiss.IndexFlatL2(embeddings.shape[1])
50
+ faiss_index_cache.add(np.array(embeddings).astype('float32'))
51
+
52
+ if gen_model_cache is None:
53
+ print("Lazy-loading Smarter GenAI Model (flan-t5-base)...")
54
+ # UPGRADED back to the much smarter 'base' model!
55
+ gen_tokenizer_cache = AutoTokenizer.from_pretrained("google/flan-t5-base")
56
+ gen_model_cache = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base")
57
+ gen_model_cache.eval()
58
+
59
+ return embedding_model_cache, faiss_index_cache, gen_tokenizer_cache, gen_model_cache
60
 
 
61
 
62
+ def generate_sales_pitch(user_query, company_name, sector, theme, description, tokenizer, model):
63
+ # Now that we have the smarter 'base' model, we give it a strict prompt
64
+ # forcing it to act like an aggressive, smart Wall Street analyst.
 
65
  prompt = (
66
+ f"Task: Write a creative, aggressive sales pitch explaining why this company is the ultimate investment for the '{user_query}' market. Do not summarize the company.\n"
67
+ f"Company Name: {company_name}\n"
68
+ f"Industry: {theme}\n"
69
+ f"Profile: {description[:200]}\n"
70
+ f"Pitch:"
71
  )
72
 
73
  try:
74
  device = "cuda" if torch.cuda.is_available() else "cpu"
75
+ model.to(device)
76
+ inputs = tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True).to(device)
77
 
78
  torch.manual_seed(42)
79
 
80
  with torch.no_grad():
81
+ outputs = model.generate(
82
  **inputs,
83
+ max_new_tokens=75,
84
  do_sample=False,
85
  num_beams=4,
86
  repetition_penalty=1.5,
87
  early_stopping=True
88
  )
89
+ pitch = tokenizer.decode(outputs[0], skip_special_tokens=True)
90
 
91
+ pitch = pitch.replace("Pitch:", "").replace("Sales Pitch:", "").strip()
92
+ if pitch and pitch[0] == '"' and pitch[-1] == '"':
93
+ pitch = pitch[1:-1]
 
 
 
 
 
 
 
 
 
 
94
 
95
+ # Ultimate fallback just in case it still tries to copy
96
+ if len(pitch.split()) < 6 or pitch.lower() in description.lower():
97
+ pitch = f"By leveraging their advanced {theme} capabilities, {company_name} is perfectly positioned to capture explosive growth and completely dominate the '{user_query}' space."
98
+
99
  if pitch:
100
  pitch = pitch[0].upper() + pitch[1:]
101
+
102
  return pitch
103
 
104
  except Exception:
105
+ return f"By leveraging their advanced {theme} capabilities, {company_name} is perfectly positioned to capture explosive growth and completely dominate the '{user_query}' space."
 
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ # --- 2. RECOMMENDATION ENGINE LOGIC ---
109
  @spaces.GPU # <--- Hugging Face ZeroGPU Decorator!
110
  def recommend_investment(user_query, selected_sector, top_k=3):
111
  if (not user_query or not str(user_query).strip()) and selected_sector == "All Sectors":
 
121
  if not user_query or not str(user_query).strip():
122
  user_query = f"innovative {selected_sector} companies"
123
 
124
+ # Streaming a status message so you know why the first click takes a few seconds!
125
+ yield "🚀 Initializing Smart AI Engine... (The very first search takes ~10 seconds to load the heavy AI. Future searches will be instant!)"
126
+
127
+ # Boot up the heavy AI models safely!
128
+ embedding_model, faiss_index, gen_tokenizer, gen_model = get_ai_models()
129
+
130
+ yield "🔍 Searching for matching companies and writing smart pitches..."
131
 
132
  if selected_sector != "All Sectors":
133
  enriched_query = f"{selected_sector} industry B2B company specializing in: {user_query}"
 
171
  desc = row['description']
172
 
173
  try:
174
+ pitch = generate_sales_pitch(user_query, c_name, sector, theme, desc, gen_tokenizer, gen_model)
175
  except Exception:
176
+ pitch = f"By leveraging their advanced {theme} capabilities, {c_name} is perfectly positioned to capture explosive growth and completely dominate the '{user_query}' space."
177
 
178
  tier_label = match_labels[rank - 1] if rank <= len(match_labels) else "Match"
179
 
 
186
  yield output_markdown
187
 
188
 
189
+ # --- 3. GRADIO USER INTERFACE ---
190
  custom_theme = gr.themes.Base(
191
  primary_hue="emerald",
192
  neutral_hue="slate"