ivanoctaviogaitansantos commited on
Commit
89200b0
·
verified ·
1 Parent(s): 656e26f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -62
app.py CHANGED
@@ -8,6 +8,10 @@ import io
8
  from PIL import Image
9
  from typing import List, Optional
10
 
 
 
 
 
11
  class HyperrealisticPromptGenerator:
12
  def __init__(self):
13
  self.ROLES = [
@@ -45,11 +49,11 @@ class HyperrealisticPromptGenerator:
45
  "messy chic bun"
46
  ]
47
  self.POSES = [
48
- "standing with one leg slightly forward, skirt shifting gently to subtly reveal lace thong, view from knees to head",
49
- "seated on a chair edge, legs crossed, skirt moving slightly, natural sensual expression, low angle from knees",
50
- "leaning against a desk with hips cocked, skirt riding up, captured from knees to head",
51
- "walking with natural sway, skirt flowing, viewed contrapicado from knees",
52
- "adjusting stockings or shoes, skirt slightly lifted revealing thong, viewed low angle knees up"
53
  ]
54
  self.SETTINGS = [
55
  "modern office with elegant decor and warm ambient light",
@@ -67,19 +71,14 @@ class HyperrealisticPromptGenerator:
67
  ]
68
  self.TECHNICAL_DETAILS = (
69
  "Captured in ultra HD 16K (15360×8640) vertical 9:16 full body format. "
70
- "Canon EOS R5 Cine RAW camera and Canon RF 85mm f/1.2L USM lens at f/1.2 aperture for creamy bokeh and realistic depth of field. "
71
- "ARRI SkyPanel S360-C with soft shadowless 3:1 lighting ratio. "
72
- "Advanced Path Tracing, Physically Based Rendering (PBR), Subsurface Scattering (SSS) for lifelike skin translucency, "
73
- "Ray Tracing for global illumination and reflections. "
74
- "Photogrammetry-based texture mapping, displacement maps for skin pores, delicate fabric weave and lace micro-details. "
75
- "Natural, physics-driven hair strand flow. "
76
- "Composition uses contrapicado low-angle (knee to head) shots emphasizing natural, sensual lingerie reveal."
77
  )
78
  self.CONDITION_FIXED = (
79
- "Wearing elegant thigh-high stockings, no bra, and high stilettos. "
80
- "Delicately revealing a lace thong in a natural, seductive manner, as if caught candidly. "
81
- "Age between 20 and 25, radiating youthfulness and fresh allure. "
82
- "Pose and framing strictly low-angle, knees to head vertical 9:16 aspect ratio, full body filling the frame."
83
  )
84
 
85
  def _choose_random(self, options: List[str]) -> str:
@@ -94,29 +93,26 @@ class HyperrealisticPromptGenerator:
94
  pose = self._choose_random(self.POSES)
95
  setting = self._choose_random(self.SETTINGS)
96
  atmosphere = self._choose_random(self.ATMOSPHERES)
97
- return (
98
- ```
99
- f"Role: {selected_role}\n"
100
- f"Age: {age}\n"
101
- f"Hair: {hair_style} in {hair_color}\n"
102
- f"Eyes: {eye_color}\n"
103
- f"Pose: {pose}\n"
104
- f"Environment: {setting}\n"
105
- f"Atmosphere: {atmosphere}\n"
106
- f"Outfit: {self.CONDITION_FIXED}\n"
107
- f"Technical specs: {self.TECHNICAL_DETAILS}\n"
108
- "```"
109
- )
110
 
111
  def generate_prompt_automatic(self):
112
  return self.generate_single_prompt()
113
 
 
 
 
 
 
114
  gen = HyperrealisticPromptGenerator()
115
 
116
  API_KEY = os.getenv("SAMBANOVA_API_KEY")
117
  API_URL = "https://api.sambanova.ai/v1/chat/completions"
118
  headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
119
 
 
120
  def process_image(image):
121
  if image is None:
122
  return None
@@ -124,13 +120,15 @@ def process_image(image):
124
  image.save(buffered, format="PNG")
125
  return base64.b64encode(buffered.getvalue()).decode("utf-8")
126
 
 
127
  def analizar_imagen_y_generar_prompt(image_base64):
128
  if not API_KEY:
129
  return "``````"
130
  messages = [
131
  {"role": "system", "content": "Describe images in detailed English."},
132
  {
133
- "role": "user", "content": [
 
134
  {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}},
135
  {"type": "text", "text": "Provide a detailed English description for a prompt."}
136
  ]
@@ -142,9 +140,14 @@ def analizar_imagen_y_generar_prompt(image_base64):
142
  response.raise_for_status()
143
  text_resp = response.json()["choices"][0]["message"]["content"]
144
  return f"``````"
145
- except Exception as e:
146
  return f"``````"
147
 
 
 
 
 
 
148
  def chat_sambanova(user_message, image_input, auto_mode, chat_history, loading_state):
149
  updated_history = chat_history[:] if chat_history else []
150
  image_base64 = process_image(image_input) if image_input else None
@@ -197,49 +200,49 @@ def chat_sambanova(user_message, image_input, auto_mode, chat_history, loading_s
197
  except json.JSONDecodeError:
198
  continue
199
  yield "", updated_history, "", ""
200
- except requests.exceptions.HTTPError as http_err:
201
- error_msg = f"Error HTTP {http_err.response.status_code}: {http_err.response.text}"
202
- if updated_history:
203
- updated_history[-1] = (user_message, error_msg)
204
- yield "", updated_history, error_msg, ""
205
- except requests.exceptions.ConnectionError:
206
- error_msg = "Error: No se pudo conectar con la API de SambaNova. Verifica tu conexión."
207
- if updated_history:
208
- updated_history[-1] = (user_message, error_msg)
209
- yield "", updated_history, error_msg, ""
210
- except requests.exceptions.Timeout:
211
- error_msg = "Error: La solicitud a la API timed out."
212
- if updated_history:
213
- updated_history[-1] = (user_message, error_msg)
214
- yield "", updated_history, error_msg, ""
215
  except Exception as e:
216
  error_msg = f"Error inesperado: {str(e)}"
217
- if updated_history:
218
- updated_history[-1] = (user_message, error_msg)
219
  yield "", updated_history, error_msg, ""
220
 
 
221
  def generar_prompt_interno():
222
  return gen.generate_prompt_automatic(), ""
223
 
224
- with gr.Blocks() as demo:
225
- gr.Markdown("# Hyperrealistic Prompt Generator & Chatbot")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
  chat_history = gr.State([])
228
- error_display = gr.Textbox(label="Mensajes de error", interactive=False, visible=True)
229
  loading_state = gr.State("")
230
 
231
- chatbot = gr.Chatbot(label="Chatbot IA (SambaNova - Llama-4 Maverick)", type='messages')
232
- prompt_output = gr.Markdown(label="Prompt Generado", elem_classes=["prompt-output"])
233
 
234
  with gr.Row():
235
- msg = gr.Textbox(label="Escribe tu mensaje", scale=4)
236
- img_input = gr.Image(label="Subir imagen (opcional)", type="pil", scale=2)
237
 
238
  with gr.Row():
239
- auto_mode = gr.Checkbox(label="Modo automático (generar prompt desde imagen)", value=False)
240
  btn_send = gr.Button("Enviar mensaje", variant="primary")
241
- btn_gen_prompt = gr.Button("Generar prompt automático", variant="secondary")
242
- copy_button = gr.Button("Copiar Prompt")
243
 
244
  with gr.Row():
245
  loading = gr.Markdown(value=lambda x: f"**{x}**" if x else "", label="Estado")
@@ -259,10 +262,21 @@ with gr.Blocks() as demo:
259
  inputs=[],
260
  outputs=[msg, prompt_output]
261
  )
 
 
262
  copy_button.click(
263
- fn=None,
264
- _js="() => { navigator.clipboard.writeText(document.querySelector('.prompt-output').innerText); }",
265
- outputs=None
 
 
 
 
 
 
 
 
 
266
  )
267
 
268
  if __name__ == "__main__":
@@ -270,5 +284,4 @@ if __name__ == "__main__":
270
  demo.launch()
271
  except Exception as e:
272
  print(f"Error al iniciar Gradio: {str(e)}")
273
-
274
 
 
8
  from PIL import Image
9
  from typing import List, Optional
10
 
11
+ # ==============================================================
12
+ # CLASE PRINCIPAL: GENERADOR DE PROMPTS HIPERREALISTAS
13
+ # ==============================================================
14
+
15
  class HyperrealisticPromptGenerator:
16
  def __init__(self):
17
  self.ROLES = [
 
49
  "messy chic bun"
50
  ]
51
  self.POSES = [
52
+ "standing with one leg slightly forward, natural elegance",
53
+ "seated on a chair edge, legs crossed, professional expression",
54
+ "leaning against a desk, confident look",
55
+ "walking with subtle grace, light movement",
56
+ "adjusting hair gently, natural body language"
57
  ]
58
  self.SETTINGS = [
59
  "modern office with elegant decor and warm ambient light",
 
71
  ]
72
  self.TECHNICAL_DETAILS = (
73
  "Captured in ultra HD 16K (15360×8640) vertical 9:16 full body format. "
74
+ "Canon EOS R5 Cine RAW camera and Canon RF 85mm f/1.2L USM lens at f/1.2 aperture for creamy bokeh. "
75
+ "ARRI SkyPanel S360-C soft lighting, Path Tracing, PBR, SSS for lifelike skin, and Ray Tracing. "
76
+ "Photogrammetry-based textures, displacement maps for skin pores, delicate fabric weave. "
77
+ "Natural hair strand flow, low-angle (knee to head) composition."
 
 
 
78
  )
79
  self.CONDITION_FIXED = (
80
+ "Wearing elegant professional attire matching the role, natural posture, confident expression. "
81
+ "Full body portrait, cinematic tone, vertical 9:16 framing."
 
 
82
  )
83
 
84
  def _choose_random(self, options: List[str]) -> str:
 
93
  pose = self._choose_random(self.POSES)
94
  setting = self._choose_random(self.SETTINGS)
95
  atmosphere = self._choose_random(self.ATMOSPHERES)
96
+
97
+ prompt = f"""
98
+ """
99
+ return prompt
 
 
 
 
 
 
 
 
 
100
 
101
  def generate_prompt_automatic(self):
102
  return self.generate_single_prompt()
103
 
104
+
105
+ # ==============================================================
106
+ # CONFIGURACIÓN SAMBANOVA API
107
+ # ==============================================================
108
+
109
  gen = HyperrealisticPromptGenerator()
110
 
111
  API_KEY = os.getenv("SAMBANOVA_API_KEY")
112
  API_URL = "https://api.sambanova.ai/v1/chat/completions"
113
  headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
114
 
115
+
116
  def process_image(image):
117
  if image is None:
118
  return None
 
120
  image.save(buffered, format="PNG")
121
  return base64.b64encode(buffered.getvalue()).decode("utf-8")
122
 
123
+
124
  def analizar_imagen_y_generar_prompt(image_base64):
125
  if not API_KEY:
126
  return "``````"
127
  messages = [
128
  {"role": "system", "content": "Describe images in detailed English."},
129
  {
130
+ "role": "user",
131
+ "content": [
132
  {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}},
133
  {"type": "text", "text": "Provide a detailed English description for a prompt."}
134
  ]
 
140
  response.raise_for_status()
141
  text_resp = response.json()["choices"][0]["message"]["content"]
142
  return f"``````"
143
+ except Exception:
144
  return f"``````"
145
 
146
+
147
+ # ==============================================================
148
+ # FUNCIÓN PRINCIPAL DE CHAT Y PROMPT
149
+ # ==============================================================
150
+
151
  def chat_sambanova(user_message, image_input, auto_mode, chat_history, loading_state):
152
  updated_history = chat_history[:] if chat_history else []
153
  image_base64 = process_image(image_input) if image_input else None
 
200
  except json.JSONDecodeError:
201
  continue
202
  yield "", updated_history, "", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  except Exception as e:
204
  error_msg = f"Error inesperado: {str(e)}"
205
+ updated_history[-1] = (user_message, error_msg)
 
206
  yield "", updated_history, error_msg, ""
207
 
208
+
209
  def generar_prompt_interno():
210
  return gen.generate_prompt_automatic(), ""
211
 
212
+
213
+ # ==============================================================
214
+ # INTERFAZ GRADIO CON MODO OSCURO Y ESTILO BATUTO
215
+ # ==============================================================
216
+
217
+ css_batuto = """
218
+ body {background-color: #05070A; color: #B0C8FF; font-family: 'Poppins', sans-serif;}
219
+ h1, h2, h3, h4 {color: #5CA8FF; text-align: center;}
220
+ .gradio-container {background-color: #05070A !important;}
221
+ button {background-color: #0B1A33 !important; color: #B0C8FF !important; border-radius: 12px;}
222
+ button:hover {background-color: #1B335F !important;}
223
+ .prompt-output {background-color: #0A0F1A; color: #A8CFFF; border-radius: 10px; padding: 10px;}
224
+ input, textarea {background-color: #0B101A !important; color: #DDE8FF !important;}
225
+ """
226
+
227
+ with gr.Blocks(css=css_batuto, theme="gradio/soft") as demo:
228
+ gr.Markdown("# ⚡ BATUTO / Prompt Studio — Hyperrealistic Generator")
229
 
230
  chat_history = gr.State([])
231
+ error_display = gr.Textbox(label="System messages", interactive=False, visible=True)
232
  loading_state = gr.State("")
233
 
234
+ chatbot = gr.Chatbot(label="💬 BATUTO Assistant (SambaNova - Llama-4 Maverick)", type='messages')
235
+ prompt_output = gr.Markdown(label="🎨 Prompt generado", elem_classes=["prompt-output"])
236
 
237
  with gr.Row():
238
+ msg = gr.Textbox(label="Tu mensaje", scale=4)
239
+ img_input = gr.Image(label="Sube una imagen (opcional)", type="pil", scale=2)
240
 
241
  with gr.Row():
242
+ auto_mode = gr.Checkbox(label="Modo automático (Generar prompt desde imagen)", value=False)
243
  btn_send = gr.Button("Enviar mensaje", variant="primary")
244
+ btn_gen_prompt = gr.Button("🎲 Generar prompt automático", variant="secondary")
245
+ copy_button = gr.Button("📋 Copiar Prompt")
246
 
247
  with gr.Row():
248
  loading = gr.Markdown(value=lambda x: f"**{x}**" if x else "", label="Estado")
 
262
  inputs=[],
263
  outputs=[msg, prompt_output]
264
  )
265
+
266
+ # ✅ Botón copiar funcional con alerta
267
  copy_button.click(
268
+ None,
269
+ [],
270
+ [],
271
+ _js="""() => {
272
+ const el = document.querySelector('.prompt-output');
273
+ if (el) {
274
+ navigator.clipboard.writeText(el.innerText);
275
+ alert('✅ Prompt copiado al portapapeles');
276
+ } else {
277
+ alert('❌ No se encontró el prompt para copiar');
278
+ }
279
+ }"""
280
  )
281
 
282
  if __name__ == "__main__":
 
284
  demo.launch()
285
  except Exception as e:
286
  print(f"Error al iniciar Gradio: {str(e)}")
 
287