bsassoli commited on
Commit
7472f2d
·
verified ·
1 Parent(s): e933b86

Upload folder using huggingface_hub

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ test**
README.md CHANGED
@@ -1,12 +1,10 @@
1
  ---
2
- title: Smartrec
3
- emoji: 📉
4
- colorFrom: purple
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 5.6.0
8
- app_file: app.py
9
- pinned: false
10
  ---
 
 
 
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: smartrec
3
+ app_file: app.py
 
 
4
  sdk: gradio
5
  sdk_version: 5.6.0
 
 
6
  ---
7
+ # smartrec
8
+
9
+ AI-powered personalized newsletters.
10
 
 
app.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import gradio as gr
3
+ import json
4
+ from openai import OpenAI
5
+ from src.utils import LLMHandler, format_prompt
6
+ from src.utils_api import get_recommendations
7
+ import yaml
8
+ import logging
9
+ import time
10
+ import argparse
11
+ import os
12
+ import tempfile
13
+
14
+
15
+ # logging.basicConfig(filename='logs/app.log', encoding='utf-8', level=logging.DEBUG)
16
+ logging.basicConfig(level=logging.DEBUG)
17
+
18
+
19
+ def main():
20
+
21
+ # get arguments with argparse
22
+ parser = argparse.ArgumentParser(description='Newsletter Generator')
23
+ parser.add_argument('--config-file', type=str, default='./config/config.yaml', help='Path to the configuration file.')
24
+ args = parser.parse_args()
25
+
26
+ logging.info("Starting the Newsletter Generator app...")
27
+
28
+ # Load configuration from YAML file
29
+ logging.debug("Loading configuration from config.yaml...")
30
+ with open(args.config_file, "r") as file:
31
+ config = yaml.safe_load(file)
32
+
33
+ # set environment variables for the recommender API
34
+ os.environ["RECOMMENDER_URL"] = config['recommender_api']['base_url']
35
+ os.environ["RECOMMENDER_KEY"] = config['recommender_api']['key']
36
+
37
+ # LLM settings
38
+ os.environ["OPENAI_KEY"] = config['llm']['api_key']
39
+ llm_settings = config['llm']
40
+
41
+ newsletter_meta_info = config['newsletter']
42
+
43
+ logging.debug(f"Configuration loaded: {config}")
44
+
45
+ # Initialize the LLM handler
46
+ logging.debug("Initializing the LLM handler...")
47
+ llm_handler = LLMHandler(**llm_settings)
48
+ logging.debug(f"LLM handler initialized with the following settings: {config['llm']}")
49
+
50
+
51
+ # Define the function to generate the newsletter using the OpenAI API
52
+ def generate_newsletter(
53
+ customer_id,
54
+ model_name,
55
+ temperature,
56
+ max_tokens,
57
+ system_message,
58
+ newsletter_preferences
59
+ ):
60
+
61
+
62
+ # get recommendations
63
+ logging.debug("Getting recommendations...")
64
+ customer_info, recommendations, transactions = get_recommendations(customer_id, max_recs=newsletter_meta_info['max_recommendations'], max_transactions=newsletter_meta_info['max_recents_items'])
65
+ logging.debug(f"Recommendations: {recommendations}")
66
+
67
+ logging.debug(f"Transactions: {transactions}")
68
+
69
+ # create the prompt
70
+ logging.debug("Formatting the prompt...")
71
+ prompt = format_prompt(
72
+ customer_info,
73
+ recommendations,
74
+ transactions,
75
+ newsletter_preferences,
76
+ newsletter_meta_info
77
+ )
78
+
79
+ # generate the newsletter text and images using llm_handler
80
+ logging.debug("Generating the newsletter text...")
81
+ newsletter_text = llm_handler.generate_text(
82
+ prompt,
83
+ model_name=model_name,
84
+ temperature=temperature,
85
+ max_tokens=max_tokens,
86
+ system_message=system_message
87
+ )
88
+
89
+ # Save HTML to a temporary file
90
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_file:
91
+ temp_file.write(newsletter_text.encode("utf-8"))
92
+ temp_file_path = temp_file.name
93
+
94
+
95
+ return newsletter_text, temp_file_path
96
+
97
+ # Gradio interface for the app
98
+ logging.debug("Creating interface...")
99
+ with gr.Blocks() as demo:
100
+ gr.Markdown("### Newsletter Generator")
101
+
102
+ customer_id = gr.Textbox(label="Client ID", value="04a183a27a6877e560e1025216d0a3b40d88668c68366da17edfb18ed89c574c")
103
+ newsletter_preferences = gr.Textbox(label="Newsletter preferences", placeholder="The newsletter should be catchy.")
104
+
105
+ # llm_preferences = gr.Textbox(label="LLM Preferences", placeholder="Enter LLM preferences.", visible=False)
106
+ # create an openable block for the llm preferences
107
+ with gr.Accordion("LLM Preferences", open=False):
108
+ model_name = gr.Dropdown(label="Model Name", choices=["gpt-3.5-turbo", "gpt-4-turbo"], value='gpt-3.5-turbo')
109
+ temperature = gr.Slider(label="Temperature", minimum=0.0, maximum=1.0, step=0.05, value=llm_handler.default_temperature)
110
+ max_tokens = gr.Number(label="Max Tokens", value=llm_handler.default_max_tokens)
111
+ system_message = gr.Textbox(label="System Message", placeholder="Enter the system message or Leave Blank.", value=llm_handler.default_system_message)
112
+
113
+ generate_button = gr.Button("Generate Newsletter")
114
+
115
+ # create a button to open the newsletter in a new tab
116
+ download = gr.DownloadButton(label="Download Newsletter")
117
+
118
+ with gr.Accordion("Newsletter", open=False):
119
+ newsletter_output = gr.HTML(label="Generated Newsletter")
120
+
121
+ generate_button.click(
122
+ fn=generate_newsletter,
123
+ inputs=[
124
+ customer_id,
125
+ model_name,
126
+ temperature,
127
+ max_tokens,
128
+ system_message,
129
+ newsletter_preferences],
130
+ outputs=[newsletter_output, download]
131
+ )
132
+
133
+ demo.launch(
134
+ share=True,
135
+ )
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()
config/config.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # llm configs
3
+ llm:
4
+ api_key: "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"
5
+ model_name: "gpt-4o"
6
+ default_temperature: 0.8
7
+ default_max_tokens: 3000
8
+ default_system_message: null
9
+
10
+
11
+ recommender_api:
12
+ base_url: "https://urfjxlnyxlehjqlucvuu.functions.supabase.co"
13
+ key: "CFSekdHEW2EPasoqjWTuPpvVHYQukD3ootXhd3JVUXdpuEea5d"
14
+
15
+ # app frontend
16
+ app:
17
+ server_port: 7860
18
+
19
+ # templates for newsletters and prompts
20
+ newsletter:
21
+ newsletter_example_path: "./newsletter_examples/2.txt"
22
+ prompt_template_path: "./prompt_templates/1.txt"
23
+ brand_logo: "https://seeklogo.com/images/L/luisa-spagnoli-logo-EF482BEE89-seeklogo.com.png"
24
+ brand_name: "AI x Fashion"
25
+ max_recommendations: 4
26
+ max_recents_items: 2
27
+
28
+ #personalised_text_example_path: "./newsletter_examples/1.txt"
29
+ #personalised_text_prompt_template_path: "./prompt_templates/1.txt"
30
+
31
+ # logo luisa spagnoli https://seeklogo.com/images/L/luisa-spagnoli-logo-EF482BEE89-seeklogo.com.png
32
+ # logo h&m https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/H%26M-Logo.svg/709px-H%26M-Logo.svg.png
logs/app.log ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ INFO:root:Starting the Newsletter Generator app...
2
+ INFO:root:Loading configuration from config.yaml...
3
+ INFO:root:Initializing the LLM handler...
newsletter_examples/0.txt ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Your Personal Style Update</title>
7
+ <style>
8
+ /* Reset styles for email clients */
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ width: 100%;
13
+ font-family: Arial, sans-serif;
14
+ line-height: 1.6;
15
+ color: #000000;
16
+ }
17
+
18
+ /* Container styles */
19
+ .container {
20
+ max-width: 600px;
21
+ margin: 0 auto;
22
+ padding: 20px;
23
+ background-color: #ffffff;
24
+ border: 1px solid #cccccc;
25
+ }
26
+
27
+ /* Header styles */
28
+ .header {
29
+ text-align: center;
30
+ padding: 20px 0;
31
+ border-bottom: 2px solid #000000;
32
+ }
33
+
34
+ .header img {
35
+ max-width: 150px;
36
+ height: auto;
37
+ }
38
+
39
+ /* Section styles */
40
+ .section {
41
+ margin: 30px 0;
42
+ }
43
+
44
+ /* Deal styles */
45
+ .deal-box {
46
+ background-color: #e6e6e6;
47
+ padding: 15px;
48
+ margin: 10px 0;
49
+ border-radius: 5px;
50
+ border: 2px solid #000000;
51
+ }
52
+
53
+ /* Product and bought item styles */
54
+ .product, .bought-item {
55
+ display: inline-block;
56
+ width: 45%;
57
+ margin: 10px;
58
+ text-align: center;
59
+ vertical-align: top;
60
+ border: 1px solid #cccccc;
61
+ padding: 10px;
62
+ }
63
+
64
+ .product img, .bought-item img {
65
+ width: 100%;
66
+ max-width: 200px;
67
+ max-height: 250px;
68
+ height: auto;
69
+ }
70
+
71
+ /* Button styles */
72
+ .button {
73
+ display: inline-block;
74
+ padding: 12px 25px;
75
+ background-color: #000000;
76
+ color: #ffffff;
77
+ text-decoration: none;
78
+ border-radius: 3px;
79
+ margin: 10px;
80
+ font-weight: bold;
81
+ }
82
+
83
+ /* Footer styles */
84
+ .footer {
85
+ text-align: center;
86
+ padding: 20px;
87
+ background-color: #e6e6e6;
88
+ margin-top: 30px;
89
+ border-top: 2px solid #000000;
90
+ }
91
+
92
+ /* Enhanced text styles */
93
+ h1, h2, h3 {
94
+ color: #000000;
95
+ font-weight: bold;
96
+ }
97
+
98
+ /* Link styles */
99
+ a {
100
+ color: #0066cc;
101
+ text-decoration: underline;
102
+ }
103
+
104
+ /* Price style */
105
+ .price {
106
+ font-size: 1.2em;
107
+ font-weight: bold;
108
+ color: #cc0000;
109
+ }
110
+ </style>
111
+ </head>
112
+ <body>
113
+ <div class="container">
114
+ <!-- Header Section -->
115
+ <div class="header">
116
+ <img src="${brand_logo}" alt="Brand Logo">
117
+ </div>
118
+
119
+ <!-- Personal Greeting -->
120
+ <div class="section">
121
+ <h1>Hello [Customer_Name],</h1>
122
+ <p style="font-size: 16px;">We've curated some special picks just for you based on your style preferences.</p>
123
+ <p style="font-size: 16px;">[Personalized text based on preferences]</p>
124
+ </div>
125
+
126
+ <!-- Previously Bought Items Section -->
127
+ <div class="section">
128
+ <h2>Items You Recently Loved</h2>
129
+ <p style="font-size: 16px;">Here are a few items you've purchased recently. We thought you might like similar pieces!</p>
130
+ <div class="bought-item">
131
+ <img src="/api/placeholder/200/250" alt="Previous Product 1">
132
+ <h3>Casual Linen Shirt</h3>
133
+ <p class="price">$49.99</p>
134
+ <p style="font-size: 14px; color: #666666;">Perfect for casual outings, pair it with your favorite jeans!</p>
135
+ </div>
136
+ <div class="bought-item">
137
+ <img src="/api/placeholder/200/250" alt="Previous Product 2">
138
+ <h3>Classic Denim Jacket</h3>
139
+ <p class="price">$89.99</p>
140
+ <p style="font-size: 14px; color: #666666;">A staple in every wardrobe, layer it over a T-shirt or dress.</p>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Deals Section -->
145
+ <div class="section">
146
+ <h2 style="color: #cc0000;">This Week's Exclusive Deals</h2>
147
+ <div class="deal-box">
148
+ <h3>🌟 30% OFF ALL DRESSES</h3>
149
+ <p style="font-weight: bold;">Use code: SUMMER30</p>
150
+ </div>
151
+ <div class="deal-box">
152
+ <h3>👠 BUY 2 GET 1 FREE</h3>
153
+ <p style="font-weight: bold;">On all accessories</p>
154
+ </div>
155
+ </div>
156
+
157
+ <!-- Recommended Items -->
158
+ <div class="section">
159
+ <h2>Picked Just For You</h2>
160
+ <div class="product">
161
+ <img src="/api/placeholder/200/250" alt="Product 1">
162
+ <h3>Silk Midi Dress</h3>
163
+ <p class="price">$129.99</p>
164
+ <a href="#" class="button">Shop Now</a>
165
+ </div>
166
+ <div class="product">
167
+ <img src="/api/placeholder/200/250" alt="Product 2">
168
+ <h3>Classic Blazer</h3>
169
+ <p class="price">$189.99</p>
170
+ <a href="#" class="button">Shop Now</a>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Call to Action -->
175
+ <div class="section" style="text-align: center;">
176
+ <h2>Ready to Refresh Your Wardrobe?</h2>
177
+ <a href="#" class="button">View All New Arrivals</a>
178
+ <a href="#" class="button" style="background-color: #006600;">Book Styling Session</a>
179
+ </div>
180
+
181
+ <!-- Footer -->
182
+ <div class="footer">
183
+ <p style="font-weight: bold;">Stay stylish,<br>The ${brand_name} Team</p>
184
+ <p>
185
+ <small style="color: #000000;">
186
+ You received this email because you subscribed to our newsletter.
187
+ <a href="#">Unsubscribe</a> | <a href="#">View in browser</a>
188
+ </small>
189
+ </p>
190
+ </div>
191
+ </div>
192
+ </body>
193
+ </html>
newsletter_examples/1.txt ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Your Personal Style Update</title>
7
+ <style>
8
+ /* Reset styles for email clients */
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ width: 100%;
13
+ font-family: Arial, sans-serif;
14
+ line-height: 1.6;
15
+ color: #000000;
16
+ }
17
+
18
+ /* Container styles */
19
+ .container {
20
+ max-width: 600px;
21
+ margin: 0 auto;
22
+ padding: 20px;
23
+ background-color: #ffffff;
24
+ border: 1px solid #cccccc;
25
+ }
26
+
27
+ /* Header styles */
28
+ .header {
29
+ text-align: center;
30
+ padding: 20px 0;
31
+ border-bottom: 2px solid #000000;
32
+ }
33
+
34
+ /* Section styles */
35
+ .section {
36
+ margin: 30px 0;
37
+ }
38
+
39
+ /* Deal styles */
40
+ .deal-box {
41
+ background-color: #e6e6e6;
42
+ padding: 15px;
43
+ margin: 10px 0;
44
+ border-radius: 5px;
45
+ border: 2px solid #000000;
46
+ }
47
+
48
+ /* Product styles */
49
+ .product, .bought-item {
50
+ display: inline-block;
51
+ width: 45%;
52
+ margin: 10px;
53
+ text-align: center;
54
+ vertical-align: top;
55
+ border: 1px solid #cccccc;
56
+ padding: 10px;
57
+ }
58
+
59
+ /* Button styles */
60
+ .button {
61
+ display: inline-block;
62
+ padding: 12px 25px;
63
+ background-color: #000000;
64
+ color: #ffffff;
65
+ text-decoration: none;
66
+ border-radius: 3px;
67
+ margin: 10px;
68
+ font-weight: bold;
69
+ }
70
+
71
+ /* Footer styles */
72
+ .footer {
73
+ text-align: center;
74
+ padding: 20px;
75
+ background-color: #e6e6e6;
76
+ margin-top: 30px;
77
+ border-top: 2px solid #000000;
78
+ }
79
+
80
+ /* Enhanced text styles */
81
+ h1, h2, h3 {
82
+ color: #000000;
83
+ font-weight: bold;
84
+ }
85
+
86
+ /* Link styles */
87
+ a {
88
+ color: #0066cc;
89
+ text-decoration: underline;
90
+ }
91
+
92
+ /* Price style */
93
+ .price {
94
+ font-size: 1.2em;
95
+ font-weight: bold;
96
+ color: #cc0000;
97
+ }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <div class="container">
102
+ <!-- Header Section -->
103
+ <div class="header">
104
+ <img src="${brand_logo}" alt="Brand Logo">
105
+ </div>
106
+
107
+ <!-- Personal Greeting -->
108
+ <div class="section">
109
+ <h1>Hello [Customer_Name],</h1>
110
+ <p style="font-size: 16px;"> It's Paolo again, your personal fashion assistant! How is everything going there? [PERSONALIZED TEXT BASED ON USER TASTE AND PREFERENCES] </p>
111
+ </div>
112
+
113
+ <!-- Previously Bought Items Section -->
114
+ <div class="section">
115
+ <p style="font-size: 16px;"> I hope you are enjoying your latest purchased items! You recently bought:</p>
116
+ <div class="bought-item">
117
+ <img src="/api/placeholder/200/250" alt="Previous Product 1">
118
+ <h3>Casual Linen Shirt</h3>
119
+ <p class="price">$49.99</p>
120
+ <p style="font-size: 14px; color: #666666;">Perfect for casual outings, pair it with your favorite jeans!</p>
121
+ </div>
122
+ <div class="bought-item">
123
+ <img src="/api/placeholder/200/250" alt="Previous Product 2">
124
+ <h3>Classic Denim Jacket</h3>
125
+ <p class="price">$89.99</p>
126
+ <p style="font-size: 14px; color: #666666;">A staple in every wardrobe, layer it over a T-shirt or dress.</p>
127
+ </div>
128
+ <!-- Add a third item if desired -->
129
+ </div>
130
+
131
+
132
+ <!-- Recommended Items -->
133
+ <div class="section">
134
+ <p style="font-size: 16px;"> I hope you are enjoying your latest purchased items! Based on those, I would recommend the following: </p>
135
+
136
+ <h2>Picked Just For You</h2>
137
+ <div class="product">
138
+ <img src="/api/placeholder/200/250" alt="Product 1">
139
+ <h3>Silk Midi Dress</h3>
140
+ <p class="price">$129.99</p>
141
+ <a href="#" class="button">Shop Now</a>
142
+ </div>
143
+ <div class="product">
144
+ <img src="/api/placeholder/200/250" alt="Product 2">
145
+ <h3>Classic Blazer</h3>
146
+ <p class="price">$189.99</p>
147
+ <a href="#" class="button">Shop Now</a>
148
+ </div>
149
+ </div>
150
+
151
+
152
+ <!-- Deals Section -->
153
+ <div class="section">
154
+ <h2 style="color: #cc0000;">Nothing you like here ?</h2>
155
+ <p style="font-size: 16px;"> Well, I always try our best and recommend what we think would suit you well, but hey, we all make mistakes, right? </p>
156
+ <p style="font-size: 16px;"> No worries though, I still have a lot of stuff for you! Take a look at these weekly deals! </p>
157
+ <div class="deal-box">
158
+ <h3>🌟 30% OFF ALL DRESSES</h3>
159
+ <p style="font-weight: bold;">Use code: SUMMER30</p>
160
+ </div>
161
+ <div class="deal-box">
162
+ <h3>👠 BUY 2 GET 1 FREE</h3>
163
+ <p style="font-weight: bold;">On all accessories</p>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Call to Action -->
168
+ <div class="section" style="text-align: center;">
169
+ <h2>Ready to Refresh Your Wardrobe?</h2>
170
+ <a href="#" class="button">View All New Arrivals</a>
171
+ <a href="#" class="button" style="background-color: #006600;">Book Styling Session</a>
172
+ </div>
173
+
174
+ <!-- Footer -->
175
+ <div class="footer">
176
+ <p style="font-weight: bold;">Stay stylish,<br>The ${brand_name} Team</p>
177
+ <p>
178
+ <small style="color: #000000;">
179
+ You received this email because you subscribed to our newsletter.
180
+ <a href="#">Unsubscribe</a> | <a href="#">View in browser</a>
181
+ </small>
182
+ </p>
183
+ </div>
184
+ </div>
185
+ </body>
186
+ </html>
newsletter_examples/2.txt ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Your Personalized Style Curation</title>
7
+ <style>
8
+ /* Elegant, sophisticated color palette with light brown background */
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ width: 100%;
13
+ font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, serif;
14
+ line-height: 1.6;
15
+ color: #4A4A4A;
16
+ background-color: #F0E6D2; /* Light warm brown background */
17
+ }
18
+
19
+ .container {
20
+ max-width: 600px;
21
+ margin: 0 auto;
22
+ padding: 30px;
23
+ background-color: #FFFFFF;
24
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
25
+ border: 1px solid #E0D3BA; /* Slight border to complement background */
26
+ }
27
+
28
+ .header {
29
+ text-align: center;
30
+ padding: 20px 0;
31
+ border-bottom: 1px solid #D4AD7A;
32
+ margin-bottom: 30px;
33
+ }
34
+
35
+ .header img {
36
+ max-width: 200px;
37
+ height: auto;
38
+ }
39
+
40
+ .section {
41
+ margin-bottom: 40px;
42
+ }
43
+
44
+ h1, h2, h3 {
45
+ font-family: 'Didot', serif;
46
+ color: #5D4037; /* Deep brown for headings */
47
+ margin-bottom: 15px;
48
+ }
49
+
50
+ .bought-item, .product {
51
+ display: inline-block;
52
+ width: 45%;
53
+ margin: 10px;
54
+ text-align: center;
55
+ vertical-align: top;
56
+ padding: 20px;
57
+ background-color: #FFF8E7; /* Very light cream background */
58
+ border-radius: 8px;
59
+ border: 1px solid #E0D3BA;
60
+ transition: transform 0.3s ease;
61
+ }
62
+
63
+ .bought-item:hover, .product:hover {
64
+ transform: scale(1.02);
65
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
66
+ }
67
+
68
+ .deal-box {
69
+ background-color: #F5E6D3; /* Soft light brown */
70
+ padding: 20px;
71
+ margin: 15px 0;
72
+ border-radius: 8px;
73
+ text-align: center;
74
+ border: 1px solid #D4AD7A;
75
+ }
76
+
77
+ .button {
78
+ display: inline-block;
79
+ padding: 12px 25px;
80
+ background-color: #8B6914; /* Rich brown */
81
+ color: #FFFFFF;
82
+ text-decoration: none;
83
+ border-radius: 5px;
84
+ font-weight: 600;
85
+ text-transform: uppercase;
86
+ letter-spacing: 1px;
87
+ transition: background-color 0.3s ease;
88
+ }
89
+
90
+ .button:hover {
91
+ background-color: #5D4037; /* Darker brown */
92
+ }
93
+
94
+ .footer {
95
+ text-align: center;
96
+ padding: 20px;
97
+ background-color: #F5E6D3; /* Soft light brown */
98
+ border-top: 1px solid #D4AD7A;
99
+ font-size: 0.9em;
100
+ }
101
+
102
+ .price {
103
+ font-size: 1.2em;
104
+ color: #8B6914; /* Rich brown */
105
+ font-weight: bold;
106
+ }
107
+
108
+ a {
109
+ color: #8B6914; /* Rich brown */
110
+ text-decoration: none;
111
+ }
112
+
113
+ a:hover {
114
+ text-decoration: underline;
115
+ }
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <div class="container">
120
+ <!-- Header Section -->
121
+ <div class="header">
122
+ <img src="${brand_logo}" alt="Brand Logo">
123
+ </div>
124
+
125
+ <!-- Personal Greeting -->
126
+ <div class="section">
127
+ <h1>Hello [Customer_Name],</h1>
128
+ <p> I wanted to take a moment to highlight some of the great pieces you've recently added to your wardrobe. We've curated some items that I think you'll love looking back on - each one tells a bit of your style story.
129
+ Here's a fresh look at the items you brought home recently: [LONG PERSONALIZED TEXT BASED ON USER TASTE AND PREFERENCES] </p>
130
+ </div>
131
+
132
+ <!-- Previously Bought Items Section -->
133
+ <div class="section">
134
+
135
+ <div class="bought-item">
136
+ <img src=[IMAGE LINK] alt="Previous Product 1">
137
+ <h3>Casual Linen Shirt</h3>
138
+ <p class="price">$49.99</p>
139
+ <p>Perfect for refined casual moments, effortlessly paired with tailored pieces.</p>
140
+ </div>
141
+ <div class="bought-item">
142
+ <img src=[IMAGE LINK] alt="Previous Product 2">
143
+ <h3>Classic Denim Jacket</h3>
144
+ <p class="price">$89.99</p>
145
+ <p>A versatile staple that elevates any ensemble with understated sophistication.</p>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Recommended Items -->
150
+ <p style="font-size: 16px;"> I've been looking through your recent data and noticed some really interesting patterns in your recent fashion choices. I wanted to share some new recommendations based on your previous purchases that I think will continue to elevate your personal style. These suggestions aren't just random picks, but carefully curated pieces that connect with the aesthetic you've been developing. [PERSONALIZED TEXT TO RECOMMEND NEW ITEMS BASED ON PREVIOUSLY BOUGHT ONES] </p>
151
+ <div class="section">
152
+ <div class="product">
153
+ <img src=[IMAGE LINK] alt="Product 1">
154
+ <h3>Silk Midi Dress</h3>
155
+ <p class="price">$129.99</p>
156
+ <a href="#" class="button">Explore</a>
157
+ </div>
158
+ <div class="product">
159
+ <img src=[IMAGE LINK] alt="Product 2">
160
+ <h3>Classic Blazer</h3>
161
+ <p class="price">$189.99</p>
162
+ <a href="#" class="button">Discover</a>
163
+ </div>
164
+ </div>
165
+
166
+ <!-- Deals Section -->
167
+ <div class="section">
168
+ <h2 style="color: #8B6914;">Not Quite Your Style?</h2>
169
+ <p style="font-size: 16px;"> Well, I always try our best and recommend what we think would suit you well, but hey, we all make mistakes, right? </p>
170
+ <p style="font-size: 16px;"> No worries though, I still have a lot of stuff for you! Take a look at these weekly deals! </p>
171
+ <div class="deal-box">
172
+ <h3>🌟 30% OFF ALL DRESSES</h3>
173
+ <p>Exclusive Code: LSF30</p>
174
+ </div>
175
+ <div class="deal-box">
176
+ <h3>👠 BUY 2 GET 1 FREE</h3>
177
+ <p>On our curated accessories collection</p>
178
+ </div>
179
+ </div>
180
+
181
+ <!-- Call to Action -->
182
+ <div class="section" style="text-align: center;">
183
+ <h2>Ready to Refine Your Wardrobe?</h2>
184
+ <a href="#" class="button">View New Arrivals</a>
185
+ <a href="#" class="button" style="background-color: #5D4037;">Book Styling Consultation</a>
186
+ </div>
187
+
188
+ <!-- Footer -->
189
+ <div class="footer">
190
+ <p style="font-weight: bold;">The ${brand_name} Team</p>
191
+ <p>
192
+ <small>
193
+ You're receiving this curated selection because you're part of our stylish community.
194
+ <a href="#">Unsubscribe</a> | <a href="#">View in Browser</a>
195
+ </small>
196
+ </p>
197
+ </div>
198
+ </div>
199
+ </body>
200
+ </html>
newsletter_examples/personalised_text_1.txt ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Style Update for Sophia</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ margin: 0;
11
+ padding: 0;
12
+ background-color: #f9f9f9;
13
+ color: #333;
14
+ }
15
+ .container {
16
+ max-width: 600px;
17
+ margin: 20px auto;
18
+ background: #fff;
19
+ border-radius: 8px;
20
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
21
+ overflow: hidden;
22
+ }
23
+ .header {
24
+ background-color: #fddde6;
25
+ color: #d63384;
26
+ padding: 20px;
27
+ text-align: center;
28
+ }
29
+ .header h1 {
30
+ font-size: 24px;
31
+ margin: 0;
32
+ }
33
+ .body {
34
+ padding: 20px;
35
+ }
36
+ .body h2 {
37
+ font-size: 20px;
38
+ margin-bottom: 10px;
39
+ color: #d63384;
40
+ }
41
+ .highlight {
42
+ background-color: #ffeef2;
43
+ padding: 10px;
44
+ border-radius: 5px;
45
+ margin-bottom: 20px;
46
+ }
47
+ .highlight h3 {
48
+ margin: 0 0 10px;
49
+ color: #333;
50
+ }
51
+ .button {
52
+ display: inline-block;
53
+ background-color: #d63384;
54
+ color: #fff;
55
+ text-decoration: none;
56
+ padding: 10px 20px;
57
+ border-radius: 5px;
58
+ font-weight: bold;
59
+ text-align: center;
60
+ }
61
+ .footer {
62
+ text-align: center;
63
+ padding: 20px;
64
+ background-color: #f9f9f9;
65
+ font-size: 12px;
66
+ color: #777;
67
+ }
68
+ .footer a {
69
+ color: #d63384;
70
+ text-decoration: none;
71
+ }
72
+ @media (max-width: 600px) {
73
+ .container {
74
+ margin: 10px;
75
+ }
76
+ }
77
+ </style>
78
+ </head>
79
+ <body>
80
+ <div class="container">
81
+ <!-- Header Section -->
82
+ <div class="header">
83
+ <img src="${brand_logo}" alt="Brand Logo">
84
+ <h1>Style Update Just for You, Sophia! 💖</h1>
85
+ </div>
86
+
87
+ <!-- Body Section -->
88
+ <div class="body">
89
+ <h2>Hi Sophia, 🌸</h2>
90
+ <p>Your New York style just got a glow-up! Here’s what’s trending and totally YOU:</p>
91
+
92
+ <!-- Highlight Section -->
93
+ <div class="highlight">
94
+ <h3>✨ Trending Now:</h3>
95
+ <ul>
96
+ <li><b>Oversized Chic:</b> Pair your hoodie with chunky sneakers (we know it’s on your wishlist 😉).</li>
97
+ <li><b>Floral Fantasy:</b> Pastel pink and sage green are making waves—just like your boho maxi dress!</li>
98
+ </ul>
99
+ </div>
100
+
101
+ <!-- Recommendations Section -->
102
+ <h3>🛍️ This Week’s Picks for You:</h3>
103
+ <ul>
104
+ <li>🌸 <b>Dreamy Wide-Leg Jeans</b> (Pastel Pink Edition)</li>
105
+ <li>🌿 <b>Eco-Friendly Tote Bag</b> to match your sustainable vibe</li>
106
+ <li>🎀 <b>Vintage-Inspired Jacket</b> for the perfect streetwear look</li>
107
+ </ul>
108
+
109
+ <!-- Subscription Perks Section -->
110
+ <h3>💡 Your VIP Perks:</h3>
111
+ <p>
112
+ As a VIP, you get:
113
+ <ul>
114
+ <li>Early access to new collections</li>
115
+ <li>Exclusive discounts (hint: wide-leg jeans are on sale!)</li>
116
+ <li>Free style consultations tailored to your fav styles 💌</li>
117
+ </ul>
118
+ </p>
119
+
120
+ <!-- Call-to-Action -->
121
+ <a href="#" class="button">Shop Now</a>
122
+ </div>
123
+
124
+ <!-- Footer Section -->
125
+ <div class="footer">
126
+ <p>Thanks for being part of our style squad, Sophia! 💖</p>
127
+ <p><a href="#">Unsubscribe</a> | <a href="#">Update Preferences</a></p>
128
+ </div>
129
+ </div>
130
+ </body>
131
+ </html>
personalised_text.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import gradio as gr
3
+ import json
4
+ from openai import OpenAI
5
+ from src.utils import LLMHandler, format_prompt_personalsed_text
6
+ from src.utils_api import get_recommendations
7
+ import yaml
8
+ import logging
9
+ import time
10
+ import argparse
11
+ import os
12
+ import tempfile
13
+
14
+
15
+ # logging.basicConfig(filename='logs/app.log', encoding='utf-8', level=logging.DEBUG)
16
+ logging.basicConfig(level=logging.DEBUG)
17
+
18
+
19
+ def main():
20
+
21
+ # get arguments with argparse
22
+ parser = argparse.ArgumentParser(description='Newsletter Generator')
23
+ parser.add_argument('--config-file', type=str, default='./config/config.yaml', help='Path to the configuration file.')
24
+ args = parser.parse_args()
25
+
26
+ logging.info("Starting the Newsletter Generator app...")
27
+
28
+ # Load configuration from YAML file
29
+ logging.debug("Loading configuration from config.yaml...")
30
+ with open(args.config_file, "r") as file:
31
+ config = yaml.safe_load(file)
32
+
33
+ # set environment variables for the recommender API
34
+ os.environ["RECOMMENDER_URL"] = config['recommender_api']['base_url']
35
+ os.environ["RECOMMENDER_KEY"] = config['recommender_api']['key']
36
+
37
+ # LLM settings
38
+ os.environ["OPENAI_KEY"] = config['llm']['api_key']
39
+ llm_settings = config['llm']
40
+
41
+ newsletter_meta_info = config['newsletter']
42
+
43
+ logging.debug(f"Configuration loaded: {config}")
44
+
45
+ # Initialize the LLM handler
46
+ logging.debug("Initializing the LLM handler...")
47
+ llm_handler = LLMHandler(**llm_settings)
48
+ logging.debug(f"LLM handler initialized with the following settings: {config['llm']}")
49
+
50
+
51
+ # Define the function to generate the newsletter using the OpenAI API
52
+ def generate_newsletter(
53
+ customer_id,
54
+ model_name,
55
+ temperature,
56
+ max_tokens,
57
+ system_message,
58
+ newsletter_preferences
59
+ ):
60
+
61
+
62
+ # get recommendations
63
+ logging.debug("Getting recommendations...")
64
+ customer_info, recommendations, transactions = get_recommendations(customer_id)
65
+ logging.debug(f"Recommendations: {recommendations}")
66
+
67
+ logging.debug(f"Transactions: {transactions}")
68
+
69
+
70
+ # create the prompt
71
+ logging.debug("Formatting the prompt...")
72
+ prompt = format_prompt_personalsed_text(
73
+ customer_info,
74
+ recommendations,
75
+ transactions,
76
+ newsletter_preferences,
77
+ newsletter_meta_info
78
+ )
79
+
80
+ # generate the newsletter text and images using llm_handler
81
+ logging.debug("Generating the newsletter text...")
82
+ newsletter_text = llm_handler.generate_text(
83
+ prompt,
84
+ model_name=model_name,
85
+ temperature=temperature,
86
+ max_tokens=max_tokens,
87
+ system_message=system_message
88
+ )
89
+
90
+ # Save HTML to a temporary file
91
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_file:
92
+ temp_file.write(newsletter_text.encode("utf-8"))
93
+ temp_file_path = temp_file.name
94
+
95
+
96
+ return newsletter_text, temp_file_path
97
+
98
+ # Gradio interface for the app
99
+ logging.debug("Creating interface...")
100
+ with gr.Blocks() as demo:
101
+ gr.Markdown("### Newsletter Generator")
102
+
103
+ customer_id = gr.Textbox(label="Client ID", value="04a183a27a6877e560e1025216d0a3b40d88668c68366da17edfb18ed89c574c")
104
+ newsletter_preferences = gr.Textbox(label="Newsletter preferences", placeholder="The newsletter should be catchy.")
105
+
106
+ # llm_preferences = gr.Textbox(label="LLM Preferences", placeholder="Enter LLM preferences.", visible=False)
107
+ # create an openable block for the llm preferences
108
+ with gr.Accordion("LLM Preferences", open=False):
109
+ model_name = gr.Dropdown(label="Model Name", choices=["gpt-3.5-turbo", "gpt-4-turbo"], value='gpt-3.5-turbo')
110
+ temperature = gr.Slider(label="Temperature", minimum=0.0, maximum=1.0, step=0.05, value=llm_handler.default_temperature)
111
+ max_tokens = gr.Number(label="Max Tokens", value=llm_handler.default_max_tokens)
112
+ system_message = gr.Textbox(label="System Message", placeholder="Enter the system message or Leave Blank.", value=llm_handler.default_system_message)
113
+
114
+ generate_button = gr.Button("Generate Newsletter")
115
+
116
+ # create a button to open the newsletter in a new tab
117
+ download = gr.DownloadButton(label="Download Newsletter")
118
+
119
+ with gr.Accordion("Newsletter", open=False):
120
+ newsletter_output = gr.HTML(label="Generated Newsletter", value=open(config["newsletter"]["personalised_text_example_path"]).read())
121
+
122
+ generate_button.click(
123
+ fn=generate_newsletter,
124
+ inputs=[
125
+ customer_id,
126
+ model_name,
127
+ temperature,
128
+ max_tokens,
129
+ system_message,
130
+ newsletter_preferences],
131
+ outputs=[newsletter_output, download]
132
+ )
133
+
134
+ demo.launch(
135
+ server_port=config['app']['server_port'],
136
+ )
137
+
138
+ if __name__ == "__main__":
139
+ main()
prompt_templates/0.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ### Instructions:
3
+ Write the newsletter using the provided template, making it engaging and personalized for the client. Incorporate the recommended items naturally within the body of the newsletter, keeping a ${intonation} tone throughout. Adhere to the specified length and include relevant call-to-action phrases for each recommended item to increase client engagement.
4
+
5
+ Ensure the overall layout follows the template loosely. Make sure each recommended item has a brief but engaging introduction, highlighting why it suits the customer’s interests based on their history.
6
+
7
+ ### Newsletter template
8
+ ${newsletter_template}
9
+
10
+ ### Customer Information
11
+ ${customer_history}.
12
+
13
+ ### Previous Transactions
14
+ ${transactions}
15
+
16
+ ### Recommended Items
17
+ ${recommended_items}
18
+
prompt_templates/1.txt ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Create a personalized HTML newsletter. Start from the provided newsletter template and use the following specifications.
3
+
4
+
5
+ ### Preferences
6
+ Make it engaging and informal.
7
+ Add some personal lines that create a connection with the reader.
8
+ Highlight the connection between previously bought items and recommended ones.
9
+ Don't be afraid of adding textual content!
10
+ Do not change the prices of the items.
11
+ ${newsletter_settings}
12
+
13
+
14
+ ### Newsletter template
15
+ Use the provided HTML structure below as the template. Integrate the provided content, following the layout as closely as possible.
16
+ ${newsletter_template}
17
+
18
+
19
+ ### Customer Information
20
+ ${customer_info}.
21
+
22
+
23
+ ### Previously Bought Items
24
+ ${transactions}
25
+
26
+
27
+ ### Recommended Items
28
+ List the following recommended items with descriptions, images, and links:
29
+ ${recommended_items}
30
+
31
+
32
+ ### Additional Features
33
+ Add a [GREETING, DISCOUNT CODE, CTA (e.g., "Shop Now" or "Learn More"), UPCOMING EVENT], if specified:
34
+ - Greeting: Use a personalized greeting, such as “Hello, [CUSTOMER NAME]!”
35
+ - Discount Code (if any): [DISCOUNT CODE or DISCOUNT DETAILS]
36
+ - Call to Action : Use a button or link with text like "[CTA TEXT]" that leads to [CTA URL]
37
+
38
+
prompt_templates/personalised_text_1.txt ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are an AI writing a personalized newsletter for a user based on their profile data. Here is the user's information:
2
+
3
+ Name: ${name}
4
+ Email: ${email}
5
+ Preferences:
6
+ - Topics of interest: ${topics}
7
+ - Products they viewed: ${products_viewed}
8
+ Interaction frequency: ${interaction_frequency}
9
+ Subscription:
10
+ - Plan: ${plan}
11
+ Location: ${location}
12
+
13
+ Write a warm, engaging, and personalized newsletter for this user. Include:
14
+ 1. A personalized greeting.
15
+ 2. Highlights based on their preferences.
16
+ 3. Recommendations for products, articles, or upcoming events they might like.
17
+ 4. A reminder of their subscription benefits.
18
+ 5. A closing call-to-action encouraging further interaction.
19
+
20
+ Ensure the tone is friendly, professional, and resonates with their interests.
21
+
22
+ ### Preferences
23
+ ${newsletter_settings}
24
+
25
+ ### Newsletter template
26
+ Use the provided HTML structure below as the template. Integrate the provided content, following the layout and formatting as closely as possible.
27
+ ${newsletter_template}
28
+
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ pyyaml
3
+ openai
src/__pycache__/utils.cpython-311.pyc ADDED
Binary file (7.65 kB). View file
 
src/__pycache__/utils.cpython-312.pyc ADDED
Binary file (6.55 kB). View file
 
src/__pycache__/utils_api.cpython-311.pyc ADDED
Binary file (7.21 kB). View file
 
src/__pycache__/utils_api.cpython-312.pyc ADDED
Binary file (5.69 kB). View file
 
src/utils.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from openai import OpenAI
2
+ import os
3
+ from string import Template
4
+ import logging
5
+
6
+
7
+ class LLMHandler:
8
+ def __init__(self, api_key, model_name, default_temperature, default_max_tokens, default_system_message=None):
9
+
10
+ self.client = OpenAI(api_key=api_key)
11
+ self.model_name = model_name
12
+ self.default_temperature = default_temperature
13
+ self.default_max_tokens = default_max_tokens
14
+ self.default_system_message = default_system_message
15
+
16
+
17
+ def generate_text(self, prompt, model_name, temperature=None, max_tokens=None, system_message=None):
18
+ # prompt must be built outside
19
+
20
+ # optional parameters
21
+ model_name = model_name or self.model_name
22
+ temperature = temperature or self.default_temperature
23
+ max_tokens = max_tokens or self.default_max_tokens
24
+ system_message = system_message or self.default_system_message
25
+
26
+ # prepare messages
27
+ messages = [{"role": "user", "content": prompt}]
28
+ if system_message:
29
+ messages.insert(0, {"role": "system", "content": system_message})
30
+
31
+ # get response
32
+ logging.debug(f"Generating text with model {model_name}, temperature {temperature}, and max tokens {max_tokens}...")
33
+ logging.debug(f"Prompt: {messages}")
34
+ response = self.client.chat.completions.create(
35
+ model=model_name,
36
+ messages=messages,
37
+ temperature=temperature,
38
+ max_tokens=max_tokens
39
+ )
40
+ return response.choices[0].message.content
41
+
42
+
43
+ def format_prompt_personalsed_text(
44
+ customer_info,
45
+ recommendations,
46
+ transactions,
47
+ newsletter_settings,
48
+ newsletter_meta_info
49
+ ):
50
+
51
+ # get the paths to the template files
52
+ personalised_text_prompt_template_path = newsletter_meta_info['personalised_text_prompt_template_path']
53
+ personalised_text_example_path = newsletter_meta_info['personalised_text_example_path']
54
+
55
+
56
+ # load the template from file
57
+ logging.debug(f"Loading example from {personalised_text_example_path}...")
58
+ with open(personalised_text_example_path, "r") as f:
59
+ example = Template(f.read())
60
+
61
+ # load the template from file
62
+ logging.debug(f"Loading prompt template from {personalised_text_prompt_template_path}...")
63
+ with open(personalised_text_prompt_template_path, "r") as f:
64
+ template = Template(f.read())
65
+ example = example.safe_substitute(
66
+ brand_logo=newsletter_meta_info.get("brand_logo", ""),
67
+ brand_name=newsletter_meta_info.get("brand_name", ""),
68
+
69
+ )
70
+
71
+
72
+ # Sample JSON object
73
+ user_data = {
74
+ "user": {
75
+ "name": "Sophia",
76
+ "email": "[email protected]",
77
+ "preferences": {
78
+ "favorite_styles": ["streetwear", "boho", "vintage"],
79
+ "colors": ["pastel pink", "sage green", "cream"],
80
+ "recent_buys": ["oversized hoodie", "floral maxi dress"],
81
+ "wishlist_items": ["chunky sneakers", "wide-leg jeans"],
82
+ "interests": ["seasonal trends", "sustainable fashion"]
83
+ },
84
+ "interaction_frequency": "bi-weekly",
85
+ "subscription": {
86
+ "plan": "VIP",
87
+ "signup_date": "2024-03-10"
88
+ },
89
+ "location": "New York, NY",
90
+ "age_group": "18-24"
91
+ }
92
+ }
93
+
94
+ logging.debug("Formatting the prompt...")
95
+
96
+ user = user_data["user"]
97
+ preferences = user["preferences"]
98
+
99
+ prompt = template.safe_substitute(
100
+ name=user["name"],
101
+ email=user["email"],
102
+ topics=", ".join(preferences["interests"]),
103
+ plan=user["subscription"]["plan"],
104
+ interaction_frequency=user["interaction_frequency"],
105
+ products_viewed=", ".join(preferences["wishlist_items"]),
106
+ location=user["location"],
107
+ newsletter_settings=newsletter_settings,
108
+ newsletter_template=example
109
+ )
110
+
111
+ logging.debug(f"Formatted prompt: {prompt}")
112
+
113
+ return prompt
114
+
115
+
116
+ def format_prompt(
117
+ customer_info,
118
+ recommendations,
119
+ transactions,
120
+ newsletter_settings,
121
+ newsletter_meta_info
122
+ ):
123
+
124
+ # get the paths to the template files
125
+ prompt_template_path = newsletter_meta_info['prompt_template_path']
126
+ newsletter_example_path = newsletter_meta_info['newsletter_example_path']
127
+
128
+
129
+ # load the template from file
130
+ logging.debug(f"Loading example from {newsletter_example_path}...")
131
+ with open(newsletter_example_path, "r") as f:
132
+ example = Template(f.read())
133
+ example = example.safe_substitute(
134
+ brand_logo=newsletter_meta_info.get("brand_logo", ""),
135
+ brand_name=newsletter_meta_info.get("brand_name", ""),
136
+ )
137
+
138
+ # load the template from file
139
+ logging.debug(f"Loading prompt template from {prompt_template_path}...")
140
+ with open(prompt_template_path, "r") as f:
141
+ template = Template(f.read())
142
+
143
+ logging.debug("Formatting the prompt...")
144
+ prompt = template.safe_substitute(
145
+ customer_info=customer_info,
146
+ recommended_items=recommendations,
147
+ transactions=transactions,
148
+ newsletter_settings=newsletter_settings,
149
+ newsletter_template=example,
150
+ )
151
+ logging.debug(f"Formatted prompt: {prompt}")
152
+
153
+ return prompt
154
+
155
+
156
+ def load_newsletter_templates(templates_dir, num_examples=1):
157
+
158
+ # iterate over the files in the templates directory and load each template, then concatenate them into a single string
159
+ templates = []
160
+ for i, file in enumerate(os.listdir(templates_dir)):
161
+ with open(os.path.join(templates_dir, file), "r") as f:
162
+ templates.append(f.read())
163
+ if i == num_examples - 1:
164
+ break
165
+
166
+ # concatenate the templates into a single string, adding a newline and " Example numebr "
167
+ newsletter_template = "\n".join([f"Example {i+1}\n{template}\n\n\n\n" for i, template in enumerate(templates)])
168
+
169
+ return newsletter_template
src/utils_api.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ import os
4
+
5
+
6
+ def make_api_request(endpoint, ids):
7
+ """
8
+ Generic function to make API requests with error handling
9
+
10
+ :param endpoint: The specific API endpoint (e.g. 'get-images-from-id')
11
+ :param ids: List of IDs to query
12
+ :return: JSON response or None if error occurs
13
+ """
14
+ api_key = os.environ.get("RECOMMENDER_KEY")
15
+ if not api_key:
16
+ raise ValueError("API key is not set")
17
+ headers = {
18
+ "x-api-key": api_key,
19
+ "Content-Type": "application/json"
20
+ }
21
+
22
+ payload = {"ids": ids}
23
+
24
+ try:
25
+ recommender_url = os.environ.get("RECOMMENDER_URL")
26
+ if not recommender_url:
27
+ raise ValueError("Recommender URL is not set")
28
+ response = requests.post(f"{recommender_url}/{endpoint}",
29
+ headers=headers,
30
+ data=json.dumps(payload))
31
+
32
+ response.raise_for_status() # Raise an exception for bad status codes
33
+ return response.json()
34
+
35
+ except requests.exceptions.RequestException as e:
36
+ print(f"Error occurred while calling {endpoint}: {e}")
37
+ return None
38
+
39
+ def get_images_from_id(ids):
40
+ """Get images for specific product IDs"""
41
+ return make_api_request("get-images-from-id", ids)
42
+
43
+
44
+ def parse_article_data(article_data):
45
+ """Parse the article data and return a dictionary"""
46
+
47
+ # get the article information
48
+ article_info = article_data['article']
49
+ aricle_id = article_info.get('article_id', 'Unknown')
50
+ article_name = article_info.get('prod_name', 'Unknown')
51
+ article_type = article_info.get('department_name', 'Unknown')
52
+ article_color = article_info.get('perceived_colour_master_name', 'Unknown')
53
+
54
+ # get the article images
55
+ article_images = get_images_from_id([aricle_id])
56
+ img_url = 'no image'
57
+ if len(article_images) > 0:
58
+ img_url = article_images[0].get('url', 'no image')
59
+
60
+ return {
61
+ "product_name": article_name,
62
+ "product_type": article_type,
63
+ "product_color": article_color,
64
+ "product_image_url": img_url
65
+ }
66
+
67
+ def parse_transaction_data(transaction_data):
68
+ """Parse the transaction data and return a dictionary with article information."""
69
+
70
+ # Retrieve general transaction details
71
+ transaction_date = transaction_data.get('t_dat', 'Unknown')
72
+ sales_channel_id = transaction_data.get('sales_channel_id', 'Unknown')
73
+ price = transaction_data.get('price', 'Unknown')
74
+
75
+ # Get the article information
76
+ article_info = transaction_data.get('article', {})
77
+ article_id = article_info.get('article_id', 'Unknown')
78
+ article_name = article_info.get('prod_name', 'Unknown')
79
+ article_type = article_info.get('department_name', 'Unknown')
80
+ article_color = article_info.get('perceived_colour_master_name', 'Unknown')
81
+ article_detail = article_info.get('detail_desc', 'Unknown')
82
+
83
+ # Get the article images
84
+ article_images = get_images_from_id([article_id])
85
+ img_url = 'no image'
86
+ if article_images:
87
+ img_url = article_images[0].get('url', 'no image')
88
+
89
+ return {
90
+ "transaction_date": transaction_date,
91
+ "sales_channel_id": sales_channel_id,
92
+ "price": price,
93
+ "product_name": article_name,
94
+ "product_type": article_type,
95
+ "product_color": article_color,
96
+ "product_image_url": img_url,
97
+ "article_id": article_id
98
+ }
99
+
100
+
101
+
102
+ def parse_recommendations(recommendations, max_recs=4, max_transactions=2, only_with_images=True):
103
+ """Parse the recommendations and return a list of product IDs"""
104
+
105
+ # get the client information
106
+ customer_info = recommendations[0]['customer'][0]
107
+ customer_id = customer_info.get('customer_id', 'Unknown')
108
+ customer_name = customer_info.get('name', customer_id[:8]) # TODO change to real customer name
109
+ customer_age = customer_info.get('age', 'Unknown')
110
+
111
+ customer_info = {
112
+ "customer name": customer_name,
113
+ "customer age": customer_age
114
+ }
115
+
116
+ # get the recommendations
117
+ formatted_recommendations = [parse_article_data(article) for article in recommendations[0]['prediction']]
118
+
119
+ formatted_transactions = [parse_transaction_data(transaction) for transaction in recommendations[0]['transactions']]
120
+
121
+ if only_with_images:
122
+ formatted_recommendations = [rec for rec in formatted_recommendations if rec['product_image_url'] != 'no image']
123
+ formatted_transactions = [t for t in formatted_transactions if t['product_image_url'] != 'no image']
124
+
125
+ if len(formatted_recommendations) > max_recs:
126
+ formatted_recommendations = formatted_recommendations[:max_recs]
127
+
128
+ if len(formatted_transactions) > max_transactions:
129
+ formatted_transactions = formatted_transactions[:max_transactions]
130
+
131
+ return customer_info, formatted_recommendations, formatted_transactions
132
+
133
+ def get_recommendations(customer_ids, max_recs=4, max_transactions=2):
134
+ """Get product recommendations for specific customer IDs"""
135
+ if isinstance(customer_ids, str):
136
+ customer_ids = [customer_ids]
137
+ recommendations = make_api_request("get-recommendations", customer_ids)
138
+ if recommendations:
139
+ return parse_recommendations(recommendations, max_recs, max_transactions)
140
+ return None
141
+
142
+
143
+ if __name__ == "__main__":
144
+ # Example product IDs
145
+ product_ids = ["0110065002", "0111609001"]
146
+
147
+ # Example customer ID
148
+ customer_id = ["04a183a27a6877e560e1025216d0a3b40d88668c68366da17edfb18ed89c574c"]
149
+
150
+ # Demonstrate different API calls
151
+ print("Images:", get_images_from_id(product_ids))
152
+ print("Recommendations:", get_recommendations(customer_id))
153
+
154
+