Spaces:
Paused
Paused
File size: 11,402 Bytes
fb56537 ddd1a88 fb56537 3cdec95 fb56537 3cdec95 |
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 225 226 227 228 229 230 |
# app.py
#
# Versão 18.1.0 (Interface Alinhada com a Arquitetura V2)
#
# - A chamada de evento da UI agora aciona o novo pipeline do Composer2D.
# - A função de processamento de stream (`process_chat_stream`) foi corrigida
# para ser compatível com o objeto `GenerationState`, resolvendo o AttributeError.
# - A extração de dados para a galeria e o vídeo foi atualizada para ler a
# nova estrutura do DNA (`storyboard_producao`).
import gradio as gr
import yaml
import logging
import os
import sys
import time
from PIL import Image
# --- 1. CONFIGURAÇÃO DE LOGGING E TEMAS ---
logging.basicConfig(level=logging.INFO, format='%(message)s')
logger = logging.getLogger(__name__)
cinematic_theme = gr.themes.Base(primary_hue=gr.themes.colors.purple).set(body_background_fill="#111827", body_text_color="#D1D5DB", button_primary_background_fill="linear-gradient(90deg, #6D28D9, #4F46E5)", block_background_fill="#1F2937", block_border_width="1px", block_border_color="#374151")
# --- 2. INICIALIZAÇÃO CRÍTICA DO FRAMEWORK ---
try:
import aduc_framework
# Importa os tipos necessários para a UI
from aduc_framework.types import PreProductionParams, ProductionParams
with open("config.yaml", 'r') as f:
config = yaml.safe_load(f)
WORKSPACE_DIR = config['application']['workspace_dir']
# Cria a instância global do ADUC
aduc = aduc_framework.create_aduc_instance(workspace_root=WORKSPACE_DIR)
logger.info("Framework ADUC e interface Gradio inicializados com sucesso.")
except Exception as e:
logger.critical(f"ERRO CRÍTICO NA INICIALIZAÇÃO DO ADUC FRAMEWORK: {e}", exc_info=True)
with gr.Blocks(theme=cinematic_theme) as demo_error:
gr.Markdown("# ERRO CRÍTICO NA INICIALIZAÇÃO")
gr.Markdown("Não foi possível iniciar o Aduc Framework. A aplicação não pode continuar. Verifique os logs.")
gr.Textbox(value=str(e), label="Detalhes do Erro", lines=10)
demo_error.launch()
sys.exit(1)
# --- 3. FUNÇÕES WRAPPER E LÓGICA DA UI ---
def chat_beautifier(role):
# (Função auxiliar para deixar os nomes dos especialistas bonitos no chat)
if "Composer2D" in role: return "Arquiteto Narrativo"
if "Neura_Link" in role: return "Interface Neural"
if "Planner5D" in role: return "Diretor de Set"
return "Sistema"
def process_chat_stream(generator, initial_chat_history=[]):
"""
Processa um gerador que produz objetos GenerationState e formata
o histórico de chat e outros dados para a UI.
"""
chatbot_display_history = initial_chat_history.copy()
fully_displayed_message_count = len(initial_chat_history)
for dna_state in generator: # O gerador agora produz objetos 'GenerationState'
# --- CORREÇÃO DE `AttributeError` ---
# Acessamos os atributos do objeto Pydantic diretamente
backend_chat_history = dna_state.chat_history
gallery_images = []
if dna_state.storyboard_producao: # Lendo da nova estrutura V2
for scene in dna_state.storyboard_producao:
gallery_images.extend([kf.caminho_pixel for kf in scene.keyframes])
elif dna_state.scenes: # Fallback para a estrutura legada
for scene in dna_state.scenes:
gallery_images.extend([kf.caminho_pixel for kf in scene.keyframes])
final_video_path = dna_state.caminho_filme_final_bruto or dna_state.caminho_filme_final_masterizado
dna_json = dna_state.model_dump() # Converte o estado atual para JSON para exibição
# ------------------------------------
# Lógica para "digitar" a mensagem
while len(backend_chat_history) > fully_displayed_message_count:
new_message_obj = backend_chat_history[fully_displayed_message_count]
role = chat_beautifier(new_message_obj.get('role', 'Sistema'))
content_to_type = new_message_obj.get('content', '')
is_ai_message = "Sistema" not in role
chatbot_display_history.append({"role": "assistant" if is_ai_message else "user", "content": ""})
full_typed_message = ""
for char in f"**{role}:** {content_to_type}":
full_typed_message += char
chatbot_display_history[-1]["content"] = full_typed_message + "▌"
yield chatbot_display_history, gr.update(value=gallery_images), gr.update(value=dna_json), gr.update(value=final_video_path)
time.sleep(0.005)
chatbot_display_history[-1]["content"] = full_typed_message
fully_displayed_message_count += 1
# Garante que a UI seja atualizada mesmo que não haja novas mensagens de chat
yield chatbot_display_history, gr.update(value=gallery_images), gr.update(value=dna_json), gr.update(value=final_video_path)
def run_story_and_keyframes_wrapper(project_name, prompt, num_scenes, ref_files, duration_per_fragment):
"""
Este wrapper inicia o pipeline de PRÉ-PRODUÇÃO V2.
"""
if not project_name or not project_name.strip():
raise gr.Error("Por favor, forneça um nome para o projeto.")
aduc.load_project(project_name.strip())
if not ref_files: raise gr.Error("Por favor, forneça pelo menos uma imagem de referência.")
ref_paths = [aduc.process_image_for_story(f.name, f"ref_{i}.jpg") for i, f in enumerate(ref_files)]
params = PreProductionParams(
prompt=prompt,
num_scenes=int(num_scenes),
ref_paths=ref_paths,
duration_per_fragment=duration_per_fragment
)
# Chama a função V2 do AducSdr, que espera `params` e retorna um gerador de `GenerationState`
generator = aduc.task_run_story_and_keyframes(params)
final_state_json = {}
for update in process_chat_stream(generator):
yield update
if update[2] is not gr.skip():
final_state_json = update[2] # O terceiro item é o DNA em formato JSON
return final_state_json
def run_production_wrapper(project_name, state_dict, trim, handler, dest, guidance, stg, steps):
"""
Este wrapper inicia o pipeline de PRODUÇÃO V2.
"""
if not project_name or not project_name.strip():
raise gr.Error("O nome do projeto parece ter sido perdido.")
aduc.load_project(project_name.strip())
params = ProductionParams(trim_percent=int(trim), handler_strength=handler, destination_convergence_strength=dest, guidance_scale=guidance, stg_scale=stg, inference_steps=int(steps))
# A nova função de produção do AducSdr pode precisar ser ajustada para receber os params
# ou lê-los diretamente do DNA que já foi atualizado na pré-produção.
# Por enquanto, mantemos a chamada.
prod_generator = aduc.task_produce_movie(params)
# O `state_dict` já é um dicionário, então podemos extrair o histórico de chat dele
initial_chat = state_dict.get("chat_history", [])
final_state_json = {}
for update in process_chat_stream(prod_generator, initial_chat_history=initial_chat):
yield update
if update[2] is not gr.skip():
final_state_json = update[2]
return final_state_json
# --- 4. DEFINIÇÃO DA UI ---
with gr.Blocks(theme=cinematic_theme, css="style.css") as demo:
generation_state_holder = gr.State({})
gr.Markdown("<h1>ADUC-SDR 🎬 - O Diretor de Cinema IA</h1>")
with gr.Accordion("Configurações do Projeto", open=True):
project_name_input = gr.Textbox(label="Nome do Projeto", value="Meu_Filme_01", info="O progresso será salvo em uma pasta com este nome.")
with gr.Row():
with gr.Column(scale=2):
with gr.Accordion("Etapa 1: Pré-Produção (Roteiro e Storyboard)", open=True):
prompt_input = gr.Textbox(label="Ideia Geral do Filme", value="Um robô solitário explora as ruínas de uma cidade coberta pela natureza.")
ref_image_input = gr.File(label="Imagens de Referência", file_count="multiple", file_types=["image"])
with gr.Row():
num_scenes_slider = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Número de Cenas")
duration_per_fragment_slider = gr.Slider(label="Duração de cada Ato (s)", minimum=2.0, maximum=10.0, value=5.0, step=0.1)
start_pre_prod_button = gr.Button("1. Gerar Roteiro e Storyboard", variant="primary")
with gr.Accordion("Etapa 2: Produção do Vídeo", open=False, visible=False) as step2_accordion:
trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5, label="Poda Causal (%)")
handler_strength_slider = gr.Slider(label="Força do Déjà-Vu", minimum=0.0, maximum=1.0, value=0.5, step=0.05)
dest_strength_slider = gr.Slider(label="Força da Âncora Final", minimum=0.0, maximum=1.0, value=0.75, step=0.05)
guidance_scale_slider = gr.Slider(minimum=1.0, maximum=10.0, value=2.0, step=0.1, label="Escala de Orientação")
stg_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.025, step=0.005, label="Escala STG")
inference_steps_slider = gr.Slider(minimum=10, maximum=50, value=20, step=1, label="Passos de Inferência")
produce_original_button = gr.Button("2. Produzir Vídeo", variant="primary", interactive=False)
with gr.Column(scale=3):
final_video_output = gr.Video(label="Último Clipe Gerado / Filme Final", interactive=False)
chat_history_chatbot = gr.Chatbot(label="Diário da Produção", height=600, type='messages')
keyframe_gallery = gr.Gallery(label="Keyframes Gerados", object_fit="contain", height="auto")
with gr.Accordion("🧬 DNA Digital (Estado do Projeto)", open=False):
dna_display = gr.JSON()
# --- 5. CONEXÕES DE EVENTOS ---
start_pre_prod_button.click(
fn=lambda: gr.update(interactive=False),
outputs=[start_pre_prod_button]
).then(
fn=run_story_and_keyframes_wrapper,
inputs=[project_name_input, prompt_input, num_scenes_slider, ref_image_input, duration_per_fragment_slider],
outputs=[chat_history_chatbot, keyframe_gallery, dna_display, final_video_output]
).then(
fn=lambda data: (data, gr.update(visible=True, open=True), gr.update(interactive=True)),
inputs=dna_display,
outputs=[generation_state_holder, step2_accordion, produce_original_button]
)
produce_original_button.click(
fn=lambda: gr.update(interactive=False),
outputs=[produce_original_button]
).then(
fn=run_production_wrapper,
inputs=[
project_name_input,
generation_state_holder,
trim_percent_slider,
handler_strength_slider,
dest_strength_slider,
guidance_scale_slider,
stg_scale_slider,
inference_steps_slider
],
outputs=[chat_history_chatbot, keyframe_gallery, dna_display, final_video_output]
)
if __name__ == "__main__":
os.makedirs(WORKSPACE_DIR, exist_ok=True)
logger.info("Aplicação Gradio pronta. Lançando interface...")
demo.launch(
server_name="0.0.0.0",
server_port=int(os.getenv("PORT", "7860")),
) |