Malaji71 commited on
Commit
b0c6c7b
·
verified ·
1 Parent(s): e24ddbd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -30
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py — PromptCraft: Refinamiento Estructural de Prompts
2
  import gradio as gr
3
  import os
4
  import time
@@ -18,6 +18,7 @@ class LlamaRefiner:
18
  hf_token = os.getenv("PS")
19
  if not hf_token:
20
  raise ValueError("Secret 'PS' (HF_TOKEN) no encontrado.")
 
21
  self.hf_client = InferenceClient(api_key=hf_token)
22
  self.agent = ImprovedSemanticAgent()
23
  if not self.agent.is_ready:
@@ -25,11 +26,12 @@ class LlamaRefiner:
25
  logger.info(f"Inicialización del agente: {init_msg}")
26
  if not self.agent.is_ready:
27
  logger.error("❌ Agente NO está listo.")
 
28
  logger.info("🚀 Cargando traductor local (es → en)...")
29
  self.translator = pipeline(
30
  "translation_es_to_en",
31
  model="Helsinki-NLP/opus-mt-es-en",
32
- device=-1
33
  )
34
  logger.info("✅ Traductor local listo.")
35
 
@@ -38,31 +40,10 @@ class LlamaRefiner:
38
  return text
39
  try:
40
  result = self.translator(text, max_length=250, clean_up_tokenization_spaces=True)
41
- raw_translation = result[0]['translation_text'].strip()
42
  except Exception as e:
43
  logger.warning(f"Traducción local fallida: {e}. Usando texto original.")
44
- raw_translation = text
45
-
46
- user_text_lower = text.lower()
47
- output = raw_translation
48
-
49
- if any(kw in user_text_lower for kw in ["llamas", "ardiendo", "quem", "incendi", "fuego"]):
50
- output = output.replace("fiery", "on fire")
51
- if not any(term in output.lower() for term in ["on fire", "burning", "in flames", "ablaze", "aflame"]):
52
- output = output + " on fire"
53
-
54
- if any(kw in user_text_lower for kw in ["oro", "dorado"]):
55
- if "golden" not in output.lower() and "gold" not in output.lower():
56
- if any(w in output.lower() for w in ["statue", "sculpture", "figure"]):
57
- output = output + " made of gold"
58
- else:
59
- output = output + " golden"
60
-
61
- if any(kw in user_text_lower for kw in ["congelado", "hielo", "helado", "ice"]):
62
- if not any(term in output.lower() for term in ["frozen", "ice", "icy"]):
63
- output = output + " frozen"
64
-
65
- return output.strip()
66
 
67
  def retrieve_similar_examples(self, user_prompt_en: str, category: str = "auto", k: int = 6) -> list:
68
  if not self.agent.is_ready:
@@ -71,6 +52,7 @@ class LlamaRefiner:
71
  query_embedding = self.agent.embedding_model.encode([user_prompt_en], convert_to_numpy=True, normalize_embeddings=True)[0]
72
  query_embedding = query_embedding.astype('float32').reshape(1, -1)
73
  distances, indices = self.agent.index.search(query_embedding, 50)
 
74
  candidates = []
75
  for idx in indices[0]:
76
  if 0 <= idx < len(self.agent.indexed_examples):
@@ -80,19 +62,25 @@ class LlamaRefiner:
80
  if isinstance(caption, str) and len(caption) > 10:
81
  if category == "auto" or ex_category == category:
82
  candidates.append((idx, caption, ex_category))
 
83
  if not candidates:
84
  return []
 
85
  if len(candidates) <= k:
86
  return [cap for _, cap, _ in candidates]
 
87
  candidate_texts = [cap for _, cap, _ in candidates]
88
  pairs = [[user_prompt_en, cand] for cand in candidate_texts]
89
  scores = self.agent.reranker.predict(pairs)
 
90
  scored = [(candidates[i][1], scores[i]) for i in range(len(candidates))]
91
  scored.sort(key=lambda x: x[1], reverse=True)
 
92
  top_examples = [ex for ex, _ in scored[:k]]
93
  return top_examples
 
94
  except Exception as e:
95
- logger.error(f"Error en recuperación: {e}")
96
  try:
97
  return [
98
  self.agent.indexed_examples[idx]['caption']
@@ -102,13 +90,103 @@ class LlamaRefiner:
102
  except:
103
  return []
104
 
 
 
 
 
 
 
105
  def refine_with_llm(self, user_prompt: str, category: str = "auto") -> Tuple[str, str, list]:
106
  user_prompt_en = self.translate_to_english(user_prompt)
107
  examples = self.retrieve_similar_examples(user_prompt_en, category=category, k=6)
108
 
109
- # Usar SOLO enriquecimiento local (como en la versión que funcionaba)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  enhanced_prompt, _ = self.agent.enhance_prompt(user_prompt_en, category=category)
111
- return enhanced_prompt.strip(), " Enriquecimiento semántico local", examples
112
 
113
  class SDXLGenerator:
114
  def __init__(self):
@@ -145,6 +223,7 @@ def create_interface():
145
  return "", "", "Prompt vacío."
146
  if refiner is None:
147
  return "", "", "Servicios no disponibles."
 
148
  progress(0.2, desc="🌍 Traduciendo...")
149
  category_map = {
150
  "Automática": "auto",
@@ -155,6 +234,7 @@ def create_interface():
155
  "Texto": "text"
156
  }
157
  category_en = category_map.get(category_es, "auto")
 
158
  refined, info, examples = refiner.refine_with_llm(prompt, category_en)
159
  examples_text = "\n".join(f"{i+1}. {ex}" for i, ex in enumerate(examples)) if examples else "Ninguno"
160
  status = f"Prompt refinado: {refined}\n{info}"
@@ -165,6 +245,7 @@ def create_interface():
165
  return None, "❌ No hay prompt refinado. Primero haz clic en 'Refinar prompt'."
166
  if generator is None:
167
  return None, "❌ Generador no inicializado."
 
168
  aspect_ratios = {
169
  "1:1": (1024, 1024),
170
  "16:9": (1344, 768),
@@ -175,12 +256,12 @@ def create_interface():
175
  "9:21": (640, 1536),
176
  }
177
  width, height = aspect_ratios.get(aspect_ratio, (1024, 1024))
178
- progress(0.5, desc="🎨 Generando imagen...")
179
  try:
180
  image_path, gen_msg = generator.generate_image(refined_prompt, width, height)
181
  return image_path, gen_msg
182
  except Exception as e:
183
- error_msg = f"❌ Error: {str(e)}"
184
  logger.error(error_msg)
185
  return None, error_msg
186
 
@@ -209,8 +290,10 @@ def create_interface():
209
  prompt_input = gr.Textbox(label="Tu idea (en castellano)", lines=3, placeholder="Ej: un mercado en Antigua Guatemala...")
210
  category_es = gr.Dropdown(label="Categoría", choices=CATEGORY_CHOICES_ES, value="Automática")
211
  aspect = gr.Dropdown(label="Proporción", choices=["1:1", "16:9", "9:16", "4:3", "3:4", "21:9", "9:21"], value="1:1")
 
212
  refine_btn = gr.Button("🔄 Refinar prompt", variant="secondary")
213
  generate_btn = gr.Button("🎨 Generar imagen", variant="primary")
 
214
  with gr.Column():
215
  refined_output = gr.Textbox(
216
  label="Prompt refinado (inglés)",
 
1
+ # app.py — PromptCraft: Refinamiento Estructural de Prompts (con fallback a Together vía HF Router)
2
  import gradio as gr
3
  import os
4
  import time
 
18
  hf_token = os.getenv("PS")
19
  if not hf_token:
20
  raise ValueError("Secret 'PS' (HF_TOKEN) no encontrado.")
21
+ # Cliente para fallback original (Hyperbolic)
22
  self.hf_client = InferenceClient(api_key=hf_token)
23
  self.agent = ImprovedSemanticAgent()
24
  if not self.agent.is_ready:
 
26
  logger.info(f"Inicialización del agente: {init_msg}")
27
  if not self.agent.is_ready:
28
  logger.error("❌ Agente NO está listo.")
29
+
30
  logger.info("🚀 Cargando traductor local (es → en)...")
31
  self.translator = pipeline(
32
  "translation_es_to_en",
33
  model="Helsinki-NLP/opus-mt-es-en",
34
+ device=-1 # CPU
35
  )
36
  logger.info("✅ Traductor local listo.")
37
 
 
40
  return text
41
  try:
42
  result = self.translator(text, max_length=250, clean_up_tokenization_spaces=True)
43
+ return result[0]['translation_text'].strip()
44
  except Exception as e:
45
  logger.warning(f"Traducción local fallida: {e}. Usando texto original.")
46
+ return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  def retrieve_similar_examples(self, user_prompt_en: str, category: str = "auto", k: int = 6) -> list:
49
  if not self.agent.is_ready:
 
52
  query_embedding = self.agent.embedding_model.encode([user_prompt_en], convert_to_numpy=True, normalize_embeddings=True)[0]
53
  query_embedding = query_embedding.astype('float32').reshape(1, -1)
54
  distances, indices = self.agent.index.search(query_embedding, 50)
55
+
56
  candidates = []
57
  for idx in indices[0]:
58
  if 0 <= idx < len(self.agent.indexed_examples):
 
62
  if isinstance(caption, str) and len(caption) > 10:
63
  if category == "auto" or ex_category == category:
64
  candidates.append((idx, caption, ex_category))
65
+
66
  if not candidates:
67
  return []
68
+
69
  if len(candidates) <= k:
70
  return [cap for _, cap, _ in candidates]
71
+
72
  candidate_texts = [cap for _, cap, _ in candidates]
73
  pairs = [[user_prompt_en, cand] for cand in candidate_texts]
74
  scores = self.agent.reranker.predict(pairs)
75
+
76
  scored = [(candidates[i][1], scores[i]) for i in range(len(candidates))]
77
  scored.sort(key=lambda x: x[1], reverse=True)
78
+
79
  top_examples = [ex for ex, _ in scored[:k]]
80
  return top_examples
81
+
82
  except Exception as e:
83
+ logger.error(f"Error en recuperación con re-ranking y categoría: {e}")
84
  try:
85
  return [
86
  self.agent.indexed_examples[idx]['caption']
 
90
  except:
91
  return []
92
 
93
+ def _clean_output(self, text: str) -> str:
94
+ text = text.strip()
95
+ if text.startswith(("Here is", "Final:", "Output:", '"', "'")):
96
+ text = text.split(":", 1)[-1].strip().strip("\"'")
97
+ return text
98
+
99
  def refine_with_llm(self, user_prompt: str, category: str = "auto") -> Tuple[str, str, list]:
100
  user_prompt_en = self.translate_to_english(user_prompt)
101
  examples = self.retrieve_similar_examples(user_prompt_en, category=category, k=6)
102
 
103
+ if not examples:
104
+ fallbacks = {
105
+ "entity": [
106
+ "an elderly maya man weaving a hammock under a ceiba tree, golden hour light filtering through leaves, Antigua Guatemala setting, hyperrealistic style",
107
+ "a young indigenous woman in traditional Kekchi attire by Lake Atitlán, morning mist, volcano backdrop, soft natural light, documentary photography"
108
+ ],
109
+ "style": [
110
+ "oil painting of a forest in autumn, warm amber and crimson tones, impasto brushstrokes, style of Vincent van Gogh",
111
+ "cyberpunk cityscape at night, neon reflections on wet streets, cinematic lighting, style of Blade Runner 2049"
112
+ ],
113
+ "composition": [
114
+ "a lone wolf on a snowy mountain peak, northern lights in the sky, wind blowing snow, rule of thirds composition, photorealistic wildlife"
115
+ ],
116
+ "imaginative": [
117
+ "a floating island with ancient ruins, waterfalls cascading into clouds, golden hour, fantasy concept art, highly detailed"
118
+ ],
119
+ "text": [
120
+ "minimalist typography design, the word 'LIBERTAD' in bold sans-serif, high contrast black on white, professional layout"
121
+ ],
122
+ "auto": [
123
+ "an elderly maya man weaving a hammock under a ceiba tree, golden hour light filtering through leaves, Antigua Guatemala setting, hyperrealistic style, intricate textures of rope and bark",
124
+ "a cyberpunk street at night in Tokyo, neon signs reflecting on wet pavement, rain mist in air, distant flying cars, cinematic wide shot, Blade Runner atmosphere",
125
+ "a library interior with tall oak bookshelves, sunbeams through stained glass windows, dust particles floating, oil painting style, warm amber tones, masterpiece",
126
+ "a lone wolf howling on a snowy mountain peak, northern lights in the sky, wind blowing snow, photorealistic wildlife photography, 8k detailed fur",
127
+ "a steampunk airship floating above Victorian London, copper pipes and brass gears, cloudy sky, detailed machinery, concept art by Jakub Rozalski",
128
+ "a young woman in traditional Kekchi attire standing by Lake Atitlán, morning mist, volcano backdrop, soft natural light, documentary photography style"
129
+ ]
130
+ }
131
+ examples = fallbacks.get(category, fallbacks["auto"])
132
+ logger.warning(f"No se encontraron ejemplos para categoría '{category}'. Usando fallback.")
133
+
134
+ system_message = (
135
+ "You are a prompt engineering analyst for diffusion models (Midjourney, FLUX, SDXL). "
136
+ "Analyze the DESCRIPTIVE GRAMMAR (word order, phrasing, element sequence) used in the reference prompts below. "
137
+ "Reconstruct the user's concept using that exact same descriptive logic. "
138
+ "Do NOT follow a predefined template (e.g. subject→lighting→style). "
139
+ "Do NOT invent elements not implied by the user. "
140
+ "Preserve the user's core intent exactly. "
141
+ "Output ONLY the final prompt in English. No explanations, no markdown."
142
+ )
143
+
144
+ user_message = "User concept:\n" + user_prompt_en + "\n\nReference prompts (observe their descriptive grammar):\n" + "\n".join(examples)
145
+
146
+ # ✅ PRIMERO: Intentar con Together vía HF Router (más estable)
147
+ try:
148
+ client = OpenAI(
149
+ base_url="https://router.huggingface.co/v1",
150
+ api_key=os.getenv("PS")
151
+ )
152
+ completion = client.chat.completions.create(
153
+ model="meta-llama/Llama-3.2-3B-Instruct:together",
154
+ messages=[
155
+ {"role": "system", "content": system_message},
156
+ {"role": "user", "content": user_message}
157
+ ],
158
+ max_tokens=250,
159
+ temperature=0.2,
160
+ timeout=30.0
161
+ )
162
+ refined = self._clean_output(completion.choices[0].message.content)
163
+ info = f"🧠 Refinado con Llama-3.2-3B vía Together (HF Router, {len(examples)} ejemplos, categoría: {category})."
164
+ return refined, info, examples
165
+
166
+ except (APIError, Timeout, Exception) as e1:
167
+ logger.error(f"Error con Together (HF Router): {e1}")
168
+
169
+ # 🔄 SEGUNDO: Intentar con HF predeterminado (Hyperbolic)
170
+ try:
171
+ completion = self.hf_client.chat.completions.create(
172
+ model="meta-llama/Llama-3.2-3B-Instruct",
173
+ messages=[
174
+ {"role": "system", "content": system_message},
175
+ {"role": "user", "content": user_message}
176
+ ],
177
+ max_tokens=250,
178
+ temperature=0.2
179
+ )
180
+ refined = self._clean_output(completion.choices[0].message.content)
181
+ info = f"🧠 Fallback: HF (Hyperbolic, {len(examples)} ejemplos)."
182
+ return refined, info, examples
183
+
184
+ except Exception as e2:
185
+ logger.error(f"También falló Hyperbolic: {e2}")
186
+
187
+ # ⚠️ ÚLTIMO RECURSO: enriquecimiento semántico local
188
  enhanced_prompt, _ = self.agent.enhance_prompt(user_prompt_en, category=category)
189
+ return enhanced_prompt.strip(), f"⚠️ LLMs no disponibles. Usando enriquecimiento semántico (categoría: {category}).", examples
190
 
191
  class SDXLGenerator:
192
  def __init__(self):
 
223
  return "", "", "Prompt vacío."
224
  if refiner is None:
225
  return "", "", "Servicios no disponibles."
226
+
227
  progress(0.2, desc="🌍 Traduciendo...")
228
  category_map = {
229
  "Automática": "auto",
 
234
  "Texto": "text"
235
  }
236
  category_en = category_map.get(category_es, "auto")
237
+
238
  refined, info, examples = refiner.refine_with_llm(prompt, category_en)
239
  examples_text = "\n".join(f"{i+1}. {ex}" for i, ex in enumerate(examples)) if examples else "Ninguno"
240
  status = f"Prompt refinado: {refined}\n{info}"
 
245
  return None, "❌ No hay prompt refinado. Primero haz clic en 'Refinar prompt'."
246
  if generator is None:
247
  return None, "❌ Generador no inicializado."
248
+
249
  aspect_ratios = {
250
  "1:1": (1024, 1024),
251
  "16:9": (1344, 768),
 
256
  "9:21": (640, 1536),
257
  }
258
  width, height = aspect_ratios.get(aspect_ratio, (1024, 1024))
259
+ progress(0.5, desc="🎨 Generando imagen (puede tardar 10-20s)...")
260
  try:
261
  image_path, gen_msg = generator.generate_image(refined_prompt, width, height)
262
  return image_path, gen_msg
263
  except Exception as e:
264
+ error_msg = f"❌ Error al generar: {str(e)}"
265
  logger.error(error_msg)
266
  return None, error_msg
267
 
 
290
  prompt_input = gr.Textbox(label="Tu idea (en castellano)", lines=3, placeholder="Ej: un mercado en Antigua Guatemala...")
291
  category_es = gr.Dropdown(label="Categoría", choices=CATEGORY_CHOICES_ES, value="Automática")
292
  aspect = gr.Dropdown(label="Proporción", choices=["1:1", "16:9", "9:16", "4:3", "3:4", "21:9", "9:21"], value="1:1")
293
+
294
  refine_btn = gr.Button("🔄 Refinar prompt", variant="secondary")
295
  generate_btn = gr.Button("🎨 Generar imagen", variant="primary")
296
+
297
  with gr.Column():
298
  refined_output = gr.Textbox(
299
  label="Prompt refinado (inglés)",