aducsdr commited on
Commit
714b61d
·
verified ·
1 Parent(s): c99f4f7

Update aduc_framework/orchestrator.py

Browse files
Files changed (1) hide show
  1. aduc_framework/orchestrator.py +61 -34
aduc_framework/orchestrator.py CHANGED
@@ -2,12 +2,14 @@
2
  #
3
  # Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
4
  #
5
- # Versão 3.2.0 (Com Orquestração de Pós-Produção)
6
  #
7
- # Esta versão representa a camada de orquestração do Aduc Framework.
8
- # Ela é agnóstica a qualquer interface (UI ou API) e opera com
9
- # tipos de dados bem definidos (Pydantic) e um estado de geração central.
10
- # Agora, é responsável por inicializar os singletons e orquestrar todas as etapas.
 
 
11
 
12
  import logging
13
  from typing import List, Dict, Any, Tuple, Callable, Optional, Generator
@@ -20,15 +22,20 @@ import time
20
  import gc
21
  import torch
22
 
23
- # Importa componentes internos do framework
24
  from .director import AducDirector
25
  from .types import GenerationState, PreProductionParams, ProductionParams
 
 
26
  from .engineers import deformes2d_thinker_singleton, deformes3d_engine_singleton, Deformes4DEngine
27
- # Importa managers diretamente para as tarefas de pós-produção
 
28
  from .managers.latent_enhancer_manager import latent_enhancer_specialist_singleton
29
  from .managers.seedvr_manager import seedvr_manager_singleton
30
  from .managers.mmaudio_manager import mmaudio_manager_singleton
31
  from .managers.vae_manager import vae_manager_singleton
 
 
32
  from .tools.video_encode_tool import video_encode_tool_singleton
33
 
34
  logger = logging.getLogger(__name__)
@@ -39,7 +46,7 @@ class AducOrchestrator:
39
  """
40
  Implementa o Maestro (Γ), a camada de orquestração central do Aduc Framework.
41
  Ele recebe solicitações, atualiza o estado de geração, delega tarefas para os
42
- engenheiros especialistas e retorna o estado atualizado.
43
  """
44
  def __init__(self, workspace_dir: str):
45
  self.director = AducDirector(workspace_dir)
@@ -48,12 +55,14 @@ class AducOrchestrator:
48
  self.painter = deformes3d_engine_singleton
49
  self.painter.initialize(workspace_dir)
50
  self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
51
- logger.info("ADUC Maestro (Framework Core) está no pódio. Engenheiros especialistas prontos.")
52
 
53
  def get_current_state(self) -> GenerationState:
 
54
  return self.director.get_full_state()
55
 
56
  def process_image_for_story(self, image_path: str, size: int, filename: str) -> str:
 
57
  img = Image.open(image_path).convert("RGB")
58
  img_square = ImageOps.fit(img, (size, size), Image.Resampling.LANCZOS)
59
  processed_path = os.path.join(self.director.workspace_dir, filename)
@@ -61,35 +70,48 @@ class AducOrchestrator:
61
  logger.info(f"Imagem de referência processada e salva em: {processed_path}")
62
  return processed_path
63
 
 
64
  def task_pre_production(self, params: PreProductionParams, progress_callback: ProgressCallback = None) -> Tuple[List[str], List[str], GenerationState]:
 
65
  logger.info("Maestro: Iniciando tarefa de Pré-Produção.")
66
  self.director.update_parameters("pre_producao", params)
 
67
  if progress_callback: progress_callback(0.1, "Gerando storyboard...")
68
  storyboard_list = deformes2d_thinker_singleton.generate_storyboard(prompt=params.prompt, num_keyframes=params.num_keyframes, ref_image_paths=params.ref_paths)
69
  self.director.update_pre_production_state(params.prompt, params.ref_paths, storyboard_list)
 
70
  if progress_callback: progress_callback(0.2, "Iniciando geração de keyframes...")
 
71
  keyframes_detailed_data = self.painter.generate_keyframes_from_storyboard(generation_state=self.director.get_full_state_as_dict(), progress_callback=progress_callback)
72
  self.director.update_keyframes_state(keyframes_detailed_data)
 
73
  final_keyframe_paths = [kf["caminho_pixel"] for kf in keyframes_detailed_data]
74
  final_state = self.director.get_full_state()
75
  logger.info("Maestro: Tarefa de Pré-Produção concluída.")
76
  return storyboard_list, final_keyframe_paths, final_state
77
 
 
78
  def task_produce_original_movie(self, params: ProductionParams, progress_callback: ProgressCallback = None) -> Tuple[str, List[str], GenerationState]:
 
79
  logger.info("Maestro: Iniciando tarefa de Produção do Filme Original.")
80
  self.director.update_parameters("producao", params)
 
 
81
  result_data = self.editor.generate_original_movie(full_generation_state=self.director.get_full_state_as_dict(), progress_callback=progress_callback)
82
  self.director.update_video_state(result_data["video_data"])
 
83
  final_video_path = result_data["final_path"]
84
  latent_paths = result_data["latent_paths"]
85
  final_state = self.director.get_full_state()
86
  logger.info("Maestro: Tarefa de Produção do Filme Original concluída.")
87
  return final_video_path, latent_paths, final_state
88
 
 
 
89
  def task_run_latent_upscaler(self, latent_paths: List[str], chunk_size: int, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
90
- if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado corretamente.")
91
- if not latent_paths:
92
- raise ValueError("Não é possível fazer o upscale: nenhum caminho de latente foi fornecido.")
93
 
94
  logger.info("--- ORQUESTRADOR: Tarefa de Upscaling de Latentes ---")
95
  run_timestamp = int(time.time())
@@ -97,34 +119,26 @@ class AducOrchestrator:
97
  os.makedirs(temp_dir, exist_ok=True)
98
 
99
  final_upscaled_clip_paths = []
100
- num_chunks = -(-len(latent_paths) // chunk_size) if chunk_size > 0 else 0
101
 
102
  for i in range(num_chunks):
103
- chunk_start_index = i * chunk_size
104
- chunk_end_index = chunk_start_index + chunk_size
105
- chunk_paths = latent_paths[chunk_start_index:chunk_end_index]
106
-
107
  if progress_callback: progress_callback(i / num_chunks, f"Upscalando & Decodificando Lote {i+1}/{num_chunks}")
108
 
109
  tensors_in_chunk = [torch.load(p, map_location=self.device) for p in chunk_paths]
110
  sub_group_latent = torch.cat(tensors_in_chunk, dim=2)
111
- del tensors_in_chunk; gc.collect(); torch.cuda.empty_cache()
112
 
113
  upscaled_latent_chunk = latent_enhancer_specialist_singleton.upscale(sub_group_latent)
114
- del sub_group_latent; gc.collect(); torch.cuda.empty_cache()
115
-
116
  pixel_tensor = vae_manager_singleton.decode(upscaled_latent_chunk)
117
- del upscaled_latent_chunk; gc.collect(); torch.cuda.empty_cache()
118
 
119
- base_name = f"upscaled_clip_{i:04d}_{run_timestamp}"
120
- current_clip_path = os.path.join(temp_dir, f"{base_name}.mp4")
121
  self.editor.save_video_from_tensor(pixel_tensor, current_clip_path, fps=24)
122
  final_upscaled_clip_paths.append(current_clip_path)
123
- del pixel_tensor; gc.collect(); torch.cuda.empty_cache()
124
 
125
- yield {"progress": (i + 1) / num_chunks, "desc": f"Lote {i+1}/{num_chunks} completo."}
 
 
126
 
127
- if progress_callback: progress_callback(0.98, "Montando vídeo com upscale...")
128
  final_video_path = os.path.join(self.director.workspace_dir, f"upscaled_movie_{run_timestamp}.mp4")
129
  video_encode_tool_singleton.concatenate_videos(final_upscaled_clip_paths, final_video_path, self.director.workspace_dir)
130
 
@@ -133,22 +147,29 @@ class AducOrchestrator:
133
  yield {"final_path": final_video_path}
134
 
135
  def task_run_hd_mastering(self, source_video_path: str, model_version: str, steps: int, prompt: str, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
136
- if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado corretamente.")
 
137
  logger.info(f"--- ORQUESTRADOR: Tarefa de Masterização HD com SeedVR {model_version} ---")
138
 
 
 
 
139
  run_timestamp = int(time.time())
140
  output_path = os.path.join(self.director.workspace_dir, f"hd_mastered_movie_{model_version}_{run_timestamp}.mp4")
141
 
142
  final_path = seedvr_manager_singleton.process_video(
143
  input_video_path=source_video_path,
144
  output_video_path=output_path,
145
- prompt=prompt
 
 
146
  )
147
  logger.info(f"Masterização HD completa! Vídeo final em: {final_path}")
148
  yield {"final_path": final_path}
149
 
150
  def task_run_audio_generation(self, source_video_path: str, audio_prompt: str, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
151
- if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado corretamente.")
 
152
  logger.info(f"--- ORQUESTRADOR: Tarefa de Geração de Áudio ---")
153
 
154
  if progress_callback: progress_callback(0.1, "Preparando para geração de áudio...")
@@ -157,14 +178,20 @@ class AducOrchestrator:
157
  source_name = Path(source_video_path).stem
158
  output_path = os.path.join(self.director.workspace_dir, f"{source_name}_with_audio_{run_timestamp}.mp4")
159
 
160
- result = subprocess.run(
161
- ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", source_video_path],
162
- capture_output=True, text=True, check=True
163
- )
164
- duration = float(result.stdout.strip())
 
 
 
 
 
165
 
166
  if progress_callback: progress_callback(0.5, "Gerando trilha de áudio...")
167
 
 
168
  final_path = mmaudio_manager_singleton.generate_audio_for_video(
169
  video_path=source_video_path,
170
  prompt=audio_prompt,
 
2
  #
3
  # Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
4
  #
5
+ # Versão 4.0.0 (Multi-Pool Orchestration)
6
  #
7
+ # Esta versão do orquestrador opera com uma arquitetura de múltiplos pools de
8
+ # especialistas. A responsabilidade de gerenciar a VRAM e o ciclo de vida dos
9
+ # modelos foi delegada aos seus respectivos managers (LtxPoolManager,
10
+ # SeedVrPoolManager, MMAudioPoolManager), cada um operando em um conjunto
11
+ # dedicado de GPUs. O orquestrador agora foca exclusivamente na lógica de
12
+ # alto nível do fluxo de trabalho.
13
 
14
  import logging
15
  from typing import List, Dict, Any, Tuple, Callable, Optional, Generator
 
22
  import gc
23
  import torch
24
 
25
+ # Componentes internos do framework
26
  from .director import AducDirector
27
  from .types import GenerationState, PreProductionParams, ProductionParams
28
+
29
+ # Engenheiros de alto nível que definem a lógica do fluxo
30
  from .engineers import deformes2d_thinker_singleton, deformes3d_engine_singleton, Deformes4DEngine
31
+
32
+ # Managers (Pools) de especialistas que executam as tarefas em hardware dedicado
33
  from .managers.latent_enhancer_manager import latent_enhancer_specialist_singleton
34
  from .managers.seedvr_manager import seedvr_manager_singleton
35
  from .managers.mmaudio_manager import mmaudio_manager_singleton
36
  from .managers.vae_manager import vae_manager_singleton
37
+
38
+ # Ferramentas de utilidade
39
  from .tools.video_encode_tool import video_encode_tool_singleton
40
 
41
  logger = logging.getLogger(__name__)
 
46
  """
47
  Implementa o Maestro (Γ), a camada de orquestração central do Aduc Framework.
48
  Ele recebe solicitações, atualiza o estado de geração, delega tarefas para os
49
+ engenheiros e seus pools de especialistas, e retorna o estado atualizado.
50
  """
51
  def __init__(self, workspace_dir: str):
52
  self.director = AducDirector(workspace_dir)
 
55
  self.painter = deformes3d_engine_singleton
56
  self.painter.initialize(workspace_dir)
57
  self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
58
+ logger.info("ADUC Maestro (Framework Core) pronto para reger a orquestra de especialistas.")
59
 
60
  def get_current_state(self) -> GenerationState:
61
+ """Retorna o estado de geração atual."""
62
  return self.director.get_full_state()
63
 
64
  def process_image_for_story(self, image_path: str, size: int, filename: str) -> str:
65
+ """Processa uma imagem de referência para o formato quadrado padrão."""
66
  img = Image.open(image_path).convert("RGB")
67
  img_square = ImageOps.fit(img, (size, size), Image.Resampling.LANCZOS)
68
  processed_path = os.path.join(self.director.workspace_dir, filename)
 
70
  logger.info(f"Imagem de referência processada e salva em: {processed_path}")
71
  return processed_path
72
 
73
+ # --- ETAPA 1: PRÉ-PRODUÇÃO ---
74
  def task_pre_production(self, params: PreProductionParams, progress_callback: ProgressCallback = None) -> Tuple[List[str], List[str], GenerationState]:
75
+ """Orquestra a criação do storyboard e dos keyframes visuais."""
76
  logger.info("Maestro: Iniciando tarefa de Pré-Produção.")
77
  self.director.update_parameters("pre_producao", params)
78
+
79
  if progress_callback: progress_callback(0.1, "Gerando storyboard...")
80
  storyboard_list = deformes2d_thinker_singleton.generate_storyboard(prompt=params.prompt, num_keyframes=params.num_keyframes, ref_image_paths=params.ref_paths)
81
  self.director.update_pre_production_state(params.prompt, params.ref_paths, storyboard_list)
82
+
83
  if progress_callback: progress_callback(0.2, "Iniciando geração de keyframes...")
84
+ # O 'painter' (Deformes3D) internamente usará o LTX Pool Manager para gerar as imagens
85
  keyframes_detailed_data = self.painter.generate_keyframes_from_storyboard(generation_state=self.director.get_full_state_as_dict(), progress_callback=progress_callback)
86
  self.director.update_keyframes_state(keyframes_detailed_data)
87
+
88
  final_keyframe_paths = [kf["caminho_pixel"] for kf in keyframes_detailed_data]
89
  final_state = self.director.get_full_state()
90
  logger.info("Maestro: Tarefa de Pré-Produção concluída.")
91
  return storyboard_list, final_keyframe_paths, final_state
92
 
93
+ # --- ETAPA 2: PRODUÇÃO ---
94
  def task_produce_original_movie(self, params: ProductionParams, progress_callback: ProgressCallback = None) -> Tuple[str, List[str], GenerationState]:
95
+ """Orquestra a geração do vídeo principal a partir dos keyframes."""
96
  logger.info("Maestro: Iniciando tarefa de Produção do Filme Original.")
97
  self.director.update_parameters("producao", params)
98
+
99
+ # O 'editor' (Deformes4D) usa intensivamente o LTX Pool Manager
100
  result_data = self.editor.generate_original_movie(full_generation_state=self.director.get_full_state_as_dict(), progress_callback=progress_callback)
101
  self.director.update_video_state(result_data["video_data"])
102
+
103
  final_video_path = result_data["final_path"]
104
  latent_paths = result_data["latent_paths"]
105
  final_state = self.director.get_full_state()
106
  logger.info("Maestro: Tarefa de Produção do Filme Original concluída.")
107
  return final_video_path, latent_paths, final_state
108
 
109
+ # --- ETAPA 3: PÓS-PRODUÇÃO (Cadeia de Efeitos) ---
110
+
111
  def task_run_latent_upscaler(self, latent_paths: List[str], chunk_size: int, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
112
+ """Aplica upscale 2x nos latentes e os decodifica para um novo vídeo."""
113
+ if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado.")
114
+ if not latent_paths: raise ValueError("Nenhum caminho de latente fornecido para o upscale.")
115
 
116
  logger.info("--- ORQUESTRADOR: Tarefa de Upscaling de Latentes ---")
117
  run_timestamp = int(time.time())
 
119
  os.makedirs(temp_dir, exist_ok=True)
120
 
121
  final_upscaled_clip_paths = []
122
+ num_chunks = -(-len(latent_paths) // chunk_size)
123
 
124
  for i in range(num_chunks):
125
+ chunk_paths = latent_paths[i * chunk_size:(i + 1) * chunk_size]
 
 
 
126
  if progress_callback: progress_callback(i / num_chunks, f"Upscalando & Decodificando Lote {i+1}/{num_chunks}")
127
 
128
  tensors_in_chunk = [torch.load(p, map_location=self.device) for p in chunk_paths]
129
  sub_group_latent = torch.cat(tensors_in_chunk, dim=2)
 
130
 
131
  upscaled_latent_chunk = latent_enhancer_specialist_singleton.upscale(sub_group_latent)
 
 
132
  pixel_tensor = vae_manager_singleton.decode(upscaled_latent_chunk)
 
133
 
134
+ current_clip_path = os.path.join(temp_dir, f"upscaled_clip_{i:04d}.mp4")
 
135
  self.editor.save_video_from_tensor(pixel_tensor, current_clip_path, fps=24)
136
  final_upscaled_clip_paths.append(current_clip_path)
 
137
 
138
+ del tensors_in_chunk, sub_group_latent, upscaled_latent_chunk, pixel_tensor
139
+ gc.collect(); torch.cuda.empty_cache()
140
+ yield {"progress": (i + 1) / num_chunks}
141
 
 
142
  final_video_path = os.path.join(self.director.workspace_dir, f"upscaled_movie_{run_timestamp}.mp4")
143
  video_encode_tool_singleton.concatenate_videos(final_upscaled_clip_paths, final_video_path, self.director.workspace_dir)
144
 
 
147
  yield {"final_path": final_video_path}
148
 
149
  def task_run_hd_mastering(self, source_video_path: str, model_version: str, steps: int, prompt: str, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
150
+ """Aplica masterização em HD usando o pool de GPUs do SeedVR."""
151
+ if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado.")
152
  logger.info(f"--- ORQUESTRADOR: Tarefa de Masterização HD com SeedVR {model_version} ---")
153
 
154
+ # Com pools dedicados, não há mais necessidade de descarregar modelos LTX.
155
+ # A chamada �� direta e o SeedVrPoolManager gerencia seus próprios recursos.
156
+
157
  run_timestamp = int(time.time())
158
  output_path = os.path.join(self.director.workspace_dir, f"hd_mastered_movie_{model_version}_{run_timestamp}.mp4")
159
 
160
  final_path = seedvr_manager_singleton.process_video(
161
  input_video_path=source_video_path,
162
  output_video_path=output_path,
163
+ prompt=prompt,
164
+ model_version=model_version,
165
+ steps=steps
166
  )
167
  logger.info(f"Masterização HD completa! Vídeo final em: {final_path}")
168
  yield {"final_path": final_path}
169
 
170
  def task_run_audio_generation(self, source_video_path: str, audio_prompt: str, progress_callback: ProgressCallback = None) -> Generator[Dict[str, Any], None, None]:
171
+ """Gera e adiciona áudio ao vídeo usando o pool de GPUs do MMAudio."""
172
+ if not self.director.workspace_dir: raise RuntimeError("Orchestrator não inicializado.")
173
  logger.info(f"--- ORQUESTRADOR: Tarefa de Geração de Áudio ---")
174
 
175
  if progress_callback: progress_callback(0.1, "Preparando para geração de áudio...")
 
178
  source_name = Path(source_video_path).stem
179
  output_path = os.path.join(self.director.workspace_dir, f"{source_name}_with_audio_{run_timestamp}.mp4")
180
 
181
+ try:
182
+ result = subprocess.run(
183
+ ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", source_video_path],
184
+ capture_output=True, text=True, check=True
185
+ )
186
+ duration = float(result.stdout.strip())
187
+ except Exception as e:
188
+ logger.error(f"Não foi possível obter a duração do vídeo '{source_video_path}': {e}", exc_info=True)
189
+ yield {"error": "Falha ao obter duração do vídeo."}
190
+ return
191
 
192
  if progress_callback: progress_callback(0.5, "Gerando trilha de áudio...")
193
 
194
+ # A chamada é direta. O MMAudioPoolManager gerencia suas GPUs dedicadas.
195
  final_path = mmaudio_manager_singleton.generate_audio_for_video(
196
  video_path=source_video_path,
197
  prompt=audio_prompt,