Spaces:
Paused
Paused
File size: 10,016 Bytes
fc8e69c 1e1df5b fc8e69c 1e1df5b fc8e69c 1e1df5b fc8e69c 3f4e88d 1e1df5b 3f4e88d 1e1df5b 3f4e88d 1e1df5b 3f4e88d 1e1df5b fc8e69c 30a0766 fc8e69c 1e1df5b fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b fc8e69c 1e1df5b 4300251 b5aeef7 4300251 1e1df5b fc8e69c 1e1df5b fc8e69c 1e1df5b e1676e0 1e1df5b e1676e0 fc8e69c e1676e0 1e1df5b e1676e0 fc8e69c 30a0766 fc8e69c 1e1df5b fc8e69c 1e1df5b fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b 4300251 1e1df5b fc8e69c 1e1df5b fc8e69c 1e1df5b e1676e0 fc8e69c 1e1df5b fc8e69c 1e1df5b dff87bb 1e1df5b dff87bb 1e1df5b 632bee9 1e1df5b 632bee9 1e1df5b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
#!/usr/bin/env python3
"""
VINCIE Service UI (Gradio) — Multi-GPU Pool Manager
- Importa e utiliza o singleton 'vince_pool_manager_singleton'.
- A instância do manager é criada uma única vez no primeiro import, mantendo
os modelos "quentes" em todas as GPUs disponíveis.
- A UI do Gradio despacha as tarefas para o pool manager, que as distribui
automaticamente entre os workers da GPU.
- UI minimalista: galeria e vídeo por aba, com opções avançadas.
"""
import os
import gradio as gr
from pathlib import Path
from typing import List, Tuple, Optional
# ==============================================================================
# <<< PONTO CENTRAL DA INTEGRAÇÃO >>>
# Importamos o singleton do nosso novo pool manager.
# A inicialização pesada (download, carregamento de modelos para 4 GPUs)
# acontece automaticamente dentro deste módulo na primeira vez que ele é importado.
# ==============================================================================
try:
from services.vince_pool_manager import vince_pool_manager_singleton as server
except Exception as e:
print(f"ERRO FATAL: Não foi possível importar o VincePoolManager. A aplicação não pode iniciar.")
print(f"Detalhe do erro: {e}")
# Se o import falhar, a aplicação não tem como funcionar.
# Lançamos um erro para que os logs mostrem claramente o problema.
raise RuntimeError("Falha na inicialização do VincePoolManager.") from e
# Verificação para garantir que o singleton foi criado com sucesso.
if server is None:
raise RuntimeError("O VincePoolManager não foi inicializado corretamente. Verifique os logs de erro.")
# --- Funções Utilitárias ---
def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
"""Busca os arquivos de imagem e vídeo mais recentes no diretório de saída."""
# Esta função não precisa de alterações.
img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
images: List[Path] = []
for pat in img_globs:
images += list(out_dir.rglob(pat))
# Ordena por data de modificação para pegar os mais recentes
images = sorted(images, key=lambda p: p.stat().st_mtime, reverse=True) if images else []
image_paths = [str(p) for p in images[:max_images]]
videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime, reverse=True)
video_path = str(videos[0]) if videos else None
return image_paths, video_path
# --- Funções de Callback da UI (Handlers) ---
def ui_multi_turn(
input_image: Optional[str],
turns_text: Optional[str],
steps_input: int,
cfg_scale_input: float,
aspect_ratio_input: str,
resolution_input: int,
progress=gr.Progress(track_tqdm=True)
):
"""Callback para a aba 'Multi-turn Editing'."""
progress(0.1, desc="Validando entradas...")
if not input_image or not Path(input_image).exists():
gr.Warning("Arquivo de imagem de entrada ausente ou inválido.")
return [], None
if not turns_text or not turns_text.strip():
gr.Warning("As instruções (turns) estão vazias.")
return [], None
turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
try:
progress(0.5, desc="Enviando tarefa para o pool de GPUs. Aguardando a inferência...")
# A chamada para o servidor agora é limpa e delega todo o trabalho pesado.
out_dir = server.generate_multi_turn(
input_image=input_image,
turns=turns,
cfg_scale=float(cfg_scale_input),
aspect_ratio=str(aspect_ratio_input),
resolution=int(resolution_input),
steps=int(steps_input),
)
progress(0.9, desc="Inferência concluída. Buscando resultados...")
except Exception as e:
print(f"[UI][multi_turn] Erro durante a inferência: {e}")
# gr.Error exibe uma notificação de erro clara para o usuário.
gr.Error(f"Erro na Geração: {e}")
return [], None
out_path = Path(out_dir)
if not out_path.exists():
gr.Warning(f"O diretório de saída '{out_path}' não foi encontrado.")
return [], None
imgs, vid = _list_media(out_path)
if not imgs and not vid:
gr.Warning(f"Nenhum arquivo de mídia encontrado no diretório de saída.")
return imgs, vid
def ui_multi_concept(
files: Optional[List[str]],
descs_text: Optional[str],
final_prompt: Optional[str],
steps_input: int,
cfg_scale_input: float,
aspect_ratio_input: str,
resolution_input: int,
progress=gr.Progress(track_tqdm=True)
):
"""Callback para a aba 'Multi-concept Composition'."""
progress(0.1, desc="Validando entradas...")
if not files:
gr.Warning("Nenhum arquivo de imagem de conceito fornecido.")
return [], None
if not descs_text or not descs_text.strip():
gr.Warning("As descrições dos conceitos estão vazias.")
return [], None
if not final_prompt or not final_prompt.strip():
gr.Warning("O prompt final está vazio.")
return [], None
descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
if len(descs) != len(files):
gr.Warning(f"O número de descrições ({len(descs)}) não corresponde ao número de imagens ({len(files)}).")
return [], None
try:
progress(0.5, desc="Enviando tarefa para o pool de GPUs. Aguardando a inferência...")
out_dir = server.generate_multi_concept(
concept_images=files,
concept_prompts=descs,
final_prompt=final_prompt,
cfg_scale=float(cfg_scale_input),
aspect_ratio=str(aspect_ratio_input),
resolution=int(resolution_input),
steps=int(steps_input),
pad_placeholder=False, # Este parâmetro pode ser exposto na UI se necessário
)
progress(0.9, desc="Inferência concluída. Buscando resultados...")
except Exception as e:
print(f"[UI][multi_concept] Erro durante a inferência: {e}")
gr.Error(f"Erro na Geração: {e}")
return [], None
out_path = Path(out_dir)
imgs, vid = _list_media(out_path)
return imgs, vid
# --- Definição da Interface Gráfica com Gradio ---
with gr.Blocks(title="VINCIE (Multi-GPU)") as demo:
gr.Markdown("# 🎨 VINCIE — Edição e Composição (Multi-GPU)")
gr.Markdown("Esta interface utiliza um pool de GPUs para processar as solicitações de forma rápida e paralela.")
# Opções Avançadas são definidas uma vez e reutilizadas nas abas
with gr.Accordion("Opções Avançadas (Comum a todas as abas)", open=False):
steps_input = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50)
cfg_scale_input = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=15.0, step=0.5, value=7.5)
aspect_ratio_input = gr.Dropdown(
label="Proporção da Imagem (Aspect Ratio)",
choices=["keep_ratio", "1:1", "16:9", "9:16", "4:3", "3:4"],
value="keep_ratio",
)
resolution_input = gr.Slider(label="Resolução (lado menor)", minimum=256, maximum=1024, step=64, value=512)
with gr.Tabs():
# Aba 1 — Multi-turn Editing
with gr.TabItem("Edição Sequencial (Multi-turn)"):
with gr.Row():
with gr.Column(scale=1):
img_in_1 = gr.Image(type="filepath", label="Imagem de Entrada")
turns_in_1 = gr.Textbox(lines=8, label="Instruções de Edição (uma por linha)")
run_btn_1 = gr.Button("Gerar Edição", variant="primary")
with gr.Column(scale=2):
gallery_out_1 = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
video_out_1 = gr.Video(label="Vídeo Gerado (se aplicável)")
run_btn_1.click(
fn=ui_multi_turn,
inputs=[img_in_1, turns_in_1, steps_input, cfg_scale_input, aspect_ratio_input, resolution_input],
outputs=[gallery_out_1, video_out_1],
)
# Aba 2 — Multi-concept Composition
with gr.TabItem("Composição de Conceitos (Multi-concept)"):
with gr.Row():
with gr.Column(scale=1):
files_in_2 = gr.File(file_count="multiple", type="filepath", label="Imagens de Conceito")
descs_in_2 = gr.Textbox(lines=8, label="Descrições (uma por linha, na mesma ordem das imagens)")
final_prompt_in_2 = gr.Textbox(lines=2, label="Prompt Final da Composição")
run_btn_2 = gr.Button("Gerar Composição", variant="primary")
with gr.Column(scale=2):
gallery_out_2 = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
video_out_2 = gr.Video(label="Vídeo Gerado (se aplicável)")
run_btn_2.click(
fn=ui_multi_concept,
inputs=[files_in_2, descs_in_2, final_prompt_in_2, steps_input, cfg_scale_input, aspect_ratio_input, resolution_input],
outputs=[gallery_out_2, video_out_2],
)
# --- Ponto de Entrada da Aplicação ---
if __name__ == "__main__":
# Busca as configurações do servidor a partir de variáveis de ambiente, com valores padrão.
server_name = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
server_port = int(os.getenv("GRADIO_SERVER_PORT", "7860"))
print(f"Iniciando a interface Gradio em http://{server_name}:{server_port}")
demo.launch(
server_name=server_name,
server_port=server_port,
# allowed_paths é importante para que o Gradio possa servir os arquivos de resultado
allowed_paths=["/app/outputs", "/app/ckpt"],
show_error=True, # Exibe tracebacks de erro detalhados no navegador (bom para depuração)
) |