File size: 7,916 Bytes
fb56537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# aduc_framework/engineers/composer_2D.py

# Versão 3.1.0 (Diálogo de Revisão Estruturado)
#
# - Implementa o fluxo de revisão em duas etapas (4.5 e 5) para
#   maior qualidade e consistência do storyboard.
# - Etapa 4.5: A IA recebe o storyboard completo para uma análise global.
# - Etapa 5: A IA itera cena a cena para fazer um polimento fino,
#   usando o contexto global absorvido na etapa anterior.
# - Utiliza os métodos de serialização do Pydantic (`model_dump(mode='json')` e
#   `model_dump_json()`) para evitar TypeErrors com objetos datetime.

import logging
import json
from typing import Generator, Dict, Any, List

# --- Interface neural e tipos ---
from .neura_link import neura_link_singleton as NeuraLink
from ..types import GenerationState, CatalogoDeAtivos, Scene, Ato, AtivoCatalogado

logger = logging.getLogger(__name__)

class Composer2D:
    """
    Orquestra um pipeline conversacional para gerar e refinar um storyboard,
    usando um sistema de identificação de ativos universal e um diálogo de
    revisão em várias etapas.
    """

    def compose_storyboard(self, dna: GenerationState) -> Generator[GenerationState, None, None]:
        """
        Executa o pipeline completo de pré-produção, incluindo a fase de revisão.
        """
        params = getattr(dna.parametros_geracao, "pre_producao", None)
        if not params:
            raise ValueError("Parâmetros de pré-produção ausentes no DNA (parametros_geracao.pre_producao requerido).")
        
        num_scenes = params.num_scenes
        duration_per_fragment = params.duration_per_fragment
        global_prompt = params.prompt

        refs = list(getattr(dna, "midias_referencia", []))
        image_paths = [r.caminho for r in refs if r.caminho]
        image_map_para_llm = [{"tag_referencia": r.tag} for r in refs]

        NeuraLink.reset_memory()
        dna.chat_history.append({"role": "Composer2D", "content": "Memória da IA reiniciada. Iniciando roteiro..."})
        yield dna

        # --- ETAPAS 1-4: GERAÇÃO DO RASCUNHO INICIAL ---

        # ETAPA 1: NARRATIVA
        narrativa = NeuraLink.execute_task(
            task_id="TASK_01_CREATE_CINEMATIC_NARRATIVE",
            template_data={"global_prompt": global_prompt, "num_scenes": num_scenes},
            image_paths=image_paths or None, expected_format="text", use_memory=True,
        )
        dna.texto_global_historia = (narrativa or "").strip()
        dna.chat_history.append({"role": "Composer2D", "content": "Narrativa global criada."})
        yield dna

        # ETAPA 2: CATÁLOGO DE ATIVOS
        asset_catalog_dict = NeuraLink.execute_task(
            task_id="TASK_02_SELECT_AND_CATALOG_ASSETS",
            template_data={"image_map": json.dumps(image_map_para_llm, ensure_ascii=False, indent=2)},
            expected_format="json", use_memory=True,
        )
        dna.ativos_catalogados = CatalogoDeAtivos(**(asset_catalog_dict or {}))
        dna.chat_history.append({"role": "Composer2D", "content": "Catálogo de ativos universais definido."})
        yield dna

        all_assets_by_id: Dict[str, AtivoCatalogado] = {asset.id: asset for asset_list in [dna.ativos_catalogados.cenarios, dna.ativos_catalogados.personagens, dna.ativos_catalogados.objetos] for asset in asset_list}

        def upsert_asset_from_llm(llm_obj: Dict[str, Any], asset_type: str) -> AtivoCatalogado:
            asset_id = llm_obj.get("id")
            if not asset_id: raise ValueError(f"Ativo do tipo '{asset_type}' recebido do LLM sem 'id'.")
            if asset_id in all_assets_by_id: return all_assets_by_id[asset_id]
            
            logger.info(f"IA propôs um novo ativo dinamicamente: {asset_id} (Tipo: {asset_type})")
            novo_ativo = AtivoCatalogado(**llm_obj)
            getattr(dna.ativos_catalogados, f"{asset_type}s", []).append(novo_ativo)
            all_assets_by_id[novo_ativo.id] = novo_ativo
            return novo_ativo

        # ETAPA 3: STORYBOARD
        storyboard_dict = NeuraLink.execute_task(
            task_id="TASK_03_CREATE_DIRECTORS_STORYBOARD",
            template_data={"num_scenes": num_scenes}, expected_format="json", use_memory=True,
        )
        scene_list = (storyboard_dict or {}).get("storyboard", [])
        normalized_scenes: List[Scene] = []
        for idx, s_dict in enumerate(scene_list):
            if "id_cena" not in s_dict: s_dict["id_cena"] = idx + 1
            canonical_cenario = upsert_asset_from_llm(s_dict.get("cenario_escolhido", {}), "cenario")
            s_dict["cenario_escolhido"] = canonical_cenario.model_dump()
            normalized_scenes.append(Scene(**s_dict))
        dna.storyboard_producao = normalized_scenes
        dna.chat_history.append({"role": "Composer2D", "content": f"Rascunho do storyboard com {len(normalized_scenes)} cenas criado."})
        yield dna

        # ETAPA 4: ATOS
        for scene in dna.storyboard_producao:
            acts_dict = NeuraLink.execute_task(
                task_id="TASK_04_DETAIL_SCENE_INTO_ACTS",
                template_data={"scene_directors_manual": scene.model_dump_json(indent=2), "max_duration_per_act_s": duration_per_fragment},
                expected_format="json", use_memory=True,
            )
            scene.atos = [Ato(**a) for a in (acts_dict or {}).get("atos", [])]
            dna.chat_history.append({"role": "Composer2D", "content": f"Cena {scene.id_cena}: atos detalhados."})
            yield dna

        # --- ETAPA 4.5: PRÉ-REVISÃO (CARREGAMENTO DE CONTEXTO OTIMIZADO) ---
        dna.chat_history.append({"role": "Composer2D", "content": "Rascunho finalizado. Apresentando à IA para análise global antes do polimento..."})
        yield dna

        full_storyboard_json = json.dumps(
            [s.model_dump(mode='json') for s in dna.storyboard_producao], 
            indent=2, 
            ensure_ascii=False
        )
        confirmation_message = NeuraLink.execute_task(
            task_id="TASK_4_5_PRE_REVIEW_CONTEXT",
            template_data={"full_storyboard_json": full_storyboard_json},
            expected_format="text", use_memory=True,
        )
        dna.chat_history.append({"role": "Supervisor de Roteiro (IA)", "content": confirmation_message})
        yield dna

        # --- ETAPA 5: REVISÃO DE CONTINUIDADE CENA A CENA (COM CONTEXTO IMPLÍCITO) ---
        dna.chat_history.append({"role": "Composer2D", "content": "Contexto carregado. Iniciando revisão detalhada cena a cena..."})
        yield dna
        
        refined_storyboard = []
        for scene_to_review in dna.storyboard_producao:
            dna.chat_history.append({"role": "Composer2D", "content": f"Polindo Cena {scene_to_review.id_cena}/{len(dna.storyboard_producao)}: '{scene_to_review.titulo_cena}'..."})
            yield dna

            scene_json_for_prompt = scene_to_review.model_dump_json(indent=2)
            
            refined_scene_dict = NeuraLink.execute_task(
                task_id="TASK_05_SCENE_CONTINUITY_REVIEW",
                template_data={"scene_to_review_json": scene_json_for_prompt},
                expected_format="json", use_memory=True,
            )
            try:
                refined_scene = Scene(**refined_scene_dict)
                refined_storyboard.append(refined_scene)
            except Exception as e:
                logger.warning(f"Falha ao validar cena refinada {scene_to_review.id_cena}. Mantendo a versão original. Erro: {e}")
                refined_storyboard.append(scene_to_review)

        dna.storyboard_producao = refined_storyboard
        dna.chat_history.append({"role": "Composer2D", "content": "Revisão de continuidade concluída. Storyboard finalizado e polido."})
        yield dna

        logger.info("Pipeline completo do Composer2D (Geração e Revisão) concluído com sucesso.")
        return

# Singleton
composer_2d_singleton = Composer2D()