Spaces:
Runtime error
Runtime error
Update aduc_framework/engineers/deformes2D_thinker.py
Browse files
aduc_framework/engineers/deformes2D_thinker.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
| 2 |
#
|
| 3 |
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 4 |
#
|
| 5 |
-
# Versão
|
| 6 |
#
|
| 7 |
-
# Esta classe é o cérebro criativo do framework
|
| 8 |
-
#
|
| 9 |
-
#
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
import logging
|
| 12 |
from pathlib import Path
|
|
@@ -20,9 +23,10 @@ logger = logging.getLogger(__name__)
|
|
| 20 |
|
| 21 |
class Deformes2DThinker:
|
| 22 |
"""
|
| 23 |
-
O especialista cognitivo que atua como
|
| 24 |
-
|
| 25 |
"""
|
|
|
|
| 26 |
def _read_prompt_template(self, filename: str) -> str:
|
| 27 |
"""Lê um arquivo de template de prompt do diretório 'prompts'."""
|
| 28 |
try:
|
|
@@ -32,19 +36,18 @@ class Deformes2DThinker:
|
|
| 32 |
except FileNotFoundError:
|
| 33 |
raise gr.Error(f"Arquivo de template de prompt não encontrado: {filename}")
|
| 34 |
|
|
|
|
| 35 |
def get_directorial_decision(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
| 36 |
"""
|
| 37 |
-
Função principal do Diretor. Analisa o estado
|
| 38 |
-
|
| 39 |
"""
|
| 40 |
try:
|
| 41 |
template = self._read_prompt_template("autonomous_director_prompt.txt")
|
| 42 |
|
| 43 |
-
# Serializa o contexto complexo em strings legíveis para o LLM
|
| 44 |
keyframes_str = "\n".join([f"- ID {kf['id']}: {kf['prompt_keyframe']}" for kf in context.get('keyframes_gerados', [])])
|
| 45 |
script_str = "\n".join([f"- Ato {i+1}: {ato}" for i, ato in enumerate(context.get('roteiro_completo', []))])
|
| 46 |
|
| 47 |
-
# Preenche o template com todas as informações contextuais
|
| 48 |
formatted_prompt = template.format(
|
| 49 |
prompt_geral=context.get('prompt_geral', ''),
|
| 50 |
midias_usuario=", ".join(context.get('midias_usuario', [])),
|
|
@@ -53,7 +56,6 @@ class Deformes2DThinker:
|
|
| 53 |
keyframes_gerados=keyframes_str if keyframes_str else "Nenhuma cena filmada ainda."
|
| 54 |
)
|
| 55 |
|
| 56 |
-
# Prepara o payload para o Gemini. A imagem do último keyframe é crucial para a avaliação.
|
| 57 |
prompt_parts = [formatted_prompt]
|
| 58 |
keyframes_gerados = context.get('keyframes_gerados', [])
|
| 59 |
if keyframes_gerados:
|
|
@@ -62,16 +64,13 @@ class Deformes2DThinker:
|
|
| 62 |
try:
|
| 63 |
prompt_parts.append(Image.open(last_keyframe_path))
|
| 64 |
except FileNotFoundError:
|
| 65 |
-
logger.warning(f"Não foi possível encontrar a imagem do último keyframe
|
| 66 |
|
| 67 |
-
# Chama o Gemini e espera uma decisão estruturada em JSON
|
| 68 |
decision_json = gemini_manager_singleton.get_json_object(prompt_parts)
|
| 69 |
return decision_json
|
| 70 |
|
| 71 |
except Exception as e:
|
| 72 |
-
logger.error(f"O Diretor Autônomo (Gemini) falhou: {e}. Acionando fallback
|
| 73 |
-
# Fallback robusto: se o Gemini falhar, tentamos avançar com uma decisão genérica
|
| 74 |
-
# para não interromper a produção.
|
| 75 |
return self._get_fallback_decision(context)
|
| 76 |
|
| 77 |
def _get_fallback_decision(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -89,6 +88,35 @@ class Deformes2DThinker:
|
|
| 89 |
"is_cut": False
|
| 90 |
}
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
def generate_storyboard(self, prompt: str, num_keyframes: int, ref_image_paths: List[str]) -> List[str]:
|
| 93 |
"""
|
| 94 |
Atua como Roteirista para criar o roteiro inicial (lista de atos)
|
|
@@ -105,8 +133,9 @@ class Deformes2DThinker:
|
|
| 105 |
storyboard = storyboard_data.get("scene_storyboard", [])
|
| 106 |
if not storyboard or len(storyboard) != num_keyframes:
|
| 107 |
logger.warning(f"Número de cenas gerado diferente do solicitado. Pedido: {num_keyframes}, Gerado: {len(storyboard)}")
|
| 108 |
-
#
|
| 109 |
-
|
|
|
|
| 110 |
|
| 111 |
return storyboard
|
| 112 |
except Exception as e:
|
|
|
|
| 2 |
#
|
| 3 |
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 4 |
#
|
| 5 |
+
# Versão 4.0.0 (Dual-Role Autonomous Director & Cinematographer)
|
| 6 |
#
|
| 7 |
+
# Esta classe é o cérebro criativo do framework. Ela utiliza um LLM para
|
| 8 |
+
# desempenhar múltiplos papéis:
|
| 9 |
+
# 1. Roteirista: Cria a lista inicial de atos.
|
| 10 |
+
# 2. Diretor Autônomo: Gerencia a produção de keyframes, com poder de
|
| 11 |
+
# avaliar, corrigir e improvisar o roteiro.
|
| 12 |
+
# 3. Cineasta: Cria os prompts de movimento que conectam os keyframes.
|
| 13 |
|
| 14 |
import logging
|
| 15 |
from pathlib import Path
|
|
|
|
| 23 |
|
| 24 |
class Deformes2DThinker:
|
| 25 |
"""
|
| 26 |
+
O especialista cognitivo que atua como a inteligência criativa central,
|
| 27 |
+
desempenhando os papéis de Roteirista, Diretor Autônomo e Cineasta.
|
| 28 |
"""
|
| 29 |
+
|
| 30 |
def _read_prompt_template(self, filename: str) -> str:
|
| 31 |
"""Lê um arquivo de template de prompt do diretório 'prompts'."""
|
| 32 |
try:
|
|
|
|
| 36 |
except FileNotFoundError:
|
| 37 |
raise gr.Error(f"Arquivo de template de prompt não encontrado: {filename}")
|
| 38 |
|
| 39 |
+
# --- PAPEL 1: DIRETOR AUTÔNOMO (Para Deformes3D) ---
|
| 40 |
def get_directorial_decision(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
| 41 |
"""
|
| 42 |
+
Função principal do Diretor. Analisa o estado da produção e retorna a
|
| 43 |
+
próxima ação (avançar, corrigir, improvisar) para a criação de KEYFRAMES.
|
| 44 |
"""
|
| 45 |
try:
|
| 46 |
template = self._read_prompt_template("autonomous_director_prompt.txt")
|
| 47 |
|
|
|
|
| 48 |
keyframes_str = "\n".join([f"- ID {kf['id']}: {kf['prompt_keyframe']}" for kf in context.get('keyframes_gerados', [])])
|
| 49 |
script_str = "\n".join([f"- Ato {i+1}: {ato}" for i, ato in enumerate(context.get('roteiro_completo', []))])
|
| 50 |
|
|
|
|
| 51 |
formatted_prompt = template.format(
|
| 52 |
prompt_geral=context.get('prompt_geral', ''),
|
| 53 |
midias_usuario=", ".join(context.get('midias_usuario', [])),
|
|
|
|
| 56 |
keyframes_gerados=keyframes_str if keyframes_str else "Nenhuma cena filmada ainda."
|
| 57 |
)
|
| 58 |
|
|
|
|
| 59 |
prompt_parts = [formatted_prompt]
|
| 60 |
keyframes_gerados = context.get('keyframes_gerados', [])
|
| 61 |
if keyframes_gerados:
|
|
|
|
| 64 |
try:
|
| 65 |
prompt_parts.append(Image.open(last_keyframe_path))
|
| 66 |
except FileNotFoundError:
|
| 67 |
+
logger.warning(f"Não foi possível encontrar a imagem do último keyframe: {last_keyframe_path}")
|
| 68 |
|
|
|
|
| 69 |
decision_json = gemini_manager_singleton.get_json_object(prompt_parts)
|
| 70 |
return decision_json
|
| 71 |
|
| 72 |
except Exception as e:
|
| 73 |
+
logger.error(f"O Diretor Autônomo (Gemini) falhou: {e}. Acionando fallback.", exc_info=True)
|
|
|
|
|
|
|
| 74 |
return self._get_fallback_decision(context)
|
| 75 |
|
| 76 |
def _get_fallback_decision(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
|
| 88 |
"is_cut": False
|
| 89 |
}
|
| 90 |
|
| 91 |
+
# --- PAPEL 2: CINEASTA (Para Deformes4D) ---
|
| 92 |
+
def get_motion_decision(self, start_kf: Dict, end_kf: Dict, motion_history: str) -> str:
|
| 93 |
+
"""
|
| 94 |
+
Atua como Cineasta para decidir o melhor `motion_prompt` que conecta
|
| 95 |
+
dois keyframes existentes, criando o MOVIMENTO.
|
| 96 |
+
"""
|
| 97 |
+
try:
|
| 98 |
+
template = self._read_prompt_template("motion_director_prompt.txt")
|
| 99 |
+
|
| 100 |
+
formatted_prompt = template.format(
|
| 101 |
+
cena_atual_desc=start_kf.get('prompt_keyframe', 'início da cena'),
|
| 102 |
+
cena_futura_desc=end_kf.get('prompt_keyframe', 'fim da cena'),
|
| 103 |
+
historico_movimento=motion_history
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
prompt_parts = [
|
| 107 |
+
formatted_prompt,
|
| 108 |
+
"IMAGEM ATUAL:", Image.open(start_kf['caminho_pixel']),
|
| 109 |
+
"IMAGEM FUTURA:", Image.open(end_kf['caminho_pixel'])
|
| 110 |
+
]
|
| 111 |
+
|
| 112 |
+
motion_prompt = gemini_manager_singleton.get_raw_text(prompt_parts)
|
| 113 |
+
return motion_prompt.strip().replace("`", "").replace("\"", "")
|
| 114 |
+
|
| 115 |
+
except Exception as e:
|
| 116 |
+
logger.error(f"O Cineasta (Gemini) falhou ao criar motion_prompt: {e}. Usando fallback.", exc_info=True)
|
| 117 |
+
return f"Uma transição cinematográfica suave de '{start_kf.get('prompt_keyframe', 'cena anterior')}' para '{end_kf.get('prompt_keyframe', 'próxima cena')}'."
|
| 118 |
+
|
| 119 |
+
# --- PAPEL 3: ROTEIRISTA (Para Orquestrador) ---
|
| 120 |
def generate_storyboard(self, prompt: str, num_keyframes: int, ref_image_paths: List[str]) -> List[str]:
|
| 121 |
"""
|
| 122 |
Atua como Roteirista para criar o roteiro inicial (lista de atos)
|
|
|
|
| 133 |
storyboard = storyboard_data.get("scene_storyboard", [])
|
| 134 |
if not storyboard or len(storyboard) != num_keyframes:
|
| 135 |
logger.warning(f"Número de cenas gerado diferente do solicitado. Pedido: {num_keyframes}, Gerado: {len(storyboard)}")
|
| 136 |
+
# Garante que a lista tenha o tamanho exato solicitado
|
| 137 |
+
default_scene = storyboard[-1] if storyboard else "Cena de continuação."
|
| 138 |
+
storyboard = (storyboard + [default_scene] * num_keyframes)[:num_keyframes]
|
| 139 |
|
| 140 |
return storyboard
|
| 141 |
except Exception as e:
|