BATUTO-ART commited on
Commit
d0aba05
·
verified ·
1 Parent(s): 610d436

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -96
app.py CHANGED
@@ -13,22 +13,29 @@ import aiohttp
13
  import gradio as gr
14
  from PIL import Image
15
  import warnings
 
 
16
 
17
  # 忽略asyncio警告
18
  warnings.filterwarnings("ignore", category=DeprecationWarning)
19
 
20
- # 设置事件循环策略(修复Python 3.13+兼容性问题)
21
  if hasattr(asyncio, 'WindowsProactorEventLoopPolicy') and isinstance(asyncio.get_event_loop_policy(), asyncio.WindowsProactorEventLoopPolicy):
22
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
23
 
 
24
  SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY", "").strip()
25
  HF_TOKEN = os.getenv("HF_TOKEN", "").strip()
26
  REVE_API_KEY = os.getenv("REVE_API_KEY", "").strip()
27
 
 
28
  OUTPUT_DIR = "generaciones_batuto"
 
29
  LOG_FILE = "neurocore_logs.jsonl"
30
  os.makedirs(OUTPUT_DIR, exist_ok=True)
 
31
 
 
32
  SAMBA_MODELS = [
33
  "DeepSeek-R1", "DeepSeek-V3.1", "DeepSeek-V3", "DeepSeek-V3-0324",
34
  "Meta-Llama-3.3-70B-Instruct", "Llama-4-Maverick-17B-128E-Instruct",
@@ -49,7 +56,8 @@ HF_MODELS = [
49
  "Qwen/Qwen3-Coder-Plus", "Qwen/Qwen3-Omni-30B-A3B-Instruct"
50
  ]
51
 
52
- ALL_MODELS = ["AUTO-SELECT"] + SAMBA_MODELS + HF_MODELS
 
53
 
54
  CSS = """
55
  :root{--primary:#00C896;--secondary:#00FFE0;--bg:#000;--border:rgba(0,200,150,.35);}
@@ -57,25 +65,97 @@ body,.gradio-container{background:#000!important; color: #fff !important;}
57
  .panel{border:1px solid var(--border);border-radius:16px;padding:12px}
58
  .dark .gradio-container {background: #000 !important;}
59
  .dark .gr-button-primary {background: linear-gradient(45deg, #00C896, #00FFE0) !important;}
 
60
  """
61
 
62
  def log_event(data: dict):
 
63
  data["timestamp"] = datetime.datetime.now().isoformat()
64
  with open(LOG_FILE, "a", encoding="utf-8") as f:
65
  f.write(json.dumps(data, ensure_ascii=False) + "\n")
66
 
67
  def save_generation(content, model_name, type="text"):
 
68
  filename = f"{model_name.replace('/', '_')}_{int(time.time())}.txt"
69
  path = os.path.join(OUTPUT_DIR, filename)
70
  with open(path, "w", encoding="utf-8") as f:
71
  f.write(content)
72
  return path
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  def smart_select(prompt: str) -> str:
 
75
  p = prompt.lower()
76
  if any(x in p for x in ["código", "python", "script", "programa", "code"]):
77
  return "DeepSeek-Coder-V2"
78
- if any(x in p for x in ["razona", "piensa", "matemáticas", "math", "logic"]):
79
  return "DeepSeek-R1"
80
  if any(x in p for x in ["vision", "mira", "describe", "imagen", "image"]):
81
  return "Meta-Llama-3.2-11B-Vision-Instruct"
@@ -84,7 +164,7 @@ def smart_select(prompt: str) -> str:
84
  return "DeepSeek-V3.1"
85
 
86
  async def stream_samba(model: str, prompt: str, temp: float, tokens: int) -> AsyncGenerator[str, None]:
87
- """Stream response from Sambanova API"""
88
  url = "https://api.sambanova.ai/v1/chat/completions"
89
  headers = {"Authorization": f"Bearer {SAMBANOVA_API_KEY}", "Content-Type": "application/json"}
90
  payload = {
@@ -128,73 +208,37 @@ async def stream_samba(model: str, prompt: str, temp: float, tokens: int) -> Asy
128
  save_generation(full_res, model)
129
  log_event({"model": model, "prompt": prompt[:100], "response_length": len(full_res)})
130
 
131
- async def run_reve(prompt: str, n: int) -> List[Image.Image]:
132
- """Generate images using REVE API"""
133
- if not REVE_API_KEY:
134
- return []
135
-
136
- url = "https://api.reve.com/v1/image/create"
137
- headers = {"Authorization": f"Bearer {REVE_API_KEY}", "Content-Type": "application/json"}
138
- imgs = []
139
-
140
- try:
141
- timeout = aiohttp.ClientTimeout(total=120.0)
142
- async with aiohttp.ClientSession(timeout=timeout) as session:
143
- tasks = []
144
- for i in range(n):
145
- task = session.post(url, headers=headers, json={"prompt": prompt})
146
- tasks.append(task)
147
-
148
- responses = await asyncio.gather(*tasks, return_exceptions=True)
149
-
150
- for response in responses:
151
- if isinstance(response, Exception):
152
- continue
153
- if response.status == 200:
154
- try:
155
- data = await response.json()
156
- for img_data in data.get("images", []):
157
- img_bytes = base64.b64decode(img_data)
158
- img = Image.open(BytesIO(img_bytes))
159
- imgs.append(img)
160
- except Exception as e:
161
- print(f"Error procesando imagen: {e}")
162
- except Exception as e:
163
- print(f"Error en REVE API: {e}")
164
-
165
- return imgs
166
-
167
- def handle_execution(model: str, prompt: str, temp: float, tokens: int, n: int):
168
- """Handle execution based on model selection"""
169
  if not prompt.strip():
170
  return "Por favor ingresa un comando.", []
171
 
172
  active_model = smart_select(prompt) if model == "AUTO-SELECT" else model
173
 
174
- # Verificar que tenemos la API key necesaria
175
- if active_model in SAMBA_MODELS and not SAMBANOVA_API_KEY:
176
- return "Error: Falta SAMBANOVA_API_KEY en las variables de entorno.", []
177
-
178
  if active_model == "REVE":
179
  if not REVE_API_KEY:
180
- return "Error: Falta REVE_API_KEY en las variables de entorno.", []
181
 
182
- # Ejecutar en un nuevo evento de asyncio
183
- try:
184
- loop = asyncio.new_event_loop()
185
- asyncio.set_event_loop(loop)
186
- images = loop.run_until_complete(run_reve(prompt, n))
187
- loop.close()
188
- return f"✅ Generadas {len(images)} imágenes con REVE.", images
189
- except Exception as e:
190
- return f"Error generando imágenes: {str(e)}", []
191
 
192
- # Para modelos de texto, retornamos un generador
 
 
 
 
193
  return stream_samba(active_model, prompt, temp, tokens)
194
 
195
  def create_interface():
196
- """Create the Gradio interface"""
197
- with gr.Blocks(title="BATUTO X • Neurocore", css=CSS, theme=gr.themes.Default(primary_hue="emerald", neutral_hue="zinc")) as demo:
198
  gr.HTML("""
199
  <div style="text-align: center; padding: 20px; background: linear-gradient(45deg, #000, #001a14); border-radius: 16px; margin-bottom: 20px;">
200
  <h1 style="color: #00C896; margin: 0; font-size: 2.5em;">⚡ BATUTO X • NEUROCORE PRO</h1>
@@ -202,6 +246,9 @@ def create_interface():
202
  </div>
203
  """)
204
 
 
 
 
205
  with gr.Row():
206
  with gr.Column(scale=1):
207
  with gr.Group():
@@ -211,46 +258,62 @@ def create_interface():
211
  label="🧠 Modelo",
212
  info="Selecciona un modelo o usa AUTO-SELECT para detección inteligente"
213
  )
 
214
  temp_opt = gr.Slider(
215
  0, 1.5, 0.7,
216
  label="🌡️ Temperature",
217
  info="Controla la aleatoriedad (0 = determinístico, 1.5 = muy creativo)"
218
  )
 
219
  tokens_opt = gr.Slider(
220
  128, 8192, 2048,
221
  step=128,
222
  label="📏 Máximo Tokens",
223
  info="Longitud máxima de la respuesta"
224
  )
225
- num_opt = gr.Slider(
226
- 1, 4, 1, step=1,
227
- label="🖼️ Cantidad de Imágenes",
228
- visible=False,
229
- info="Solo aplica para modelos de imagen"
230
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
  with gr.Column(scale=2):
233
  with gr.Group():
234
  prompt_input = gr.Textbox(
235
  lines=5,
236
  label="💬 Entrada",
237
- placeholder="Escribe tu comando aquí...\nEjemplo: 'Genera un código Python para ordenar una lista' o 'Describe esta imagen: [URL o descripción]'",
238
  elem_classes=["prompt-box"]
239
  )
240
 
241
  send_btn = gr.Button(
242
  "🚀 EJECUTAR COMANDO",
243
  variant="primary",
244
- size="lg",
245
- elem_classes=["execute-btn"]
246
  )
247
 
248
  with gr.Group():
249
  canvas = gr.Textbox(
250
  lines=12,
251
  label="📤 Salida",
252
- interactive=False,
253
- elem_classes=["output-box"]
254
  )
255
 
256
  gallery = gr.Gallery(
@@ -260,27 +323,37 @@ def create_interface():
260
  visible=False
261
  )
262
 
263
- # Mostrar/ocultar controles basado en selección de modelo
264
  def toggle_controls(model):
265
  if model == "REVE":
266
- return [gr.Slider(visible=True), gr.Gallery(visible=True)]
 
 
 
 
 
267
  else:
268
- return [gr.Slider(visible=False), gr.Gallery(visible=False)]
 
 
 
 
 
269
 
270
  model_opt.change(
271
  fn=toggle_controls,
272
  inputs=model_opt,
273
- outputs=[num_opt, gallery]
274
  )
275
 
276
- # Conectar el botón
277
  send_btn.click(
278
  fn=handle_execution,
279
- inputs=[model_opt, prompt_input, temp_opt, tokens_opt, num_opt],
280
  outputs=[canvas, gallery]
281
  )
282
 
283
- # Ejemplos
284
  with gr.Accordion("📚 Ejemplos de Uso", open=False):
285
  gr.Examples(
286
  examples=[
@@ -296,36 +369,40 @@ def create_interface():
296
 
297
  return demo
298
 
299
- if __name__ == "__main__":
300
- # Configurar logging
301
- print(f"🚀 Iniciando BATUTO X Neurocore")
302
  print(f"📁 Directorio de salida: {os.path.abspath(OUTPUT_DIR)}")
 
303
  print(f"📝 Archivo de logs: {os.path.abspath(LOG_FILE)}")
304
 
305
  if SAMBANOVA_API_KEY:
306
  print("✅ SAMBANOVA_API_KEY configurada")
307
  else:
308
- print("⚠️ SAMBANOVA_API_KEY no encontrada - algunos modelos no funcionarán")
309
 
310
  if REVE_API_KEY:
311
  print("✅ REVE_API_KEY configurada")
312
  else:
313
- print("⚠️ REVE_API_KEY no encontrada - generación de imágenes no disponible")
314
 
315
- # Crear y lanzar la interfaz
316
  demo = create_interface()
317
 
318
- try:
319
- demo.launch(
320
- server_name="0.0.0.0",
321
- server_port=int(os.getenv("PORT", "7860")),
322
- share=os.getenv("GRADIO_SHARE", "False").lower() == "true",
323
- debug=False,
324
- show_error=True,
325
- quiet=True
 
 
 
 
326
  )
327
- except KeyboardInterrupt:
328
- print("\n🛑 Aplicación detenida por el usuario")
329
- except Exception as e:
330
- print(f"❌ Error al iniciar la aplicación: {e}")
331
- raise
 
13
  import gradio as gr
14
  from PIL import Image
15
  import warnings
16
+ import requests
17
+ import concurrent.futures
18
 
19
  # 忽略asyncio警告
20
  warnings.filterwarnings("ignore", category=DeprecationWarning)
21
 
22
+ # 设置事件循环策略
23
  if hasattr(asyncio, 'WindowsProactorEventLoopPolicy') and isinstance(asyncio.get_event_loop_policy(), asyncio.WindowsProactorEventLoopPolicy):
24
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
25
 
26
+ # API配置
27
  SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY", "").strip()
28
  HF_TOKEN = os.getenv("HF_TOKEN", "").strip()
29
  REVE_API_KEY = os.getenv("REVE_API_KEY", "").strip()
30
 
31
+ # 目录配置
32
  OUTPUT_DIR = "generaciones_batuto"
33
+ REVE_OUTPUT_DIR = "generaciones_reve"
34
  LOG_FILE = "neurocore_logs.jsonl"
35
  os.makedirs(OUTPUT_DIR, exist_ok=True)
36
+ os.makedirs(REVE_OUTPUT_DIR, exist_ok=True)
37
 
38
+ # 模型列表
39
  SAMBA_MODELS = [
40
  "DeepSeek-R1", "DeepSeek-V3.1", "DeepSeek-V3", "DeepSeek-V3-0324",
41
  "Meta-Llama-3.3-70B-Instruct", "Llama-4-Maverick-17B-128E-Instruct",
 
56
  "Qwen/Qwen3-Coder-Plus", "Qwen/Qwen3-Omni-30B-A3B-Instruct"
57
  ]
58
 
59
+ # 添加REVE作为特殊模型
60
+ ALL_MODELS = ["AUTO-SELECT", "REVE"] + SAMBA_MODELS + HF_MODELS
61
 
62
  CSS = """
63
  :root{--primary:#00C896;--secondary:#00FFE0;--bg:#000;--border:rgba(0,200,150,.35);}
 
65
  .panel{border:1px solid var(--border);border-radius:16px;padding:12px}
66
  .dark .gradio-container {background: #000 !important;}
67
  .dark .gr-button-primary {background: linear-gradient(45deg, #00C896, #00FFE0) !important;}
68
+ .gr-button-primary {background: linear-gradient(45deg, #00C896, #00FFE0) !important;}
69
  """
70
 
71
  def log_event(data: dict):
72
+ """记录事件日志"""
73
  data["timestamp"] = datetime.datetime.now().isoformat()
74
  with open(LOG_FILE, "a", encoding="utf-8") as f:
75
  f.write(json.dumps(data, ensure_ascii=False) + "\n")
76
 
77
  def save_generation(content, model_name, type="text"):
78
+ """保存生成的文本"""
79
  filename = f"{model_name.replace('/', '_')}_{int(time.time())}.txt"
80
  path = os.path.join(OUTPUT_DIR, filename)
81
  with open(path, "w", encoding="utf-8") as f:
82
  f.write(content)
83
  return path
84
 
85
+ def guardar_imagen_local(img, index):
86
+ """保存图像到本地"""
87
+ try:
88
+ timestamp = int(time.time())
89
+ nombre_archivo = f"reve_{timestamp}_{index}.png"
90
+ ruta_completa = os.path.join(REVE_OUTPUT_DIR, nombre_archivo)
91
+ img.save(ruta_completa)
92
+ return ruta_completa
93
+ except Exception as e:
94
+ print(f"⚠️ Error guardando: {e}")
95
+ return None
96
+
97
+ def llamar_api_reve(prompt, ratio, version, api_key, index):
98
+ """调用REVE API生成单张图片"""
99
+ API_URL = "https://api.reve.com/v1/image/create"
100
+
101
+ payload = {
102
+ "prompt": prompt,
103
+ "aspect_ratio": ratio,
104
+ "version": version
105
+ }
106
+ headers = {
107
+ "Authorization": f"Bearer {api_key}",
108
+ "Accept": "application/json",
109
+ "Content-Type": "application/json"
110
+ }
111
+
112
+ try:
113
+ response = requests.post(API_URL, headers=headers, json=payload, timeout=60)
114
+ if response.status_code == 200:
115
+ data = response.json()
116
+ if "image" in data:
117
+ img_bytes = base64.b64decode(data["image"])
118
+ img = Image.open(BytesIO(img_bytes))
119
+ guardar_imagen_local(img, index)
120
+ return img, data.get('credits_used', 0), None
121
+ return None, 0, f"Error {response.status_code}: {response.text}"
122
+ except Exception as e:
123
+ return None, 0, f"Excepción: {str(e)}"
124
+
125
+ def generar_imagenes_batch(prompt, api_key, ratio="9:16", version="latest", num_imagenes=1):
126
+ """批量生成REVE图片"""
127
+ if not api_key:
128
+ return [], "❌ ¡Falta la API Key de REVE! Configúrala en las variables de entorno."
129
+
130
+ imagenes_nuevas = []
131
+ errores = []
132
+ creditos_totales = 0
133
+
134
+ with concurrent.futures.ThreadPoolExecutor(max_workers=num_imagenes) as executor:
135
+ futuros = [
136
+ executor.submit(llamar_api_reve, prompt, ratio, version, api_key, i)
137
+ for i in range(num_imagenes)
138
+ ]
139
+
140
+ for futuro in concurrent.futures.as_completed(futuros):
141
+ img, creditos, error = futuro.result()
142
+ if img:
143
+ imagenes_nuevas.append(img)
144
+ creditos_totales += creditos
145
+ if error:
146
+ errores.append(error)
147
+
148
+ if imagenes_nuevas:
149
+ return imagenes_nuevas, f"✅ {len(imagenes_nuevas)} imágenes generadas | Créditos usados: {creditos_totales}"
150
+ else:
151
+ return [], f"❌ Error: {'; '.join(errores[:2])}"
152
+
153
  def smart_select(prompt: str) -> str:
154
+ """智能选择模型"""
155
  p = prompt.lower()
156
  if any(x in p for x in ["código", "python", "script", "programa", "code"]):
157
  return "DeepSeek-Coder-V2"
158
+ if any(x in p for x in ["razona", "piensa", "matemáticas", "math", "logic", "resuelve"]):
159
  return "DeepSeek-R1"
160
  if any(x in p for x in ["vision", "mira", "describe", "imagen", "image"]):
161
  return "Meta-Llama-3.2-11B-Vision-Instruct"
 
164
  return "DeepSeek-V3.1"
165
 
166
  async def stream_samba(model: str, prompt: str, temp: float, tokens: int) -> AsyncGenerator[str, None]:
167
+ """Sambanova API流式获取响应"""
168
  url = "https://api.sambanova.ai/v1/chat/completions"
169
  headers = {"Authorization": f"Bearer {SAMBANOVA_API_KEY}", "Content-Type": "application/json"}
170
  payload = {
 
208
  save_generation(full_res, model)
209
  log_event({"model": model, "prompt": prompt[:100], "response_length": len(full_res)})
210
 
211
+ def handle_execution(model: str, prompt: str, temp: float, tokens: int, n: int, ratio: str, version: str):
212
+ """处理执行请求"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  if not prompt.strip():
214
  return "Por favor ingresa un comando.", []
215
 
216
  active_model = smart_select(prompt) if model == "AUTO-SELECT" else model
217
 
218
+ # 处理REVE图像生成
 
 
 
219
  if active_model == "REVE":
220
  if not REVE_API_KEY:
221
+ return "Error: Falta REVE_API_KEY en las variables de entorno.", []
222
 
223
+ images, message = generar_imagenes_batch(
224
+ prompt,
225
+ REVE_API_KEY,
226
+ ratio,
227
+ version,
228
+ n
229
+ )
230
+ return message, images
 
231
 
232
+ # 处理文本模型
233
+ if active_model in SAMBA_MODELS and not SAMBANOVA_API_KEY:
234
+ return "❌ Error: Falta SAMBANOVA_API_KEY en las variables de entorno.", []
235
+
236
+ # 返回流式生成器
237
  return stream_samba(active_model, prompt, temp, tokens)
238
 
239
  def create_interface():
240
+ """创建Gradio界面"""
241
+ with gr.Blocks(title="BATUTO X • Neurocore") as demo:
242
  gr.HTML("""
243
  <div style="text-align: center; padding: 20px; background: linear-gradient(45deg, #000, #001a14); border-radius: 16px; margin-bottom: 20px;">
244
  <h1 style="color: #00C896; margin: 0; font-size: 2.5em;">⚡ BATUTO X • NEUROCORE PRO</h1>
 
246
  </div>
247
  """)
248
 
249
+ # 用于存储额外控件的状态
250
+ extra_controls_state = gr.State({"show_image_controls": False})
251
+
252
  with gr.Row():
253
  with gr.Column(scale=1):
254
  with gr.Group():
 
258
  label="🧠 Modelo",
259
  info="Selecciona un modelo o usa AUTO-SELECT para detección inteligente"
260
  )
261
+
262
  temp_opt = gr.Slider(
263
  0, 1.5, 0.7,
264
  label="🌡️ Temperature",
265
  info="Controla la aleatoriedad (0 = determinístico, 1.5 = muy creativo)"
266
  )
267
+
268
  tokens_opt = gr.Slider(
269
  128, 8192, 2048,
270
  step=128,
271
  label="📏 Máximo Tokens",
272
  info="Longitud máxima de la respuesta"
273
  )
274
+
275
+ # REVE特定的控件(初始隐藏)
276
+ with gr.Group(visible=False) as image_controls:
277
+ num_opt = gr.Slider(
278
+ 1, 4, 1, step=1,
279
+ label="🖼️ Cantidad de Imágenes",
280
+ info="Número de imágenes a generar"
281
+ )
282
+
283
+ ratio_opt = gr.Dropdown(
284
+ ["16:9", "9:16", "3:2", "2:3", "4:3", "3:4", "1:1"],
285
+ value="9:16",
286
+ label="📐 Aspect Ratio",
287
+ info="Proporción de la imagen"
288
+ )
289
+
290
+ version_opt = gr.Dropdown(
291
+ ["latest", "reve-create@20250915"],
292
+ value="latest",
293
+ label="🔧 Versión",
294
+ info="Versión del modelo REVE"
295
+ )
296
 
297
  with gr.Column(scale=2):
298
  with gr.Group():
299
  prompt_input = gr.Textbox(
300
  lines=5,
301
  label="💬 Entrada",
302
+ placeholder="Escribe tu comando aquí...\nEjemplo: 'Genera un código Python para ordenar una lista' o 'Crea una imagen de un dragón cibernético'",
303
  elem_classes=["prompt-box"]
304
  )
305
 
306
  send_btn = gr.Button(
307
  "🚀 EJECUTAR COMANDO",
308
  variant="primary",
309
+ size="lg"
 
310
  )
311
 
312
  with gr.Group():
313
  canvas = gr.Textbox(
314
  lines=12,
315
  label="📤 Salida",
316
+ interactive=False
 
317
  )
318
 
319
  gallery = gr.Gallery(
 
323
  visible=False
324
  )
325
 
326
+ # 根据模型选择显示/隐藏控件
327
  def toggle_controls(model):
328
  if model == "REVE":
329
+ return [
330
+ gr.Group(visible=True), # image_controls
331
+ gr.Gallery(visible=True), # gallery
332
+ gr.Textbox(visible=True), # canvas
333
+ {"show_image_controls": True}
334
+ ]
335
  else:
336
+ return [
337
+ gr.Group(visible=False), # image_controls
338
+ gr.Gallery(visible=False), # gallery
339
+ gr.Textbox(visible=True), # canvas
340
+ {"show_image_controls": False}
341
+ ]
342
 
343
  model_opt.change(
344
  fn=toggle_controls,
345
  inputs=model_opt,
346
+ outputs=[image_controls, gallery, canvas, extra_controls_state]
347
  )
348
 
349
+ # 连接执行按钮
350
  send_btn.click(
351
  fn=handle_execution,
352
+ inputs=[model_opt, prompt_input, temp_opt, tokens_opt, num_opt, ratio_opt, version_opt],
353
  outputs=[canvas, gallery]
354
  )
355
 
356
+ # 示例
357
  with gr.Accordion("📚 Ejemplos de Uso", open=False):
358
  gr.Examples(
359
  examples=[
 
369
 
370
  return demo
371
 
372
+ def main():
373
+ """主函数"""
374
+ print("🚀 Iniciando BATUTO X Neurocore")
375
  print(f"📁 Directorio de salida: {os.path.abspath(OUTPUT_DIR)}")
376
+ print(f"🎨 Directorio de imágenes REVE: {os.path.abspath(REVE_OUTPUT_DIR)}")
377
  print(f"📝 Archivo de logs: {os.path.abspath(LOG_FILE)}")
378
 
379
  if SAMBANOVA_API_KEY:
380
  print("✅ SAMBANOVA_API_KEY configurada")
381
  else:
382
+ print("⚠️ SAMBANOVA_API_KEY no encontrada - modelos Sambanova no funcionarán")
383
 
384
  if REVE_API_KEY:
385
  print("✅ REVE_API_KEY configurada")
386
  else:
387
+ print("⚠️ REVE_API_KEY no encontrada - generación de imágenes REVE no disponible")
388
 
389
+ # 创建界面
390
  demo = create_interface()
391
 
392
+ # 启动应用
393
+ demo.launch(
394
+ server_name="0.0.0.0",
395
+ server_port=int(os.getenv("PORT", "7860")),
396
+ share=False,
397
+ debug=False,
398
+ show_error=True,
399
+ css=CSS,
400
+ theme=gr.themes.Default(
401
+ primary_hue="emerald",
402
+ neutral_hue="zinc",
403
+ font=[gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"]
404
  )
405
+ )
406
+
407
+ if __name__ == "__main__":
408
+ main()