# aduc_framework/aduc_sdr.py # # Versão 16.0.0 (Maestro de Produção Integrado) # - Implementa o método `task_produce_movie` para delegar a produção de vídeo # ao especialista `Planner5D`, ativando o pipeline de produção iterativo. # - Atua como a ponte final entre a interface do usuário (UI) e o complexo # fluxo de trabalho de geração de vídeo cena a cena. # - Utiliza o sistema de `yield from` para transmitir de forma eficiente as # atualizações de estado do `Planner5D` de volta para a UI. import logging import os from typing import Generator # Importa os especialistas de alto nível (Engenheiros) from .engineers.composer_2D import composer_2d_singleton as Composer2D from .engineers.planner_5D import planner_5d_singleton as Planner5D # Importa as estruturas de dados (o DNA Digital) from .types import GenerationState, PreProductionParams, ProductionParams, MediaRef from .director import AducDirector logger = logging.getLogger(__name__) class AducSdr: """ O Maestro do framework ADUC-SDR. Orquestra os especialistas (Engineers) e gerencia o fluxo de dados através do Diretor (estado persistente). """ def __init__(self, workspace_root: str): self.workspace_root = workspace_root self.director: AducDirector | None = None self.composer_2d = Composer2D self.planner_5d = Planner5D logger.info("ADUC-SDR Maestro (Arquitetura V2) inicializado e pronto.") def load_project(self, project_name: str): """Carrega um projeto existente ou cria um novo.""" project_path = os.path.join(self.workspace_root, project_name) self.director = AducDirector(project_path=project_path) logger.info(f"Projeto '{project_name}' carregado no Diretor.") def _ensure_project_loaded(self): """Garante que um projeto foi carregado antes de executar tarefas.""" if not self.director: raise RuntimeError("Nenhum projeto foi carregado. Chame `aduc.load_project(project_name)` primeiro.") def get_current_state(self) -> GenerationState: """Retorna o estado completo e atual do projeto.""" self._ensure_project_loaded() return self.director.get_full_state() def process_image_for_story(self, image_path: str, filename: str) -> str: """Processa uma imagem de referência para um formato padrão.""" self._ensure_project_loaded() from PIL import Image size = 480; quality = 40 img = Image.open(image_path).convert("RGB"); img.thumbnail((size, size), Image.Resampling.LANCZOS) background = Image.new('RGB', (size, size), (0, 0, 0)); img_w, img_h = img.size; offset = ((size - img_w) // 2, (size - img_h) // 2) background.paste(img, offset) processed_path = os.path.join(self.director.project_path, filename) background.save(processed_path, 'JPEG', quality=quality) return processed_path def _process_and_yield_updates(self, generator: Generator[GenerationState, None, None]): """ Loop genérico para processar atualizações de um especialista, salvar o estado e repassar para a UI. """ for updated_dna in generator: self.director.load_state_from_dict(updated_dna.model_dump()) if self.director.state.should_checkpoint(): checkpoint_dir = os.path.join(self.director.project_path, "checkpoints") path = self.director.state.create_checkpoint(checkpoint_dir) logger.info(f"Checkpoint do projeto salvo em: {path}") self.director.save_state() yield self.director.get_full_state() def task_run_story_and_keyframes(self, params: PreProductionParams) -> Generator[GenerationState, None, None]: """ Orquestra a pré-produção (Fase 1), delegando ao Composer2D para criar o storyboard. """ self._ensure_project_loaded() logger.info("Maestro: Iniciando Pré-Produção (Storyboard) com o Composer2D...") initial_state = self.director.get_full_state() initial_state.parametros_geracao.pre_producao = params initial_state.midias_referencia = [ MediaRef(id=i, tag=f"", caminho=path) for i, path in enumerate(params.ref_paths) ] initial_state.texto_global_historia = None initial_state.ativos_catalogados = None initial_state.storyboard_producao = [] initial_state.chat_history.append({ "role": "Sistema", "content": f"Iniciando pré-produção. {len(params.ref_paths)} imagens de referência foram tageadas para uso pela IA." }) pre_production_generator = self.composer_2d.compose_storyboard(initial_state) yield from self._process_and_yield_updates(pre_production_generator) logger.info("Maestro: Pré-Produção (Fase 1) concluída.") def task_produce_movie(self, params: ProductionParams) -> Generator[GenerationState, None, None]: """ Orquestra a produção completa do filme (Fase 2), delegando ao Planner5D. """ self._ensure_project_loaded() logger.info("Maestro: Iniciando Produção de Vídeo com o Planner5D...") # 1. Obter o estado atual, que já contém o storyboard da pré-produção. current_state = self.director.get_full_state() # 2. Atualizar o estado com os novos parâmetros de produção vindos da UI. if current_state.parametros_geracao: current_state.parametros_geracao.producao = params current_state.chat_history.append({ "role": "Sistema", "content": f"Parâmetros de produção recebidos. O Diretor de Produção (Planner5D) está assumindo o controle." }) # Salva os parâmetros no dna.json antes de iniciar o processo longo. self.director.save_state() # 3. Chamar o especialista Planner5D com o estado atualizado. # Ele agora irá controlar o loop de Deformes3D e Deformes4D. production_generator = self.planner_5d.produce_movie_by_scene(current_state) # 4. Processar as atualizações, salvar o estado e repassar para a UI em tempo real. yield from self._process_and_yield_updates(production_generator) logger.info("Maestro: Produção de vídeo (Fase 2) concluída.")