appinitdev commited on
Commit
0ca29f6
·
verified ·
1 Parent(s): eadca35

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -41
app.py CHANGED
@@ -1,60 +1,110 @@
1
  import os
 
2
  import traceback
 
 
3
  import gradio as gr
4
- from gradio_client import Client
5
 
 
 
6
 
7
- # 1. Inicializamos el cliente oficial de Hugging Face que hereda las DNS del clúster
8
- HF_TOKEN = os.getenv("HF_TOKEN")
9
 
10
- # Usamos la API Serverless mediante el cliente nativo de Gradio
11
- try:
12
- # Vincula directamente el modelo espacial sin usar peticiones HTTP crudas
13
- hf_client = Client("joyfox/Qwen-Image-Edit-Cosplay", hf_token=HF_TOKEN)
14
- except Exception as e:
15
- print(f"[-] Error inicializando el cliente de Hugging Face: {e}")
16
- hf_client = None
 
 
17
 
18
- def fusionar_cosplay_bridge(imagen_real, imagen_ref):
19
- if not HF_TOKEN or hf_client is None:
20
- raise gr.Error("El servidor no tiene configurado o validado el HF_TOKEN en los Secrets.")
21
 
22
  try:
23
- print("[+] Petición recibida desde Android. Procesando a través del cliente nativo...")
24
 
25
- # Aseguramos los prefijos Data URI limpios
26
- data_uri_real = imagen_real if imagen_real.startswith("data:") else f"data:image/jpeg;base64,{imagen_real}"
27
- data_uri_ref = imagen_ref if imagen_ref.startswith("data:") else f"data:image/jpeg;base64,{imagen_ref}"
28
 
 
29
  prompt_texto = "把图2中动漫人物中的发型,服饰和装扮道具迁移到图1的真实人物,图1真实人物保持高度人脸一致性"
30
 
31
- print("[+] Enviando parámetros al modelo Qwen...")
 
 
 
 
 
 
 
 
 
32
 
33
- # Al usar .predict() del cliente nativo, Gradio resuelve internamente la red sin lanzar NameResolutionError
34
- resultado = hf_client.predict(
35
- parameter_0=data_uri_real,
36
- parameter_1=data_uri_ref,
37
- parameter_2=prompt_texto,
38
- api_name="/predict"
39
- )
40
 
41
- print(f"[+] Procesamiento exitoso. Retornando estructura...")
42
- return {"data": resultado}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
 
 
44
  except Exception as e:
45
- print("\n================= CRASH EN EL PUENTE =================")
46
  traceback.print_exc()
47
  print("=====================================================\n")
48
- raise gr.Error(f"Crash interno en el puente: {str(e)}")
49
-
50
- # 2. Interfaz limpia expuesta como API universal para Android
51
- demo = gr.Interface(
52
- fn=fusionar_cosplay_bridge,
53
- inputs=[gr.Textbox(label="Real"), gr.Textbox(label="Ref")],
54
- outputs=gr.JSON(label="Resultado"),
55
- api_name="predict"
56
- )
57
-
58
- # Desactivamos el SSR experimental para máxima compatibilidad con clientes HTTP crudos
59
- if __name__ == "__main__":
60
- demo.launch(ssr_mode=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import base64
3
  import traceback
4
+ import requests
5
+ import fastapi
6
  import gradio as gr
7
+ from pydantic import BaseModel
8
 
9
+ # 1. Inicializamos FastAPI de manera independiente
10
+ io_app = fastapi.FastAPI()
11
 
12
+ class CosplayPayload(BaseModel):
13
+ data: list[str]
14
 
15
+ @io_app.post("/api/predict")
16
+ async def fusionar_cosplay(payload: CosplayPayload):
17
+ # 🌟 CRÍTICO: Buscamos la variable de entorno directamente ADENTRO de la función
18
+ # Esto asegura que FastAPI lea el Secret inyectado en tiempo de ejecución en cada request.
19
+ token_dinamico = os.getenv("HF_TOKEN")
20
+
21
+ if not token_dinamico:
22
+ print("[-] ERROR: El HF_TOKEN no está configurado en las variables (Secrets) del Space.")
23
+ raise fastapi.HTTPException(status_code=500, detail="Falta el token HF_TOKEN en el servidor.")
24
 
25
+ if len(payload.data) < 2:
26
+ raise fastapi.HTTPException(status_code=400, detail="Se requieren exactamente 2 imágenes.")
 
27
 
28
  try:
29
+ print("[+] Petición recibida desde Android. Procesando cadenas Base64...")
30
 
31
+ # Aseguramos el prefijo Data URI para el JSON de Qwen
32
+ data_uri_real = payload.data[0] if payload.data[0].startswith("data:") else f"data:image/jpeg;base64,{payload.data[0]}"
33
+ data_uri_ref = payload.data[1] if payload.data[1].startswith("data:") else f"data:image/jpeg;base64,{payload.data[1]}"
34
 
35
+ # 🌟 Optimización del prompt para el modelo Qwen (removiendo el 'and' híbrido)
36
  prompt_texto = "把图2中动漫人物中的发型,服饰和装扮道具迁移到图1的真实人物,图1真实人物保持高度人脸一致性"
37
 
38
+ # Payload JSON limpio para la API de Inferencia Serverless
39
+ json_payload = {
40
+ "inputs": {
41
+ "text": prompt_texto,
42
+ "images": [data_uri_real, data_uri_ref]
43
+ },
44
+ "parameters": {
45
+ "max_new_tokens": 512
46
+ }
47
+ }
48
 
49
+ headers = {
50
+ "Authorization": f"Bearer {token_dinamico}",
51
+ "Content-Type": "application/json"
52
+ }
 
 
 
53
 
54
+ print("[+] Enviando petición a la API de Inferencia de Hugging Face mediante el puente...")
55
+
56
+ # Petición HTTP POST síncrona externa con un timeout prudente
57
+ API_URL = "https://api-inference.huggingface.co/models/joyfox/Qwen-Image-Edit-Cosplay"
58
+ respuesta = requests.post(API_URL, headers=headers, json=json_payload, timeout=110)
59
+ print(f"[+] Respuesta recibida. Código de estado: {respuesta.status_code}")
60
+
61
+ if respuesta.status_code == 200:
62
+ content_type = respuesta.headers.get("content-type", "").lower()
63
+
64
+ if "application/json" in content_type:
65
+ return respuesta.json()
66
+ else:
67
+ imagen_procesada_base64 = base64.b64encode(respuesta.content).decode("utf-8")
68
+ return {"data": f"data:image/jpeg;base64,{imagen_procesada_base64}"}
69
+ else:
70
+ print(f"[-] Error de la API de Inferencia: {respuesta.text}")
71
+ raise fastapi.HTTPException(status_code=502, detail=f"HF API Error: {respuesta.status_code}")
72
 
73
+ except fastapi.HTTPException as status_error:
74
+ raise status_error
75
  except Exception as e:
76
+ print("\n================= CRASH EN EL PROXY =================")
77
  traceback.print_exc()
78
  print("=====================================================\n")
79
+ raise fastapi.HTTPException(status_code=500, detail=f"Crash interno: {str(e)}")
80
+
81
+ # 2. Función puente exclusiva para que la interfaz web de Gradio pueda probarse manualmente si lo deseas
82
+ def prueba_desde_interfaz_web(real_b64, ref_b64):
83
+ token_gradio = os.getenv("HF_TOKEN")
84
+ if not token_gradio:
85
+ return "Error: Agrega el HF_TOKEN en Settings -> Secrets antes de presionar Submit."
86
+
87
+ # Redirigimos la lógica interna de Gradio a la misma lógica de negocio de la función principal
88
+ try:
89
+ import asyncio
90
+ class FakePayload:
91
+ data = [real_b64, ref_b64]
92
+ res = asyncio.run(fusionar_cosplay(FakePayload()))
93
+ return str(res)
94
+ except Exception as err:
95
+ return f"Error en la prueba web: {str(err)}"
96
+
97
+ # 3. Interfaz de Gradio (Obligatoria para la plataforma)
98
+ with gr.Blocks() as demo:
99
+ gr.Markdown("# Cosplay API Bridge Active 🎭")
100
+ gr.Markdown("Este Space está corriendo un servidor FastAPI en segundo plano optimizado para peticiones desde Android.")
101
+
102
+ with gr.Row(visible=False): # Dejamos la interfaz visual minimizada o para logs internos
103
+ txt_real = gr.Textbox(label="Real")
104
+ txt_ref = gr.Textbox(label="Ref")
105
+ btn_sub = gr.Button("Submit")
106
+ txt_out = gr.Textbox(label="Output")
107
+ btn_sub.click(fn=prueba_desde_interfaz_web, inputs=[txt_real, txt_ref], outputs=txt_out)
108
+
109
+ # 4. Montamos la aplicación de FastAPI dentro de la instancia de Gradio 'demo'
110
+ app = gr.mount_gradio_app(io_app, demo, path="/")