Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,298 +2,155 @@ from dotenv import load_dotenv
|
|
| 2 |
import streamlit as st
|
| 3 |
import os
|
| 4 |
import google.generativeai as genai
|
| 5 |
-
import
|
| 6 |
-
from
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
#
|
|
|
|
|
|
|
| 11 |
load_dotenv()
|
| 12 |
|
| 13 |
-
# Configurar
|
| 14 |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
| 15 |
|
| 16 |
-
#
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
generation_config = {
|
| 23 |
-
"temperature": temperature,
|
| 24 |
-
"top_p": 0.65,
|
| 25 |
-
"top_k": 360,
|
| 26 |
-
"max_output_tokens": 8196,
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
model = genai.GenerativeModel(
|
| 30 |
-
model_name="gemini-2.0-flash",
|
| 31 |
-
generation_config=generation_config,
|
| 32 |
-
)
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
|
| 37 |
-
#
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
- Think of the angle as a "tone overlay" on the content
|
| 55 |
-
- The formula provides the structure, the angle provides the style
|
| 56 |
-
- Both must work together seamlessly
|
| 57 |
-
|
| 58 |
-
FORMAT EXAMPLE:
|
| 59 |
-
---
|
| 60 |
-
SUBJECT: [Attractive subject line]
|
| 61 |
-
|
| 62 |
-
[Email body with persuasive and emotional content]
|
| 63 |
-
|
| 64 |
-
[Clear call to action]
|
| 65 |
-
|
| 66 |
-
[Signature]
|
| 67 |
-
---
|
| 68 |
-
|
| 69 |
-
IMPORTANT:
|
| 70 |
-
- Each email must be unique and memorable
|
| 71 |
-
- Avoid clich茅s and generalities
|
| 72 |
-
- Maintain a persuasive but credible tone
|
| 73 |
-
- Adapt language to the target audience
|
| 74 |
-
- Focus on transformative benefits
|
| 75 |
-
- Follow the selected angle style while maintaining the structure"""
|
| 76 |
-
|
| 77 |
-
# Add creative idea to system prompt if it exists
|
| 78 |
-
if creative_idea:
|
| 79 |
-
system_prompt += f"""
|
| 80 |
-
|
| 81 |
-
CREATIVE CONCEPT:
|
| 82 |
-
Use the following creative concept as the central theme for all emails in the sequence:
|
| 83 |
-
"{creative_idea}"
|
| 84 |
-
|
| 85 |
-
CREATIVE CONCEPT INSTRUCTIONS:
|
| 86 |
-
1. This concept should be the unifying theme across all emails
|
| 87 |
-
2. Use it as a metaphor or analogy throughout the sequence
|
| 88 |
-
3. Develop different aspects of this concept in each email
|
| 89 |
-
4. Make sure the concept naturally connects to the product benefits
|
| 90 |
-
5. The concept should make the emails more memorable and engaging
|
| 91 |
-
"""
|
| 92 |
-
|
| 93 |
-
# Iniciar el prompt con las instrucciones del sistema
|
| 94 |
-
email_instruction = f"{system_prompt}\n\n"
|
| 95 |
-
|
| 96 |
-
# A帽adir contenido del archivo si existe
|
| 97 |
-
if file_content:
|
| 98 |
-
email_instruction += f"""
|
| 99 |
-
REFERENCE CONTENT:
|
| 100 |
-
Carefully analyze the following content as a reference for generating emails:
|
| 101 |
-
{file_content[:3000]}
|
| 102 |
-
|
| 103 |
-
ANALYSIS INSTRUCTIONS:
|
| 104 |
-
1. Extract key information about the product or service mentioned
|
| 105 |
-
2. Identify the tone, style, and language used
|
| 106 |
-
3. Detect any data about the target audience or customer avatar
|
| 107 |
-
4. Look for benefits, features, or pain points mentioned
|
| 108 |
-
5. Use relevant terms, phrases, or concepts from the content
|
| 109 |
-
6. Maintain consistency with the brand identity or main message
|
| 110 |
-
7. Adapt the emails to resonate with the provided content
|
| 111 |
-
|
| 112 |
-
IMPORTANT COMBINATIONS:
|
| 113 |
-
"""
|
| 114 |
-
# Updated conditions for specific input combinations
|
| 115 |
-
if product and not target_audience:
|
| 116 |
-
email_instruction += f"""- FILE + PRODUCT: You have a reference document and product ({product}). Create emails that highlight this specific product's benefits and features using insights from the document. Extract audience information from the document to better target the emails.
|
| 117 |
-
"""
|
| 118 |
-
elif target_audience and not product:
|
| 119 |
-
email_instruction += f"""- FILE + TARGET AUDIENCE: You have a reference document and target audience ({target_audience}). Create emails tailored to this specific audience using language and concepts from the document. Identify products or services from the document that would appeal to this audience.
|
| 120 |
-
"""
|
| 121 |
-
elif product and target_audience:
|
| 122 |
-
email_instruction += f"""- PRODUCT + TARGET AUDIENCE: You have both product ({product}) and target audience ({target_audience}). Create emails that connect this specific product with this specific audience, using insights from the document to strengthen the connection.
|
| 123 |
-
"""
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
"""
|
| 128 |
|
| 129 |
-
|
| 130 |
-
if selected_angle != "NINGUNO":
|
| 131 |
-
email_instruction += f"""
|
| 132 |
-
MAIN ANGLE: {selected_angle}
|
| 133 |
-
SPECIFIC ANGLE INSTRUCTIONS:
|
| 134 |
-
{angles[selected_angle]["instruction"]}
|
| 135 |
-
|
| 136 |
-
IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure:
|
| 137 |
-
1. Keep the base structure of the formula intact
|
| 138 |
-
2. Apply the tone and style of the {selected_angle} angle
|
| 139 |
-
3. Ensure each element of the formula reflects the angle
|
| 140 |
-
4. The angle affects "how" it's said, not "what" is said
|
| 141 |
-
|
| 142 |
-
SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
|
| 143 |
-
"""
|
| 144 |
-
for example in angles[selected_angle]["examples"]:
|
| 145 |
-
email_instruction += f"- {example}\n"
|
| 146 |
-
|
| 147 |
-
# Dentro de la funci贸n, actualizar el prompt para incluir emoci贸n y acci贸n deseada
|
| 148 |
-
email_instruction += (
|
| 149 |
-
f"\nYour task is to create {number_of_emails} persuasive emails for {target_audience} "
|
| 150 |
-
f"that evoke {emotion} and convince them to {desired_action} about {product}. "
|
| 151 |
-
)
|
| 152 |
-
|
| 153 |
-
if selected_angle != "NINGUNO":
|
| 154 |
-
email_instruction += f"IMPORTANT: Each email MUST follow the {selected_angle} angle clearly and consistently.\n\n"
|
| 155 |
-
|
| 156 |
-
email_instruction += (
|
| 157 |
-
f"Avoid obvious mentions of {product} and focus on generating genuine interest"
|
| 158 |
-
)
|
| 159 |
-
|
| 160 |
-
if selected_angle != "NINGUNO":
|
| 161 |
-
email_instruction += f" using the selected angle"
|
| 162 |
-
|
| 163 |
-
email_instruction += ".\n\n"
|
| 164 |
-
|
| 165 |
-
email_instruction += (
|
| 166 |
-
f"IMPORTANT: Carefully study these examples of the selected formula. "
|
| 167 |
-
f"Each example represents the style and structure to follow"
|
| 168 |
-
)
|
| 169 |
-
|
| 170 |
-
if selected_angle != "NINGUNO":
|
| 171 |
-
email_instruction += f", adapted to the {selected_angle} angle"
|
| 172 |
-
|
| 173 |
-
email_instruction += ":\n\n"
|
| 174 |
-
|
| 175 |
-
# Agregar 5 ejemplos aleatorios de la f贸rmula
|
| 176 |
-
random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
|
| 177 |
-
|
| 178 |
-
email_instruction += "FORMULA EXAMPLES TO FOLLOW:\n"
|
| 179 |
-
for i, example in enumerate(random_examples, 1):
|
| 180 |
-
email_instruction += f"{i}. {example}\n"
|
| 181 |
-
|
| 182 |
-
email_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
|
| 183 |
-
email_instruction += "1. Maintain the same structure and length as the previous examples\n"
|
| 184 |
-
email_instruction += "2. Use the same tone and writing style\n"
|
| 185 |
-
email_instruction += "3. Replicate the phrase construction patterns\n"
|
| 186 |
-
email_instruction += "4. Preserve the level of specificity and detail\n"
|
| 187 |
-
email_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n\n"
|
| 188 |
-
|
| 189 |
-
email_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
|
| 190 |
-
|
| 191 |
-
# CORRECTO (con indentaci贸n):
|
| 192 |
-
if selected_angle != "NINGUNO":
|
| 193 |
-
email_instruction += f"""
|
| 194 |
-
FINAL REMINDER:
|
| 195 |
-
1. Follow the structure of the selected formula
|
| 196 |
-
2. Apply the angle as a "style layer"
|
| 197 |
-
3. Maintain coherence between formula and angle
|
| 198 |
-
4. Ensure each email reflects both elements
|
| 199 |
-
|
| 200 |
-
GENERATE NOW:
|
| 201 |
-
Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
|
| 202 |
-
"""
|
| 203 |
-
else:
|
| 204 |
-
email_instruction += f"""
|
| 205 |
-
GENERATE NOW:
|
| 206 |
-
Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
|
| 207 |
-
"""
|
| 208 |
-
|
| 209 |
-
# Modificar la forma de enviar el mensaje seg煤n si hay imagen o no
|
| 210 |
-
message_parts = [email_instruction]
|
| 211 |
|
| 212 |
-
# Add
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
else:
|
| 217 |
-
instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown."
|
| 218 |
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
"parts": message_parts,
|
| 228 |
-
},
|
| 229 |
-
]
|
| 230 |
-
)
|
| 231 |
|
| 232 |
-
#
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
-
return response.text
|
|
|
|
|
|
|
|
|
|
| 236 |
|
| 237 |
-
#
|
| 238 |
-
st.
|
| 239 |
|
| 240 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
with open("manual.md", "r", encoding="utf-8") as file:
|
| 242 |
manual_content = file.read()
|
| 243 |
-
|
| 244 |
-
# Mostrar el contenido del manual en el sidebar
|
| 245 |
st.sidebar.markdown(manual_content)
|
| 246 |
|
| 247 |
-
#
|
| 248 |
-
|
| 249 |
-
css = f.read()
|
| 250 |
-
|
| 251 |
-
# Apply the CSS
|
| 252 |
-
st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
|
| 253 |
-
|
| 254 |
-
# Centrar el t铆tulo y el subt铆tulo
|
| 255 |
-
st.markdown("<h1 style='text-align: center;'>Generador de Emails</h1>", unsafe_allow_html=True)
|
| 256 |
-
st.markdown("<h4 style='text-align: center;'>Transforma tu marketing con emails persuasivos que convierten. Esta aplicaci贸n es tu arma secreta para crear emails emocionales de respuesta directa que impulsan a la acci贸n.</h4>", unsafe_allow_html=True)
|
| 257 |
|
| 258 |
-
#
|
| 259 |
-
col1, col2 = st.columns([1, 2])
|
| 260 |
-
|
| 261 |
-
# Columnas de entrada
|
| 262 |
with col1:
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
| 265 |
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
| 268 |
|
| 269 |
-
#
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
emotion = st.selectbox(
|
| 277 |
-
"驴Qu茅 emoci贸n quieres evocar?",
|
| 278 |
-
options=["Curiosidad", "Miedo", "Esperanza", "Entusiasmo", "Confianza", "Urgencia"]
|
| 279 |
-
)
|
| 280 |
-
|
| 281 |
-
desired_action = st.text_input("Acci贸n deseada", placeholder="Ejemplo: Registrarse para una prueba gratuita")
|
| 282 |
-
|
| 283 |
-
selected_formula_key = st.selectbox(
|
| 284 |
-
"Selecciona una f贸rmula para tus emails",
|
| 285 |
-
options=list(email_formulas.email_formulas.keys()) # Updated reference
|
| 286 |
-
)
|
| 287 |
-
|
| 288 |
-
# Automatically use the keys from the angles dictionary
|
| 289 |
-
# Make sure "NINGUNO" appears first, then the rest alphabetically
|
| 290 |
-
angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
|
| 291 |
-
selected_angle = st.selectbox(
|
| 292 |
-
"Selecciona un 谩ngulo para tus emails",
|
| 293 |
-
options=angle_keys
|
| 294 |
)
|
| 295 |
|
| 296 |
-
# A帽adir cargador de archivos
|
| 297 |
uploaded_file = st.file_uploader("馃搫 Archivo o imagen de referencia",
|
| 298 |
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
|
| 299 |
|
|
@@ -309,29 +166,24 @@ with col1:
|
|
| 309 |
if file_type == 'txt':
|
| 310 |
try:
|
| 311 |
file_content = uploaded_file.read().decode('utf-8')
|
| 312 |
-
st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
|
| 313 |
except Exception as e:
|
| 314 |
st.error(f"Error al leer el archivo TXT: {str(e)}")
|
| 315 |
file_content = ""
|
| 316 |
-
|
| 317 |
elif file_type == 'pdf':
|
| 318 |
try:
|
| 319 |
-
import PyPDF2
|
| 320 |
pdf_reader = PyPDF2.PdfReader(uploaded_file)
|
| 321 |
file_content = ""
|
| 322 |
for page in pdf_reader.pages:
|
| 323 |
file_content += page.extract_text() + "\n"
|
| 324 |
-
st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
|
| 325 |
except Exception as e:
|
| 326 |
st.error(f"Error al leer el archivo PDF: {str(e)}")
|
| 327 |
file_content = ""
|
| 328 |
|
| 329 |
elif file_type == 'docx':
|
| 330 |
try:
|
| 331 |
-
import docx
|
| 332 |
doc = docx.Document(uploaded_file)
|
| 333 |
file_content = "\n".join([para.text for para in doc.paragraphs])
|
| 334 |
-
st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
|
| 335 |
except Exception as e:
|
| 336 |
st.error(f"Error al leer el archivo DOCX: {str(e)}")
|
| 337 |
file_content = ""
|
|
@@ -339,7 +191,6 @@ with col1:
|
|
| 339 |
# Manejar archivos de imagen
|
| 340 |
elif file_type in ['jpg', 'jpeg', 'png']:
|
| 341 |
try:
|
| 342 |
-
from PIL import Image
|
| 343 |
image = Image.open(uploaded_file)
|
| 344 |
image_bytes = uploaded_file.getvalue()
|
| 345 |
image_parts = {
|
|
@@ -347,65 +198,54 @@ with col1:
|
|
| 347 |
"data": image_bytes
|
| 348 |
}
|
| 349 |
is_image = True
|
| 350 |
-
st.image(image, caption="Imagen cargada", use_column_width=True)
|
| 351 |
except Exception as e:
|
| 352 |
-
st.error(f"Error
|
| 353 |
is_image = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
|
|
|
|
|
|
| 366 |
|
| 367 |
-
#
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
if valid_inputs and selected_formula:
|
| 378 |
-
try:
|
| 379 |
-
# Use st.spinner instead of col2.spinner
|
| 380 |
-
with st.spinner("Creando los emails..."):
|
| 381 |
-
# Update the function call to include creative_idea
|
| 382 |
-
generated_emails = generate_emails(
|
| 383 |
-
target_audience,
|
| 384 |
-
product,
|
| 385 |
-
temperature,
|
| 386 |
-
selected_formula,
|
| 387 |
-
selected_angle,
|
| 388 |
-
file_content if 'file_content' in locals() else "",
|
| 389 |
-
image_parts if 'image_parts' in locals() else None,
|
| 390 |
-
is_image if 'is_image' in locals() else False,
|
| 391 |
-
emotion,
|
| 392 |
-
desired_action,
|
| 393 |
-
creative_idea # Add the creative idea parameter
|
| 394 |
-
)
|
| 395 |
|
| 396 |
-
#
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
if not selected_formula:
|
| 407 |
-
col2.error("Por favor selecciona una f贸rmula.")
|
| 408 |
-
elif not (has_emotion and has_action):
|
| 409 |
-
col2.error("Por favor especifica la emoci贸n que quieres evocar y la acci贸n deseada.")
|
| 410 |
-
else:
|
| 411 |
-
col2.error("Por favor proporciona al menos una de estas combinaciones: archivo + producto, archivo + p煤blico objetivo, o producto + p煤blico objetivo + emoci贸n + acci贸n deseada.")
|
|
|
|
| 2 |
import streamlit as st
|
| 3 |
import os
|
| 4 |
import google.generativeai as genai
|
| 5 |
+
from puv_formulas import puv_formulas
|
| 6 |
+
from styles import apply_styles
|
| 7 |
+
import PyPDF2
|
| 8 |
+
import docx
|
| 9 |
+
from PIL import Image
|
| 10 |
+
import datetime # Add this import for timestamp
|
| 11 |
+
|
| 12 |
+
# Cargar variables de entorno
|
| 13 |
load_dotenv()
|
| 14 |
|
| 15 |
+
# Configurar API de Google Gemini
|
| 16 |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
| 17 |
|
| 18 |
+
# Funci贸n para obtener la respuesta del modelo Gemini
|
| 19 |
+
def get_gemini_response(product_service, target_audience, skills, formula_type, temperature, file_content="", image_parts=None):
|
| 20 |
+
# Check if we have at least one source of information
|
| 21 |
+
has_file_content = bool(file_content.strip())
|
| 22 |
+
has_image = image_parts is not None
|
| 23 |
+
has_text_input = target_audience or product_service or skills
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
if not (has_file_content or has_image or has_text_input):
|
| 26 |
+
return "Debes proporcionar al menos un tipo de informaci贸n: p煤blico objetivo, producto/servicio, habilidades o un archivo de referencia."
|
| 27 |
|
| 28 |
+
# If we only have file content but no other inputs, we can proceed
|
| 29 |
+
if (has_file_content or has_image) and not has_text_input:
|
| 30 |
+
# File-only mode
|
| 31 |
+
business_info = "Analyze the provided reference material to extract business information.\n"
|
| 32 |
+
else:
|
| 33 |
+
# Regular mode with validation
|
| 34 |
+
if not target_audience:
|
| 35 |
+
return "El campo de p煤blico objetivo es obligatorio cuando no se proporciona un archivo de referencia completo."
|
| 36 |
+
|
| 37 |
+
if not product_service and not skills:
|
| 38 |
+
return "Debes proporcionar al menos tu producto/servicio o tus habilidades cuando no se proporciona un archivo de referencia completo."
|
| 39 |
+
|
| 40 |
+
# Adjust prompt based on what's provided
|
| 41 |
+
business_info = f"Target Audience: {target_audience}\n"
|
| 42 |
+
|
| 43 |
+
if product_service:
|
| 44 |
+
business_info += f"Product/Service: {product_service}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
if skills:
|
| 47 |
+
business_info += f"My Skills/Expertise: {skills}\n"
|
|
|
|
| 48 |
|
| 49 |
+
formula = puv_formulas[formula_type]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
# Add file content if available
|
| 52 |
+
reference_info = ""
|
| 53 |
+
if file_content:
|
| 54 |
+
reference_info = f"\nREFERENCE MATERIAL:\n{file_content}\n"
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
model = genai.GenerativeModel('gemini-2.0-flash')
|
| 57 |
+
full_prompt = f"""
|
| 58 |
+
You are a UVP (Unique Value Proposition) expert. Analyze (internally only, do not output the analysis) the following information:
|
| 59 |
+
BUSINESS INFORMATION:
|
| 60 |
+
{business_info}
|
| 61 |
+
Formula Type: {formula_type}
|
| 62 |
+
{formula["description"]}
|
| 63 |
+
{reference_info}
|
| 64 |
+
|
| 65 |
+
EXAMPLE TO FOLLOW:
|
| 66 |
+
{formula["examples"]}
|
| 67 |
+
|
| 68 |
+
First, analyze (but don't output) these points:
|
| 69 |
+
1. TARGET AUDIENCE ANALYSIS - Pain Points:
|
| 70 |
+
- What specific frustrations does this audience experience?
|
| 71 |
+
- What are their biggest daily challenges?
|
| 72 |
+
- What emotional problems do they face?
|
| 73 |
+
- What have they tried before that didn't work?
|
| 74 |
+
- What's stopping them from achieving their goals?
|
| 75 |
+
|
| 76 |
+
2. PRODUCT/SERVICE ANALYSIS - Benefits:
|
| 77 |
+
- What tangible results do clients get?
|
| 78 |
+
- What specific transformation does it offer?
|
| 79 |
+
- What's the unique method or differentiator?
|
| 80 |
+
- What competitive advantages does it have?
|
| 81 |
+
- What emotional benefits does it provide?
|
| 82 |
+
|
| 83 |
+
3. SKILLS/EXPERTISE ANALYSIS - Credibility:
|
| 84 |
+
- How do these skills directly address the audience's pain points?
|
| 85 |
+
- What unique perspective do these skills bring to the solution?
|
| 86 |
+
- How do these skills enhance the product/service delivery?
|
| 87 |
+
- What credibility elements can be highlighted?
|
| 88 |
+
- How do these skills differentiate from competitors?
|
| 89 |
+
|
| 90 |
+
Based on your internal analysis of the target audience pain points and product benefits (do not include this analysis in the output), create THREE different UVPs in Spanish language following the formula structure provided.
|
| 91 |
+
CRITICAL INSTRUCTIONS:
|
| 92 |
+
- Each UVP must be specific and measurable
|
| 93 |
+
- Focus on the transformation journey
|
| 94 |
+
- Use natural, conversational language
|
| 95 |
+
- Avoid generic phrases and buzzwords
|
| 96 |
+
- Maximum 2 lines per UVP
|
| 97 |
+
- DO NOT include any analysis in the output
|
| 98 |
+
- ONLY output the three UVPs
|
| 99 |
|
| 100 |
+
Output EXACTLY in this format (no additional text) in Spanish language:
|
| 101 |
+
1. [First UVP]
|
| 102 |
+
2. [Second UVP]
|
| 103 |
+
3. [Third UVP]
|
| 104 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
+
# Handle text-only or text+image requests
|
| 107 |
+
if image_parts:
|
| 108 |
+
response = model.generate_content([full_prompt, image_parts], generation_config={"temperature": temperature})
|
| 109 |
+
else:
|
| 110 |
+
response = model.generate_content([full_prompt], generation_config={"temperature": temperature})
|
| 111 |
|
| 112 |
+
return response.parts[0].text if response and response.parts else "Error generating content."
|
| 113 |
+
|
| 114 |
+
# Configurar la aplicaci贸n Streamlit
|
| 115 |
+
st.set_page_config(page_title="UVP Generator", page_icon="馃挕", layout="wide")
|
| 116 |
|
| 117 |
+
# Aplicar estilos
|
| 118 |
+
st.markdown(apply_styles(), unsafe_allow_html=True)
|
| 119 |
|
| 120 |
+
# T铆tulo de la app
|
| 121 |
+
st.markdown("<h1>Generador de PUV</h1>", unsafe_allow_html=True)
|
| 122 |
+
st.markdown("<h3>Crea Propuestas 脷nicas de Valor poderosas que atraigan a tus clientes ideales y comuniquen tu valor de manera efectiva.</h3>", unsafe_allow_html=True)
|
| 123 |
+
|
| 124 |
+
# Sidebar manual
|
| 125 |
with open("manual.md", "r", encoding="utf-8") as file:
|
| 126 |
manual_content = file.read()
|
|
|
|
|
|
|
| 127 |
st.sidebar.markdown(manual_content)
|
| 128 |
|
| 129 |
+
# Crear dos columnas
|
| 130 |
+
col1, col2 = st.columns([1, 1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
+
# Columna izquierda para inputs
|
|
|
|
|
|
|
|
|
|
| 133 |
with col1:
|
| 134 |
+
product_service = st.text_area(
|
| 135 |
+
"驴Cu谩l es tu producto o servicio?",
|
| 136 |
+
placeholder="Ejemplo: Curso de copywriting con IA, Programa de coaching..."
|
| 137 |
+
)
|
| 138 |
|
| 139 |
+
skills = st.text_area(
|
| 140 |
+
"Mis habilidades:",
|
| 141 |
+
placeholder="Ejemplo: Experiencia en marketing digital, certificaci贸n en SEO..."
|
| 142 |
+
)
|
| 143 |
|
| 144 |
+
# Move the generate button here, right after skills
|
| 145 |
+
generate_button = st.button("Generar PUV")
|
| 146 |
+
|
| 147 |
+
with st.expander("Opciones avanzadas"):
|
| 148 |
+
target_audience = st.text_area(
|
| 149 |
+
"驴Cu谩l es tu p煤blico objetivo?",
|
| 150 |
+
placeholder="Ejemplo: Coaches que quieren atraer m谩s clientes..."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
)
|
| 152 |
|
| 153 |
+
# A帽adir cargador de archivos
|
| 154 |
uploaded_file = st.file_uploader("馃搫 Archivo o imagen de referencia",
|
| 155 |
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
|
| 156 |
|
|
|
|
| 166 |
if file_type == 'txt':
|
| 167 |
try:
|
| 168 |
file_content = uploaded_file.read().decode('utf-8')
|
|
|
|
| 169 |
except Exception as e:
|
| 170 |
st.error(f"Error al leer el archivo TXT: {str(e)}")
|
| 171 |
file_content = ""
|
| 172 |
+
|
| 173 |
elif file_type == 'pdf':
|
| 174 |
try:
|
|
|
|
| 175 |
pdf_reader = PyPDF2.PdfReader(uploaded_file)
|
| 176 |
file_content = ""
|
| 177 |
for page in pdf_reader.pages:
|
| 178 |
file_content += page.extract_text() + "\n"
|
|
|
|
| 179 |
except Exception as e:
|
| 180 |
st.error(f"Error al leer el archivo PDF: {str(e)}")
|
| 181 |
file_content = ""
|
| 182 |
|
| 183 |
elif file_type == 'docx':
|
| 184 |
try:
|
|
|
|
| 185 |
doc = docx.Document(uploaded_file)
|
| 186 |
file_content = "\n".join([para.text for para in doc.paragraphs])
|
|
|
|
| 187 |
except Exception as e:
|
| 188 |
st.error(f"Error al leer el archivo DOCX: {str(e)}")
|
| 189 |
file_content = ""
|
|
|
|
| 191 |
# Manejar archivos de imagen
|
| 192 |
elif file_type in ['jpg', 'jpeg', 'png']:
|
| 193 |
try:
|
|
|
|
| 194 |
image = Image.open(uploaded_file)
|
| 195 |
image_bytes = uploaded_file.getvalue()
|
| 196 |
image_parts = {
|
|
|
|
| 198 |
"data": image_bytes
|
| 199 |
}
|
| 200 |
is_image = True
|
|
|
|
| 201 |
except Exception as e:
|
| 202 |
+
st.error(f"Error al procesar la imagen: {str(e)}")
|
| 203 |
is_image = False
|
| 204 |
+
|
| 205 |
+
formula_type = st.selectbox(
|
| 206 |
+
"F贸rmula PUV:",
|
| 207 |
+
options=list(puv_formulas.keys())
|
| 208 |
+
)
|
| 209 |
+
temperature = st.slider(
|
| 210 |
+
"Nivel de creatividad:",
|
| 211 |
+
min_value=0.0,
|
| 212 |
+
max_value=2.0,
|
| 213 |
+
value=1.0,
|
| 214 |
+
step=0.1,
|
| 215 |
+
help="Valores m谩s altos generan propuestas m谩s creativas pero menos predecibles."
|
| 216 |
+
)
|
| 217 |
|
| 218 |
+
with col2:
|
| 219 |
+
if generate_button:
|
| 220 |
+
# Store the response in session state so it persists across reruns
|
| 221 |
+
with st.spinner("Creando tu PUV..."):
|
| 222 |
+
st.session_state.puv_response = get_gemini_response(
|
| 223 |
+
product_service,
|
| 224 |
+
target_audience,
|
| 225 |
+
skills,
|
| 226 |
+
formula_type,
|
| 227 |
+
temperature,
|
| 228 |
+
file_content,
|
| 229 |
+
image_parts
|
| 230 |
+
)
|
| 231 |
|
| 232 |
+
# Display the response if it exists in session state (separate from the generate button condition)
|
| 233 |
+
if 'puv_response' in st.session_state:
|
| 234 |
+
st.write("### Propuestas 脷nicas de Valor")
|
| 235 |
+
st.write(st.session_state.puv_response)
|
| 236 |
+
|
| 237 |
+
# Add download button if we have a valid response
|
| 238 |
+
if st.session_state.puv_response and not st.session_state.puv_response.startswith("Error") and not st.session_state.puv_response.startswith("Debes"):
|
| 239 |
+
# Get current timestamp for the filename
|
| 240 |
+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
+
# Prepare content for download
|
| 243 |
+
download_content = st.session_state.puv_response
|
| 244 |
+
|
| 245 |
+
# Download button
|
| 246 |
+
st.download_button(
|
| 247 |
+
label="DESCARGAR PUV",
|
| 248 |
+
data=download_content,
|
| 249 |
+
file_name=f"propuestas_unicas_valor_{timestamp}.txt",
|
| 250 |
+
mime="text/plain"
|
| 251 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|