RAGdemo / app.py
nmcamacho's picture
Update app.py
6952a2a verified
import gradio as gr
import pandas as pd
import numpy as np
import faiss
import os
import re
import random
from openai import OpenAI
# Initialize OpenAI client securely
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# Convert a CRM row into readable text for embeddings
def row_to_text(row):
return (
f"Clinic: {row.get('Account_Name', 'N/A')}\n"
f"Interaction Focus: {row.get('Recent_Interaction_Focus', 'N/A')}\n"
f"Interaction Notes: {row.get('Recent_Interaction_Notes', 'N/A')}\n"
f"Region: {row.get('Region', 'Unknown')}\n"
f"Account Manager: {row.get('Account_Manager', 'Unknown')}\n"
f"Revenue: €{row.get('Revenue', 'N/A')}\n"
f"Last Purchase: {row.get('Last_Purchase_Date', 'N/A')}\n"
f"Churn Risk: {row.get('Churn_Risk', 'N/A')}\n"
f"Satisfaction Score: {row.get('Satisfaction_Score', 'N/A')}/10\n"
f"Product Interest: {row.get('Product_Interest', 'N/A')}\n"
f"Notes: {row.get('Account_Notes', 'N/A')}"
)
# Get embedding for a given text
def get_embedding(text):
response = client.embeddings.create(
input=[text],
model="text-embedding-3-small"
)
return response.data[0].embedding
# Core function: generate contextual pitch
def contextual_pitch_assistant(csv_file, query, sender_name):
df = pd.read_csv(csv_file.name)
text_chunks = df.apply(row_to_text, axis=1).tolist()
embeddings = [get_embedding(t) for t in text_chunks]
dim = len(embeddings[0])
index = faiss.IndexFlatL2(dim)
index.add(np.array(embeddings).astype("float32"))
q_emb = np.array([get_embedding(query)]).astype("float32")
D, I = index.search(q_emb, 3)
retrieved = [df.iloc[i] for i in I[0]]
# Extract contextual fields
row = retrieved[0]
account_manager = row.get("Account_Manager", "team")
account_name = row.get("Account_Name", "your clinic")
focus = str(row.get("Recent_Interaction_Focus", "")).lower()
notes_focus = str(row.get("Recent_Interaction_Notes", ""))
notes_background = str(row.get("Account_Notes", ""))
if not sender_name.strip():
sender_name = "The Sales Team"
# Map focus → style instruction
if "business" in focus:
style_instruction = "Emphasize ROI, efficiency, and competitive advantage."
elif "patient" in focus:
style_instruction = "Emphasize patient comfort, satisfaction, and outcomes."
elif "clinical" in focus:
style_instruction = "Emphasize precision, innovation, and clinical quality."
else:
style_instruction = "Balance clinical, patient, and business value."
# Build prompt
prompt = f"""
You are an expert in B2B sales messaging, inspired by Challenger Sales and Jeb Blount’s prospecting techniques.
Your job is to write a short, natural-sounding HTML email pitch to a dental clinic.
Rules:
- Begin with "Dear {account_manager}," as the greeting.
- Anchor immediately in the clinic’s recent concern: "{notes_focus}".
- Use background context if relevant: "{notes_background}".
- Adapt the pitch style: {style_instruction}
- Apply Challenger logic:
* Start with the client’s concern in their own terms.
* Reframe with an insight (show a broader problem or missed opportunity).
* Link features → outcomes → impact (business, patient, or clinical).
* Confident but helpful tone.
- Keep under 150 words.
- End with a strong, specific CTA (e.g. propose a short call or demo, suggest a time).
- Close with "Best regards," followed by "{sender_name}".
- Return only HTML — no markdown, no code fences.
Query:
{query}
CRM context (for your understanding, do not copy verbatim):
{row_to_text(row)}
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0.7
)
output_text = response.choices[0].message.content
# TEMP FIX: use random pre-uploaded header image
image_choices = [
"https://huggingface.co/spaces/nmcamacho/RAGdemo/resolve/main/dental_header_1.png",
"https://huggingface.co/spaces/nmcamacho/RAGdemo/resolve/main/dental_header_2.png",
"https://huggingface.co/spaces/nmcamacho/RAGdemo/resolve/main/dental_header_3.png"
]
image_url = random.choice(image_choices)
# Clean output text
output_text = re.sub(r"(?is)dall[-·]e.*?```", "", output_text)
output_text = re.sub(r"###.*?HTML Email Pitch", "", output_text)
output_text = re.sub(r"```html|```", "", output_text)
output_text = output_text.strip()
clinic_note = f"""
<div style='margin-top:20px;padding:10px;font-size:13px;color:gray;
border-top:1px solid #ddd;'>
📌 Target: <b>{account_name}</b> (Region: {row.get('Region', 'N/A')},
Focus: {row.get('Recent_Interaction_Focus', 'N/A')})
</div>
"""
html = f"""
<div style='font-family:Arial,sans-serif;max-width:700px;margin:auto;padding:24px;background:#ffffff;
border-radius:12px;box-shadow:0 3px 10px rgba(0,0,0,0.1);'>
<img src="{image_url}" style="width:100%;border-radius:8px;margin-bottom:20px;">
{output_text}
{clinic_note}
</div>
"""
return html
# Build Gradio app (unchanged, apart from column updates)
with gr.Blocks(
title="Contextual Pitch Assistant for Dental Sales",
css="""
body { background-color: #f7f9f9; font-family: 'Inter', sans-serif; }
#output_html { min-height: 450px; }
.gradio-container { max-width: 90% !important; margin: auto; }
h1, h2, h3, h4, h5 { color: #00857C; font-weight: 600; }
.gr-button {
background-color: #00857C !important;
color: white !important;
border: none !important;
font-weight: 600;
padding: 10px 18px;
border-radius: 8px;
}
.gr-button:hover { background-color: #006e67 !important; }
.gr-file, .gr-textbox {
border: 1px solid #d1d5db !important;
border-radius: 8px !important;
}
.gr-box {
background: white !important;
border-radius: 12px !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
padding: 20px !important;
}
"""
) as app:
gr.Markdown(
"""
# 🦷 Contextual Pitch Assistant for Dental Sales
*Powered by contextual CRM data and generative AI*
---
Upload a CRM file and enter a sales question — get a personalized email pitch with a contextual image.
"""
)
with gr.Row():
csv_file = gr.File(label="📂 Upload CRM CSV (with Recent_Interaction_Focus/Notes)", file_types=[".csv"])
sender_name = gr.Textbox(
label="✍️ Who signs the email?",
placeholder="e.g. Nuno Camacho, Sales Director",
value="Nuno Camacho"
)
query = gr.Textbox(
label="💬 Sales Query",
placeholder="e.g. Which clinic is best for our imaging subscription?",
lines=2
)
run_btn = gr.Button("🚀 Generate Pitch", variant="primary")
output = gr.HTML(label="✨ Email Pitch Preview", elem_id="output_html")
run_btn.click(fn=contextual_pitch_assistant, inputs=[csv_file, query, sender_name], outputs=output)
run_btn.click(
lambda: "<p style='color:gray;'>⏳ Processing... please wait 30–60 seconds.</p>",
inputs=None,
outputs=output,
queue=False
)
app.launch()