File size: 7,436 Bytes
7e0e6b2
 
 
 
507a7b9
7c03c77
 
0884ede
7c03c77
8e37637
0884ede
507a7b9
7c03c77
7e0e6b2
 
0666861
8e37637
 
0666861
 
 
 
 
 
 
 
7e0e6b2
 
7c03c77
7e0e6b2
 
 
 
 
 
 
7c03c77
071ebb8
7e0e6b2
 
 
 
 
 
071ebb8
7e0e6b2
071ebb8
7e0e6b2
8e37637
 
 
 
 
 
 
a63e8a5
 
071ebb8
 
 
 
8e37637
 
 
 
 
 
 
 
 
 
 
9380505
7e0e6b2
9380505
 
071ebb8
 
9380505
a63e8a5
 
8e37637
9380505
a63e8a5
9380505
a63e8a5
 
9380505
 
 
071ebb8
7e0e6b2
071ebb8
 
 
 
8e37637
071ebb8
f478807
9380505
7e0e6b2
 
 
 
 
 
 
 
7c03c77
7987393
 
 
 
adec15b
 
 
7c03c77
 
 
 
 
7e0e6b2
6952a2a
 
 
 
 
 
 
 
7e0e6b2
071ebb8
 
 
7e0e6b2
6952a2a
7e0e6b2
 
6952a2a
7e0e6b2
 
8e37637
b153128
 
071ebb8
f2d20ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
071ebb8
b153128
42b62ad
 
071ebb8
f2d20ef
 
 
42b62ad
 
 
f2d20ef
8e37637
f2d20ef
 
 
 
 
 
 
 
 
 
 
 
071ebb8
0666861
42b62ad
071ebb8
42b62ad
63d328e
071ebb8
 
 
 
 
63d328e
7e0e6b2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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()