Spaces:
Sleeping
Sleeping
vishal1278
commited on
Commit
•
93e917d
1
Parent(s):
4209994
added all code
Browse files- img/beetlejuice_greeter.png +0 -0
- img/canvas.png +0 -0
- img/digital_photo_gift.png +0 -0
- img/photo-keeper.png +0 -0
- img/photo_keeper.png +0 -0
- img/strung-beads.png +0 -0
- requirements.txt +134 -0
- src/__init__.py +0 -0
- src/app.py +63 -0
- src/prompts.py +50 -0
- src/utils.py +121 -0
img/beetlejuice_greeter.png
ADDED
img/canvas.png
ADDED
img/digital_photo_gift.png
ADDED
img/photo-keeper.png
ADDED
img/photo_keeper.png
ADDED
img/strung-beads.png
ADDED
requirements.txt
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==23.2.1
|
2 |
+
annotated-types==0.7.0
|
3 |
+
anyio==4.4.0
|
4 |
+
argon2-cffi==23.1.0
|
5 |
+
argon2-cffi-bindings==21.2.0
|
6 |
+
arrow==1.3.0
|
7 |
+
asttokens==2.4.1
|
8 |
+
async-lru==2.0.4
|
9 |
+
attrs==24.2.0
|
10 |
+
babel==2.16.0
|
11 |
+
beautifulsoup4==4.12.3
|
12 |
+
bleach==6.1.0
|
13 |
+
certifi==2024.7.4
|
14 |
+
cffi==1.17.0
|
15 |
+
charset-normalizer==3.3.2
|
16 |
+
click==8.1.7
|
17 |
+
comm==0.2.2
|
18 |
+
contourpy==1.2.1
|
19 |
+
cycler==0.12.1
|
20 |
+
debugpy==1.8.5
|
21 |
+
decorator==5.1.1
|
22 |
+
defusedxml==0.7.1
|
23 |
+
distro==1.9.0
|
24 |
+
exceptiongroup==1.2.2
|
25 |
+
executing==2.0.1
|
26 |
+
fastapi==0.112.0
|
27 |
+
fastjsonschema==2.20.0
|
28 |
+
ffmpy==0.4.0
|
29 |
+
filelock==3.15.4
|
30 |
+
fonttools==4.53.1
|
31 |
+
fqdn==1.5.1
|
32 |
+
fsspec==2024.6.1
|
33 |
+
gradio==4.41.0
|
34 |
+
gradio_client==1.3.0
|
35 |
+
h11==0.14.0
|
36 |
+
httpcore==1.0.5
|
37 |
+
httpx==0.27.0
|
38 |
+
huggingface-hub==0.24.5
|
39 |
+
idna==3.7
|
40 |
+
importlib_resources==6.4.0
|
41 |
+
ipykernel==6.29.5
|
42 |
+
ipython==8.26.0
|
43 |
+
isoduration==20.11.0
|
44 |
+
jedi==0.19.1
|
45 |
+
Jinja2==3.1.4
|
46 |
+
jiter==0.5.0
|
47 |
+
json5==0.9.25
|
48 |
+
jsonpointer==3.0.0
|
49 |
+
jsonschema==4.23.0
|
50 |
+
jsonschema-specifications==2023.12.1
|
51 |
+
jupyter-events==0.10.0
|
52 |
+
jupyter-lsp==2.2.5
|
53 |
+
jupyter_client==8.6.2
|
54 |
+
jupyter_core==5.7.2
|
55 |
+
jupyter_server==2.14.2
|
56 |
+
jupyter_server_terminals==0.5.3
|
57 |
+
jupyterlab==4.2.4
|
58 |
+
jupyterlab_pygments==0.3.0
|
59 |
+
jupyterlab_server==2.27.3
|
60 |
+
kiwisolver==1.4.5
|
61 |
+
markdown-it-py==3.0.0
|
62 |
+
MarkupSafe==2.1.5
|
63 |
+
matplotlib==3.9.2
|
64 |
+
matplotlib-inline==0.1.7
|
65 |
+
mdurl==0.1.2
|
66 |
+
mistune==3.0.2
|
67 |
+
nbclient==0.10.0
|
68 |
+
nbconvert==7.16.4
|
69 |
+
nbformat==5.10.4
|
70 |
+
nest-asyncio==1.6.0
|
71 |
+
notebook_shim==0.2.4
|
72 |
+
numpy==2.0.1
|
73 |
+
openai==1.40.6
|
74 |
+
orjson==3.10.7
|
75 |
+
overrides==7.7.0
|
76 |
+
packaging==24.1
|
77 |
+
pandas==2.2.2
|
78 |
+
pandocfilters==1.5.1
|
79 |
+
parso==0.8.4
|
80 |
+
pexpect==4.9.0
|
81 |
+
pillow==10.4.0
|
82 |
+
platformdirs==4.2.2
|
83 |
+
prometheus_client==0.20.0
|
84 |
+
prompt_toolkit==3.0.47
|
85 |
+
psutil==6.0.0
|
86 |
+
ptyprocess==0.7.0
|
87 |
+
pure_eval==0.2.3
|
88 |
+
pycparser==2.22
|
89 |
+
pydantic==2.8.2
|
90 |
+
pydantic_core==2.20.1
|
91 |
+
pydub==0.25.1
|
92 |
+
Pygments==2.18.0
|
93 |
+
pyparsing==3.1.2
|
94 |
+
python-dateutil==2.9.0.post0
|
95 |
+
python-dotenv==1.0.1
|
96 |
+
python-json-logger==2.0.7
|
97 |
+
python-multipart==0.0.9
|
98 |
+
pytz==2024.1
|
99 |
+
PyYAML==6.0.2
|
100 |
+
pyzmq==26.1.0
|
101 |
+
referencing==0.35.1
|
102 |
+
requests==2.32.3
|
103 |
+
rfc3339-validator==0.1.4
|
104 |
+
rfc3986-validator==0.1.1
|
105 |
+
rich==13.7.1
|
106 |
+
rpds-py==0.20.0
|
107 |
+
ruff==0.5.7
|
108 |
+
semantic-version==2.10.0
|
109 |
+
Send2Trash==1.8.3
|
110 |
+
shellingham==1.5.4
|
111 |
+
six==1.16.0
|
112 |
+
sniffio==1.3.1
|
113 |
+
soupsieve==2.6
|
114 |
+
stack-data==0.6.3
|
115 |
+
starlette==0.37.2
|
116 |
+
terminado==0.18.1
|
117 |
+
tinycss2==1.3.0
|
118 |
+
tomli==2.0.1
|
119 |
+
tomlkit==0.12.0
|
120 |
+
tornado==6.4.1
|
121 |
+
tqdm==4.66.5
|
122 |
+
traitlets==5.14.3
|
123 |
+
typer==0.12.3
|
124 |
+
types-python-dateutil==2.9.0.20240316
|
125 |
+
typing_extensions==4.12.2
|
126 |
+
tzdata==2024.1
|
127 |
+
uri-template==1.3.0
|
128 |
+
urllib3==2.2.2
|
129 |
+
uvicorn==0.30.6
|
130 |
+
wcwidth==0.2.13
|
131 |
+
webcolors==24.8.0
|
132 |
+
webencodings==0.5.1
|
133 |
+
websocket-client==1.8.0
|
134 |
+
websockets==12.0
|
src/__init__.py
ADDED
File without changes
|
src/app.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
from PIL import Image
|
3 |
+
|
4 |
+
sys.path.append(".")
|
5 |
+
from utils import (
|
6 |
+
generate_dummy_data,
|
7 |
+
generate_summary,
|
8 |
+
generate_welcome_message,
|
9 |
+
)
|
10 |
+
from prompts import CAMPAIGN_DESCR
|
11 |
+
|
12 |
+
import gradio as gr
|
13 |
+
|
14 |
+
|
15 |
+
df = generate_dummy_data()
|
16 |
+
print(df)
|
17 |
+
|
18 |
+
campaign_names = list(CAMPAIGN_DESCR.keys())
|
19 |
+
|
20 |
+
|
21 |
+
def process_customer_selection(customer_name, campaign_name):
|
22 |
+
customer_data = df[df["Name"] == customer_name].iloc[0]
|
23 |
+
print(customer_data)
|
24 |
+
summary = generate_summary(customer_data)
|
25 |
+
message = generate_welcome_message(summary, campaign_name)
|
26 |
+
|
27 |
+
# select the appropriate hero image for each campaign
|
28 |
+
if campaign_name == "Summer Clearance":
|
29 |
+
campaign_image = "../img/strung-beads.png"
|
30 |
+
elif campaign_name == "Makehaul":
|
31 |
+
campaign_image = "../img/canvas.png"
|
32 |
+
elif campaign_name == "Lowest Prices Of the Season":
|
33 |
+
campaign_image = "../img/photo-keeper.png"
|
34 |
+
else:
|
35 |
+
print("Invalid Campaign Name")
|
36 |
+
|
37 |
+
image = Image.open(campaign_image)
|
38 |
+
|
39 |
+
return summary, message, image
|
40 |
+
|
41 |
+
|
42 |
+
def create_gradio_app():
|
43 |
+
customer_names = df["Name"].tolist()
|
44 |
+
|
45 |
+
interface = gr.Interface(
|
46 |
+
fn=process_customer_selection,
|
47 |
+
inputs=[
|
48 |
+
gr.Dropdown(choices=customer_names, label="Select a Customer"),
|
49 |
+
gr.Dropdown(choices=campaign_names, label="Select a Campaign"),
|
50 |
+
],
|
51 |
+
outputs=[
|
52 |
+
gr.Textbox(label="Customer Summary [Input]"),
|
53 |
+
gr.Textbox(label="Personalized Welcome Message [Output]"),
|
54 |
+
gr.Image(label="Campaign Hero Product"),
|
55 |
+
],
|
56 |
+
title="Personalized Email Content Generator",
|
57 |
+
)
|
58 |
+
|
59 |
+
interface.launch(share=True)
|
60 |
+
|
61 |
+
|
62 |
+
# Run the Gradio app
|
63 |
+
create_gradio_app()
|
src/prompts.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
SYSTEM_PROMPT = (
|
2 |
+
f"You are a marketing manager who can create personalized messages for customers based on some transaction and segment data about them."
|
3 |
+
f"Make sure you mention the latest product that the customer purchased and also the customer's recency of purchase. However, don't mention the exact number of days. Just use qualitative words such as 'recently', 'a while ago', etc."
|
4 |
+
f"Recognize the customer's favorite department but don't mention the department name explicitly."
|
5 |
+
f"The message should be friendly and encourage the customer to make another purchase. Use the campaign hero product for this."
|
6 |
+
f"The message should be about 70 to 100 words long."
|
7 |
+
f"Remember to keep the message professional and respectful. Do not include any exclusive offers or discounts in the message."
|
8 |
+
f"Also generate the subject line for the email. The subject line should be about 5 to 10 words long."
|
9 |
+
f"The subject line may contain a relevant emoji or two."
|
10 |
+
f"Don't use the customer's name in the message or any other personal information."
|
11 |
+
f"In addition, please create a 'hero message' for this customer. The hero message is like a tagline to inspiring the customer. Here are a few examples: 'Craft it yourself', 'Express your creativity', 'Keep inspiring'. It should be less than six words long."
|
12 |
+
)
|
13 |
+
|
14 |
+
PERSONA_DESCR = {
|
15 |
+
"Artist": (
|
16 |
+
f"\nThe following type of message would appeal to this customer: "
|
17 |
+
f"Michaels helps you make what no one else can. "
|
18 |
+
f"Position the brand as a place for artists and creatives to bring their ideas to life with essentials at good value, with a bit of whimsy and magic to be discovered."
|
19 |
+
),
|
20 |
+
"Creator": (
|
21 |
+
f"\nThe following type of message would appeal to this customer: "
|
22 |
+
f"Delight and inspire your biggest fans by giving them what they love, which is variety and inspiration. Explore ways to re-think product selection, notably in décor, to increase spend across the store."
|
23 |
+
),
|
24 |
+
"Curator": (
|
25 |
+
f"\nThe following type of message would appeal to this customer: "
|
26 |
+
f"The curator cares about making their homes and spaces beautiful and unique. "
|
27 |
+
f"Show them that Michaels is the place to get that trend they’re inspired by without losing the unique touch that makes it their own."
|
28 |
+
),
|
29 |
+
"Seeker": (
|
30 |
+
f"\nThe following type of message would appeal to this customer: "
|
31 |
+
f"Make their shopping experience quick and remove friction. "
|
32 |
+
f"They are busy, but motivated and enjoy a little retail therapy. "
|
33 |
+
f"Encourage projects and products that aren’t overly complicated, kits that eliminate multiple trips and planning, and fun for kids."
|
34 |
+
),
|
35 |
+
}
|
36 |
+
|
37 |
+
CAMPAIGN_DESCR = {
|
38 |
+
"Summer Clearance": (
|
39 |
+
f"Campaign Name: Summer Clearance\n"
|
40 |
+
f"Campaign Objective: 'This campaign should encourage customers to shop now and get up to 70% off even more items during Clearance Event. This is an online-only event offering 70% off on all custom frame collections and other items.'\n"
|
41 |
+
),
|
42 |
+
"Makehaul": (
|
43 |
+
f"Campaign Name: Makehaul\n"
|
44 |
+
f"Campaign Objective: 'This campaign offers BOGO (Buy One Get One Free) offers on thousands of items. Customers can get 70% off on all custom frames collections.'\n"
|
45 |
+
),
|
46 |
+
"Lowest Prices Of the Season": (
|
47 |
+
f"Campaign Name: Lowest Prices Of the Season\n"
|
48 |
+
f"Campaign Objective: 'This campaign comes with the BEST offers on thousands of items. These offers are rare as they happen only three times a year.'\n"
|
49 |
+
),
|
50 |
+
}
|
src/utils.py
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
from openai import OpenAI
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
import os
|
5 |
+
|
6 |
+
import sys
|
7 |
+
|
8 |
+
sys.path.append(".")
|
9 |
+
from prompts import SYSTEM_PROMPT, PERSONA_DESCR, CAMPAIGN_DESCR
|
10 |
+
|
11 |
+
load_dotenv("../.env")
|
12 |
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
13 |
+
|
14 |
+
|
15 |
+
def generate_summary(customer_data):
|
16 |
+
customer_value = "high" if customer_data["Revenue"] >= 250 else "low"
|
17 |
+
customer_recency = "recent" if customer_data["Recency"] <= 60 else "not recent"
|
18 |
+
|
19 |
+
seg = customer_data["Segment"]
|
20 |
+
if seg == "Artist":
|
21 |
+
cust_seg_descr = PERSONA_DESCR["Artist"]
|
22 |
+
elif seg == "Creator":
|
23 |
+
cust_seg_descr = PERSONA_DESCR["Creator"]
|
24 |
+
elif seg == "Curator":
|
25 |
+
cust_seg_descr = PERSONA_DESCR["Curator"]
|
26 |
+
elif seg == "Seeker":
|
27 |
+
cust_seg_descr = PERSONA_DESCR["Seeker"]
|
28 |
+
else:
|
29 |
+
print("Invalid Segment")
|
30 |
+
|
31 |
+
summary = (
|
32 |
+
f"This is a {customer_value} value customer who has spent a total of {customer_data['Revenue']} dollars. "
|
33 |
+
f"Their most recent transaction was {customer_data['Recency']} days ago, which makes him/her a {customer_recency} customer. "
|
34 |
+
f"The latest product they purchased was {customer_data['LatestProduct']}. "
|
35 |
+
f"Their favorite department is {customer_data['FavoriteDepartment']}. "
|
36 |
+
f"{cust_seg_descr} ({seg})"
|
37 |
+
)
|
38 |
+
return summary
|
39 |
+
|
40 |
+
|
41 |
+
def generate_welcome_message(summary, campaign_name):
|
42 |
+
campaign_description = "The personalize message should align with the marketing campaign described below: "
|
43 |
+
|
44 |
+
if campaign_name == "Summer Clearance":
|
45 |
+
campaign_description += CAMPAIGN_DESCR["Summer Clearance"] + "\n"
|
46 |
+
elif campaign_name == "Makehaul":
|
47 |
+
campaign_description += CAMPAIGN_DESCR["Makehaul"] + "\n"
|
48 |
+
elif campaign_name == "Lowest Prices Of the Season":
|
49 |
+
campaign_description += CAMPAIGN_DESCR["Lowest Prices Of the Season"] + "\n"
|
50 |
+
else:
|
51 |
+
print("Invalid Campaign Name")
|
52 |
+
|
53 |
+
summary += f"{campaign_description}"
|
54 |
+
|
55 |
+
response = client.chat.completions.create(
|
56 |
+
model="gpt-4o-mini",
|
57 |
+
messages=[
|
58 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
59 |
+
{"role": "user", "content": summary},
|
60 |
+
],
|
61 |
+
)
|
62 |
+
|
63 |
+
message = response.choices[0].message.content
|
64 |
+
return message
|
65 |
+
|
66 |
+
|
67 |
+
def generate_dummy_data():
|
68 |
+
data = {
|
69 |
+
"Name": [
|
70 |
+
"Alice",
|
71 |
+
"Bob",
|
72 |
+
"Charlie",
|
73 |
+
"David",
|
74 |
+
"Eva",
|
75 |
+
"Frank",
|
76 |
+
"Grace",
|
77 |
+
"Henry",
|
78 |
+
"Ivy",
|
79 |
+
"John",
|
80 |
+
],
|
81 |
+
"Recency": [20, 50, 100, 10, 120, 70, 30, 150, 60, 40],
|
82 |
+
"Revenue": [200, 350, 150, 400, 100, 250, 300, 80, 330, 280],
|
83 |
+
"LatestProduct": [
|
84 |
+
"All Strung Beads",
|
85 |
+
"Permanent and Removable Vinyl",
|
86 |
+
"Creatology Baskets",
|
87 |
+
"Kids Storage",
|
88 |
+
"Lap Trays",
|
89 |
+
"Lemax Spooky Town Collection",
|
90 |
+
"Frames & Shadow Boxes",
|
91 |
+
"ALL Fall Stems",
|
92 |
+
"Folding Step Stool",
|
93 |
+
"Mini Sticker Printer",
|
94 |
+
],
|
95 |
+
"FavoriteDepartment": [
|
96 |
+
"KIDS ART & EDUCATION",
|
97 |
+
"HOBBY CRAFTS",
|
98 |
+
"BAKEWARE",
|
99 |
+
"FRAMES",
|
100 |
+
"PAPERCRAFTING",
|
101 |
+
"STORAGE",
|
102 |
+
"HOBBY CRAFTS",
|
103 |
+
"FLORAL ACCESSORIES",
|
104 |
+
"HOBBY CRAFTS",
|
105 |
+
"KIDS ART & EDUCATION",
|
106 |
+
],
|
107 |
+
"Segment": [
|
108 |
+
"Artist",
|
109 |
+
"Creator",
|
110 |
+
"Curator",
|
111 |
+
"Artist",
|
112 |
+
"Seeker",
|
113 |
+
"Curator",
|
114 |
+
"Artist",
|
115 |
+
"Seeker",
|
116 |
+
"Creator",
|
117 |
+
"Curator",
|
118 |
+
],
|
119 |
+
}
|
120 |
+
|
121 |
+
return pd.DataFrame(data)
|