Spaces:
Paused
Paused
Carlexxx
commited on
Commit
·
fb56537
1
Parent(s):
783650e
feat: ✨ aBINC 2.2
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +16 -0
- Dockerfile +480 -0
- LICENSE +25 -0
- NOTICE.md +76 -0
- README.md +208 -6
- aduc_framework/__init__.py +57 -0
- aduc_framework/aduc_sdr.py +137 -0
- aduc_framework/director.py +98 -0
- aduc_framework/engineers/LICENSE +23 -0
- aduc_framework/engineers/NOTICE.md +76 -0
- aduc_framework/engineers/README.md +211 -0
- aduc_framework/engineers/__init__.py +35 -0
- aduc_framework/engineers/composer.py +156 -0
- aduc_framework/engineers/composer_2D.py +160 -0
- aduc_framework/engineers/deformes3D.py +152 -0
- aduc_framework/engineers/deformes4D.py +234 -0
- aduc_framework/engineers/neura_link.py +128 -0
- aduc_framework/engineers/planner_2d.py +91 -0
- aduc_framework/engineers/planner_3d.py +103 -0
- aduc_framework/engineers/planner_4d.py +176 -0
- aduc_framework/engineers/planner_5D.py +143 -0
- aduc_framework/engineers/prompt_engine.py +110 -0
- aduc_framework/managers/LICENSE +25 -0
- aduc_framework/managers/LICENSE.txt +201 -0
- aduc_framework/managers/NOTICE.md +60 -0
- aduc_framework/managers/README.md +156 -0
- aduc_framework/managers/__init__.py +25 -0
- aduc_framework/managers/config.yaml +24 -0
- aduc_framework/managers/flux_kontext_manager.py +165 -0
- aduc_framework/managers/gemini_manager.py +115 -0
- aduc_framework/managers/latent_enhancer_manager.py +109 -0
- aduc_framework/managers/llama_multimodal_manager.py +153 -0
- aduc_framework/managers/ltx_manager.py +360 -0
- aduc_framework/managers/ltx_pipeline_utils.py +774 -0
- aduc_framework/managers/mmaudio_manager.py +226 -0
- aduc_framework/managers/seedvr_manager.py +232 -0
- aduc_framework/managers/upscaler_specialist.py +91 -0
- aduc_framework/managers/vae_manager.py +98 -0
- aduc_framework/prompts/LICENSE +25 -0
- aduc_framework/prompts/NOTICE.md +76 -0
- aduc_framework/prompts/README.md +211 -0
- aduc_framework/prompts/TASK_01_CREATE_NARRATIVE_SYNTHESIS.txt +18 -0
- aduc_framework/prompts/anticipatory_keyframe_prompt.txt +25 -0
- aduc_framework/prompts/audio_director_prompt.txt +18 -0
- aduc_framework/prompts/director_composition_prompt.txt +27 -0
- aduc_framework/prompts/flux_composition_wrapper_prompt.txt +1 -0
- aduc_framework/prompts/initial_motion_prompt.txt +20 -0
- aduc_framework/prompts/keyframe_selection_prompt.txt +20 -0
- aduc_framework/prompts/model_maps/llama_3_2_vision/image_template.txt +5 -0
- aduc_framework/prompts/model_maps/llama_3_2_vision/main_template.txt +5 -0
.gitignore
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ignorar dependências clonadas
|
| 2 |
+
/deps/
|
| 3 |
+
|
| 4 |
+
# Ignorar o diretório do workspace da aplicação
|
| 5 |
+
/deformes_workspace/
|
| 6 |
+
|
| 7 |
+
# Ignorar arquivos de cache do Python
|
| 8 |
+
__pycache__/
|
| 9 |
+
*.pyc
|
| 10 |
+
*.pyo
|
| 11 |
+
*.pyd
|
| 12 |
+
|
| 13 |
+
# Ignorar logs e arquivos de ambiente
|
| 14 |
+
*.log
|
| 15 |
+
aduc_log.txt
|
| 16 |
+
.env
|
Dockerfile
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# =============================================================================
|
| 2 |
+
# DOCKERFILE - Complete AI Video Suite v2.0.0
|
| 3 |
+
# Optimized for 8x NVIDIA L40S GPUs (384GB Total VRAM)
|
| 4 |
+
# Production-Ready Multi-GPU Video Generation Suite
|
| 5 |
+
# =============================================================================
|
| 6 |
+
|
| 7 |
+
FROM nvidia/cuda:12.8.0-devel-ubuntu22.04
|
| 8 |
+
|
| 9 |
+
# =============================================================================
|
| 10 |
+
# METADATA AND LABELS
|
| 11 |
+
# =============================================================================
|
| 12 |
+
|
| 13 |
+
LABEL maintainer="Complete AI Video Suite Team"
|
| 14 |
+
LABEL description="Multi-GPU AI Video Generation Suite with LTX FP8, Q8 Kernels, SeedVR, Wan2.2, VINCIE, MMAudio"
|
| 15 |
+
LABEL version="2.0.0"
|
| 16 |
+
LABEL build_date="2025-09-18"
|
| 17 |
+
LABEL cuda_version="12.4.0"
|
| 18 |
+
LABEL python_version="3.10"
|
| 19 |
+
LABEL pytorch_version="2.8.0+cu128"
|
| 20 |
+
LABEL architecture="amd64"
|
| 21 |
+
LABEL gpu_optimized="8x_L40S"
|
| 22 |
+
LABEL total_vram="384GB"
|
| 23 |
+
LABEL license="MIT"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# =============================================================================
|
| 27 |
+
# ENVIRONMENT VARIABLES - PRODUCTION OPTIMIZED
|
| 28 |
+
# =============================================================================
|
| 29 |
+
|
| 30 |
+
ENV DEBIAN_FRONTEND=noninteractive
|
| 31 |
+
ENV TZ=UTC
|
| 32 |
+
ENV LC_ALL=C.UTF-8
|
| 33 |
+
ENV LANG=C.UTF-8
|
| 34 |
+
|
| 35 |
+
# Python optimization
|
| 36 |
+
ENV PYTHONUNBUFFERED=1
|
| 37 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 38 |
+
ENV PYTHONIOENCODING=utf-8
|
| 39 |
+
ENV PIP_NO_CACHE_DIR=1
|
| 40 |
+
ENV PIP_DISABLE_PIP_VERSION_CHECK=0
|
| 41 |
+
|
| 42 |
+
# CUDA optimizations for 8x L40S GPUs
|
| 43 |
+
ENV NVIDIA_VISIBLE_DEVICES=all
|
| 44 |
+
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics
|
| 45 |
+
ENV NVIDIA_REQUIRE_CUDA="cuda>=12.8"
|
| 46 |
+
ENV CUDA_LAUNCH_BLOCKING=0
|
| 47 |
+
ENV TORCH_CUDA_ARCH_LIST="8.9"
|
| 48 |
+
ENV CUDA_CACHE_MAXSIZE=2147483648
|
| 49 |
+
|
| 50 |
+
# Multi-GPU distributed training
|
| 51 |
+
ENV NCCL_DEBUG=DEBUG
|
| 52 |
+
ENV NCCL_TREE_THRESHOLD=1
|
| 53 |
+
ENV NCCL_P2P_DISABLE=0
|
| 54 |
+
ENV NCCL_IB_DISABLE=0
|
| 55 |
+
ENV NCCL_NVLS_ENABLE=1
|
| 56 |
+
ENV NCCL_CROSS_NIC=1
|
| 57 |
+
|
| 58 |
+
# PyTorch optimizations
|
| 59 |
+
ENV PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512,roundup_power2_divisions:16
|
| 60 |
+
ENV TORCH_BACKENDS_CUDNN_BENCHMARK=1
|
| 61 |
+
ENV TORCH_BACKENDS_CUDA_MATMUL_ALLOW_TF32=1
|
| 62 |
+
ENV TORCH_BACKENDS_CUDNN_ALLOW_TF32=1
|
| 63 |
+
|
| 64 |
+
# Application paths
|
| 65 |
+
ENV APP_HOME=/app
|
| 66 |
+
ENV HF_HOME=/app/model_cache
|
| 67 |
+
ENV HF_HUB_CACHE=/app/model_cache/hub
|
| 68 |
+
ENV TRANSFORMERS_CACHE=/app/model_cache/transformers
|
| 69 |
+
ENV TORCH_HOME=/app/model_cache/torch
|
| 70 |
+
ENV TMPDIR=/app/tmp
|
| 71 |
+
ENV OUTPUT_DIR=/app/outputs
|
| 72 |
+
|
| 73 |
+
# CPU optimizations
|
| 74 |
+
ENV OMP_NUM_THREADS=8
|
| 75 |
+
ENV MKL_NUM_THREADS=8
|
| 76 |
+
ENV NUMEXPR_NUM_THREADS=8
|
| 77 |
+
ENV OPENBLAS_NUM_THREADS=8
|
| 78 |
+
|
| 79 |
+
# =============================================================================
|
| 80 |
+
# SYSTEM PACKAGE INSTALLATION
|
| 81 |
+
# =============================================================================
|
| 82 |
+
|
| 83 |
+
RUN apt-get update && apt-get install -y \
|
| 84 |
+
build-essential \
|
| 85 |
+
cmake \
|
| 86 |
+
ninja-build \
|
| 87 |
+
pkg-config \
|
| 88 |
+
python3.11 \
|
| 89 |
+
python3.11-dev \
|
| 90 |
+
python3.11-distutils \
|
| 91 |
+
python3-pip \
|
| 92 |
+
python3.11-venv \
|
| 93 |
+
git \
|
| 94 |
+
git-lfs \
|
| 95 |
+
curl \
|
| 96 |
+
wget \
|
| 97 |
+
rsync \
|
| 98 |
+
unzip \
|
| 99 |
+
zip \
|
| 100 |
+
ffmpeg \
|
| 101 |
+
libavcodec-dev \
|
| 102 |
+
libavformat-dev \
|
| 103 |
+
libavutil-dev \
|
| 104 |
+
libswscale-dev \
|
| 105 |
+
libgl1-mesa-glx \
|
| 106 |
+
libgl1-mesa-dev \
|
| 107 |
+
libglib2.0-0 \
|
| 108 |
+
libsm6 \
|
| 109 |
+
libxext6 \
|
| 110 |
+
libxrender-dev \
|
| 111 |
+
libgomp1 \
|
| 112 |
+
libglu1-mesa \
|
| 113 |
+
libglu1-mesa-dev \
|
| 114 |
+
htop \
|
| 115 |
+
nvtop \
|
| 116 |
+
tree \
|
| 117 |
+
vim \
|
| 118 |
+
nano \
|
| 119 |
+
tmux \
|
| 120 |
+
screen \
|
| 121 |
+
net-tools \
|
| 122 |
+
iproute2 \
|
| 123 |
+
iotop \
|
| 124 |
+
&& apt-get autoremove -y \
|
| 125 |
+
&& apt-get clean \
|
| 126 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 127 |
+
&& rm -rf /tmp/* \
|
| 128 |
+
&& rm -rf /var/tmp/*
|
| 129 |
+
|
| 130 |
+
# =============================================================================
|
| 131 |
+
# PYTHON SETUP AND OPTIMIZATION
|
| 132 |
+
# =============================================================================
|
| 133 |
+
|
| 134 |
+
RUN ln -sf /usr/bin/python3.10 /usr/bin/python3 && \
|
| 135 |
+
ln -sf /usr/bin/python3.10 /usr/bin/python && \
|
| 136 |
+
python3 -m pip install --upgrade pip==24.2 setuptools==70.0.0 wheel==0.43.0
|
| 137 |
+
|
| 138 |
+
RUN pip install \
|
| 139 |
+
packaging \
|
| 140 |
+
ninja \
|
| 141 |
+
cmake \
|
| 142 |
+
pybind11 \
|
| 143 |
+
scikit-build \
|
| 144 |
+
cython \
|
| 145 |
+
numpy>=1.24.3
|
| 146 |
+
|
| 147 |
+
# =============================================================================
|
| 148 |
+
# PYTORCH AND CUDA LIBRARIES
|
| 149 |
+
# =============================================================================
|
| 150 |
+
|
| 151 |
+
RUN pip install \
|
| 152 |
+
torch>=2.8.0+cu128 \
|
| 153 |
+
torchvision \
|
| 154 |
+
torchaudio \
|
| 155 |
+
--index-url https://download.pytorch.org/whl/cu128
|
| 156 |
+
|
| 157 |
+
RUN pip install torchao
|
| 158 |
+
|
| 159 |
+
RUN python3 -c "import torch; print(f'PyTorch: {torch.__version__}'); print(f'CUDA available: {torch.cuda.is_available()}'); print(f'CUDA version: {torch.version.cuda}'); print(f'Device count: {torch.cuda.device_count()}')"
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# =============================================================================
|
| 163 |
+
# AI/ML LIBRARIES INSTALLATION
|
| 164 |
+
# =============================================================================
|
| 165 |
+
|
| 166 |
+
WORKDIR $APP_HOME
|
| 167 |
+
COPY . .
|
| 168 |
+
|
| 169 |
+
RUN pip install -r requirements.txt
|
| 170 |
+
|
| 171 |
+
# =============================================================================
|
| 172 |
+
# APPLICATION STRUCTURE SETUP
|
| 173 |
+
# =============================================================================
|
| 174 |
+
|
| 175 |
+
RUN mkdir -p \
|
| 176 |
+
$APP_HOME/installer \
|
| 177 |
+
$APP_HOME/monitoring \
|
| 178 |
+
$APP_HOME/tools \
|
| 179 |
+
$APP_HOME/configs \
|
| 180 |
+
$APP_HOME/build_cache \
|
| 181 |
+
$APP_HOME/model_cache/hub \
|
| 182 |
+
$APP_HOME/model_cache/transformers \
|
| 183 |
+
$APP_HOME/model_cache/torch \
|
| 184 |
+
$APP_HOME/model_cache/ltx_models \
|
| 185 |
+
$APP_HOME/tmp \
|
| 186 |
+
$APP_HOME/outputs \
|
| 187 |
+
$APP_HOME/logs \
|
| 188 |
+
&& chmod -R 755 $APP_HOME
|
| 189 |
+
|
| 190 |
+
# =============================================================================
|
| 191 |
+
# DOWNLOAD PREREQUISITE FILES
|
| 192 |
+
# =============================================================================
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
COPY . .
|
| 196 |
+
|
| 197 |
+
COPY configs/ ./configs/
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
RUN chmod +x start.sh && \
|
| 201 |
+
find . -name "*.sh" -exec chmod +x {} \; && \
|
| 202 |
+
find . -name "*.py" -exec chmod +x {} \;
|
| 203 |
+
|
| 204 |
+
# =============================================================================
|
| 205 |
+
# CREATE OPTIMIZATION PATCHES AND TOOLS (FIXED SYNTAX)
|
| 206 |
+
# =============================================================================
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
# =============================================================================
|
| 210 |
+
# CONFIGURATION FILES
|
| 211 |
+
# =============================================================================
|
| 212 |
+
|
| 213 |
+
# Create default LTX FP8 configuration
|
| 214 |
+
RUN cat <<'YAML_CONFIG' > $APP_HOME/configs/ltxv-13b-0.9.8-distilled-fp8.yaml
|
| 215 |
+
# LTX Video FP8 Distilled Configuration
|
| 216 |
+
# Optimized for 8x L40S GPUs (384GB VRAM)
|
| 217 |
+
model:
|
| 218 |
+
target: "ltx_video.models.transformer_temporal.TransformerTemporalModel"
|
| 219 |
+
params:
|
| 220 |
+
transformer_additional_kwargs:
|
| 221 |
+
attention_mode: "sdpa"
|
| 222 |
+
enable_flash_attention: true
|
| 223 |
+
memory_efficient_attention: true
|
| 224 |
+
network_config:
|
| 225 |
+
model_name: "ltxv-13b-0.9.8-distilled-fp8"
|
| 226 |
+
fp8_optimization: true
|
| 227 |
+
quantization: "fp8"
|
| 228 |
+
ada_optimized: true
|
| 229 |
+
multi_gpu_support: true
|
| 230 |
+
|
| 231 |
+
scheduler:
|
| 232 |
+
target: "diffusers.LTXVideoScheduler"
|
| 233 |
+
params:
|
| 234 |
+
num_train_timesteps: 1000
|
| 235 |
+
beta_start: 0.0001
|
| 236 |
+
beta_end: 0.02
|
| 237 |
+
beta_schedule: "scaled_linear"
|
| 238 |
+
|
| 239 |
+
vae:
|
| 240 |
+
target: "diffusers.AutoencoderKLLTXVideo"
|
| 241 |
+
params:
|
| 242 |
+
force_upcast: false
|
| 243 |
+
enable_slicing: true
|
| 244 |
+
enable_tiling: true
|
| 245 |
+
|
| 246 |
+
text_encoder:
|
| 247 |
+
target: "transformers.T5EncoderModel"
|
| 248 |
+
params:
|
| 249 |
+
torch_dtype: "bfloat16"
|
| 250 |
+
|
| 251 |
+
pipeline:
|
| 252 |
+
target: "diffusers.LTXVideoPipeline"
|
| 253 |
+
params:
|
| 254 |
+
scheduler_type: "LTXVideoScheduler"
|
| 255 |
+
num_inference_steps: 4
|
| 256 |
+
guidance_scale: 1.0
|
| 257 |
+
height: 704
|
| 258 |
+
width: 1216
|
| 259 |
+
num_frames: 121
|
| 260 |
+
fps: 30
|
| 261 |
+
enable_memory_efficient_attention: true
|
| 262 |
+
enable_cpu_offload: false
|
| 263 |
+
enable_model_cpu_offload: false
|
| 264 |
+
max_batch_size: 4
|
| 265 |
+
|
| 266 |
+
multi_gpu:
|
| 267 |
+
enabled: true
|
| 268 |
+
num_gpus: 8
|
| 269 |
+
distribution_strategy: "data_parallel"
|
| 270 |
+
load_balancing: "memory_aware"
|
| 271 |
+
synchronize_gpus: true
|
| 272 |
+
YAML_CONFIG
|
| 273 |
+
|
| 274 |
+
# Create multi-GPU optimization config
|
| 275 |
+
RUN cat <<'GPU_CONFIG' > $APP_HOME/configs/multi_gpu_config.yaml
|
| 276 |
+
# Multi-GPU Configuration for 8x L40S Setup
|
| 277 |
+
system:
|
| 278 |
+
gpu_count: 8
|
| 279 |
+
total_vram: "384GB"
|
| 280 |
+
compute_capability: "8.9"
|
| 281 |
+
architecture: "ADA_LOVELACE"
|
| 282 |
+
|
| 283 |
+
distributed_training:
|
| 284 |
+
backend: "nccl"
|
| 285 |
+
init_method: "env://"
|
| 286 |
+
world_size: 8
|
| 287 |
+
rank: 0
|
| 288 |
+
|
| 289 |
+
memory_optimization:
|
| 290 |
+
gradient_checkpointing: true
|
| 291 |
+
mixed_precision: "bf16"
|
| 292 |
+
max_batch_size_per_gpu: 8
|
| 293 |
+
gradient_accumulation_steps: 4
|
| 294 |
+
memory_fraction: 0.95
|
| 295 |
+
|
| 296 |
+
performance:
|
| 297 |
+
torch_compile: true
|
| 298 |
+
cuda_graphs: true
|
| 299 |
+
tensor_cores: true
|
| 300 |
+
flash_attention: true
|
| 301 |
+
memory_efficient_attention: true
|
| 302 |
+
|
| 303 |
+
load_balancing:
|
| 304 |
+
strategy: "memory_aware"
|
| 305 |
+
rebalance_interval: 30
|
| 306 |
+
utilization_threshold: 0.8
|
| 307 |
+
|
| 308 |
+
thermal_management:
|
| 309 |
+
max_temperature: 83
|
| 310 |
+
fan_curve: "aggressive"
|
| 311 |
+
throttle_threshold: 80
|
| 312 |
+
monitoring_interval: 10
|
| 313 |
+
|
| 314 |
+
power_management:
|
| 315 |
+
max_power_limit: 300
|
| 316 |
+
efficiency_mode: false
|
| 317 |
+
power_monitoring: true
|
| 318 |
+
GPU_CONFIG
|
| 319 |
+
|
| 320 |
+
# =============================================================================
|
| 321 |
+
# HEALTH CHECK SCRIPT
|
| 322 |
+
# =============================================================================
|
| 323 |
+
|
| 324 |
+
RUN cat <<'HEALTHCHECK_SCRIPT' > $APP_HOME/healthcheck.py
|
| 325 |
+
#!/usr/bin/env python3
|
| 326 |
+
"""
|
| 327 |
+
Health check script for Complete AI Video Suite
|
| 328 |
+
"""
|
| 329 |
+
import sys
|
| 330 |
+
import requests
|
| 331 |
+
import torch
|
| 332 |
+
import time
|
| 333 |
+
import logging
|
| 334 |
+
|
| 335 |
+
logging.basicConfig(level=logging.INFO)
|
| 336 |
+
logger = logging.getLogger(__name__)
|
| 337 |
+
|
| 338 |
+
def check_cuda():
|
| 339 |
+
"""Check CUDA availability and GPU status"""
|
| 340 |
+
if not torch.cuda.is_available():
|
| 341 |
+
logger.error("CUDA not available")
|
| 342 |
+
return False
|
| 343 |
+
|
| 344 |
+
gpu_count = torch.cuda.device_count()
|
| 345 |
+
logger.info(f"CUDA available with {gpu_count} GPUs")
|
| 346 |
+
|
| 347 |
+
for i in range(gpu_count):
|
| 348 |
+
try:
|
| 349 |
+
torch.cuda.set_device(i)
|
| 350 |
+
props = torch.cuda.get_device_properties(i)
|
| 351 |
+
memory_allocated = torch.cuda.memory_allocated() / 1024**3
|
| 352 |
+
memory_total = props.total_memory / 1024**3
|
| 353 |
+
|
| 354 |
+
logger.info(f"GPU {i}: {props.name} ({memory_allocated:.2f}GB/{memory_total:.1f}GB)")
|
| 355 |
+
|
| 356 |
+
x = torch.randn(100, 100, device=f'cuda:{i}')
|
| 357 |
+
y = torch.matmul(x, x)
|
| 358 |
+
torch.cuda.synchronize()
|
| 359 |
+
|
| 360 |
+
except Exception as e:
|
| 361 |
+
logger.error(f"GPU {i} test failed: {e}")
|
| 362 |
+
return False
|
| 363 |
+
|
| 364 |
+
return True
|
| 365 |
+
|
| 366 |
+
def check_web_service():
|
| 367 |
+
"""Check if web service is responding"""
|
| 368 |
+
try:
|
| 369 |
+
response = requests.get("http://localhost:7860/", timeout=10)
|
| 370 |
+
if response.status_code == 200:
|
| 371 |
+
logger.info("Web service is responding")
|
| 372 |
+
return True
|
| 373 |
+
else:
|
| 374 |
+
logger.error(f"Web service returned status code: {response.status_code}")
|
| 375 |
+
return False
|
| 376 |
+
except requests.RequestException as e:
|
| 377 |
+
logger.error(f"Web service check failed: {e}")
|
| 378 |
+
return False
|
| 379 |
+
|
| 380 |
+
def main():
|
| 381 |
+
"""Main health check routine"""
|
| 382 |
+
logger.info("Starting health check...")
|
| 383 |
+
|
| 384 |
+
if not check_cuda():
|
| 385 |
+
sys.exit(1)
|
| 386 |
+
|
| 387 |
+
if not check_web_service():
|
| 388 |
+
sys.exit(1)
|
| 389 |
+
|
| 390 |
+
logger.info("All health checks passed")
|
| 391 |
+
sys.exit(0)
|
| 392 |
+
|
| 393 |
+
if __name__ == "__main__":
|
| 394 |
+
main()
|
| 395 |
+
HEALTHCHECK_SCRIPT
|
| 396 |
+
|
| 397 |
+
RUN chmod +x $APP_HOME/healthcheck.py
|
| 398 |
+
|
| 399 |
+
# =============================================================================
|
| 400 |
+
# USER SETUP AND SECURITY
|
| 401 |
+
# =============================================================================
|
| 402 |
+
|
| 403 |
+
RUN mkdir -p /etc/sudoers.d && \
|
| 404 |
+
useradd -m -u 1000 -s /bin/bash appuser && \
|
| 405 |
+
usermod -aG sudo appuser && \
|
| 406 |
+
chown -R appuser:appuser $APP_HOME && \
|
| 407 |
+
echo "appuser ALL=(ALL) NOPASSWD: /usr/bin/nvidia-smi, /usr/bin/nvidia-ml-py" > /etc/sudoers.d/appuser
|
| 408 |
+
|
| 409 |
+
USER appuser
|
| 410 |
+
|
| 411 |
+
WORKDIR $APP_HOME
|
| 412 |
+
|
| 413 |
+
# =============================================================================
|
| 414 |
+
# RUNTIME CONFIGURATION
|
| 415 |
+
# =============================================================================
|
| 416 |
+
|
| 417 |
+
EXPOSE 7860 8001 8002 6006
|
| 418 |
+
|
| 419 |
+
VOLUME ["/app/model_cache", "/app/outputs", "/app/logs", "/app/build_cache"]
|
| 420 |
+
|
| 421 |
+
HEALTHCHECK --interval=60s --timeout=30s --start-period=300s --retries=3 \
|
| 422 |
+
CMD python3 /app/healthcheck.py
|
| 423 |
+
|
| 424 |
+
# =============================================================================
|
| 425 |
+
# FINAL SETUP AND ENTRY POINT
|
| 426 |
+
# =============================================================================
|
| 427 |
+
|
| 428 |
+
RUN cat <<'ENTRYPOINT_SCRIPT' > $APP_HOME/docker-entrypoint.sh
|
| 429 |
+
#!/bin/bash
|
| 430 |
+
set -euo pipefail
|
| 431 |
+
|
| 432 |
+
echo "🚀 Complete AI Suite - Docker Container Starting..."
|
| 433 |
+
echo "🐳 Container: $(hostname)"
|
| 434 |
+
echo "👤 User: $(whoami)"
|
| 435 |
+
echo "🎮 GPUs: $(nvidia-smi --list-gpus | wc -l || echo '0')"
|
| 436 |
+
|
| 437 |
+
if command -v nvidia-smi >/dev/null 2>&1; then
|
| 438 |
+
echo "💾 CUDA Memory:"
|
| 439 |
+
nvidia-smi --query-gpu=memory.total,memory.used --format=csv,noheader,nounits | nl
|
| 440 |
+
fi
|
| 441 |
+
|
| 442 |
+
echo "🔧 Applying optimization patches..."
|
| 443 |
+
python3 /app/tools/optimization_patch.py
|
| 444 |
+
|
| 445 |
+
echo "📁 Setting up permissions..."
|
| 446 |
+
chmod -R 755 /app/installer
|
| 447 |
+
chmod -R 755 /app/monitoring
|
| 448 |
+
chmod +x /app/start.sh
|
| 449 |
+
|
| 450 |
+
mkdir -p /app/logs /app/outputs /app/tmp
|
| 451 |
+
chmod 777 /app/logs /app/outputs /app/tmp
|
| 452 |
+
|
| 453 |
+
echo "✅ Docker container initialization complete"
|
| 454 |
+
echo "🚀 Starting Complete AI Video Suite..."
|
| 455 |
+
|
| 456 |
+
exec /app/start.sh "$@"
|
| 457 |
+
ENTRYPOINT_SCRIPT
|
| 458 |
+
|
| 459 |
+
RUN chmod +x $APP_HOME/docker-entrypoint.sh
|
| 460 |
+
|
| 461 |
+
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
| 462 |
+
|
| 463 |
+
CMD ["--listen", "--multi-gpu", "--optimize"]
|
| 464 |
+
|
| 465 |
+
# =============================================================================
|
| 466 |
+
# FINAL METADATA
|
| 467 |
+
# =============================================================================
|
| 468 |
+
|
| 469 |
+
RUN echo "Complete AI Video Suite v2.0.0" > /app/VERSION && \
|
| 470 |
+
echo "Build Date: 2025-09-18T$(date +%H:%M:%S)" >> /app/VERSION && \
|
| 471 |
+
echo "CUDA: 12.4.1" >> /app/VERSION && \
|
| 472 |
+
echo "PyTorch: $(python3 -c 'import torch; print(torch.__version__)')" >> /app/VERSION && \
|
| 473 |
+
echo "Optimized for: 8x NVIDIA L40S GPUs" >> /app/VERSION
|
| 474 |
+
|
| 475 |
+
LABEL org.opencontainers.image.title="Complete AI Video Suite"
|
| 476 |
+
LABEL org.opencontainers.image.description="Production-ready multi-GPU video generation with LTX FP8, Q8 Kernels, and more"
|
| 477 |
+
LABEL org.opencontainers.image.version="2.0.0"
|
| 478 |
+
LABEL org.opencontainers.image.created="2025-09-18T17:42:00Z"
|
| 479 |
+
LABEL org.opencontainers.image.revision="main"
|
| 480 |
+
LABEL org.opencontainers.image.licenses="MIT"
|
LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Euia-AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR para geração de vídeo coerente.
|
| 2 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 3 |
+
#
|
| 4 |
+
# Contato:
|
| 5 |
+
# Carlos Rodrigues dos Santos
|
| 6 | |
| 7 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 8 |
+
#
|
| 9 |
+
# Repositórios e Projetos Relacionados:
|
| 10 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 11 |
+
# Hugging Face (Ltx-SuperTime-60Secondos): https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/
|
| 12 |
+
# Hugging Face (Novinho): https://huggingface.co/spaces/Carlexxx/Novinho/
|
| 13 |
+
#
|
| 14 |
+
# This program is free software: you can redistribute it and/or modify
|
| 15 |
+
# it under the terms of the GNU Affero General Public License as published by
|
| 16 |
+
# the Free Software Foundation, either version 3 of the License, or
|
| 17 |
+
# (at your option) any later version.
|
| 18 |
+
#
|
| 19 |
+
# This program is distributed in the hope that it will be useful,
|
| 20 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 21 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 22 |
+
# GNU Affero General Public License for more details.
|
| 23 |
+
#
|
| 24 |
+
# You should have received a copy of the GNU Affero General Public License
|
| 25 |
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
NOTICE.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTICE
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2025 Carlos Rodrigues dos Santos. All rights reserved.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Aviso de Propriedade Intelectual e Licenciamento
|
| 8 |
+
|
| 9 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 10 |
+
|
| 11 |
+
O método e o sistema de orquestração de prompts denominados **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste documento e implementados neste software, estão atualmente em processo de patenteamento.
|
| 12 |
+
|
| 13 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, incluindo, mas não se limitando a:
|
| 14 |
+
|
| 15 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 16 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 17 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 18 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 19 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 20 |
+
|
| 21 |
+
### **Reconhecimento e Implicações (EM PORTUGUÊS):**
|
| 22 |
+
|
| 23 |
+
Ao acessar ou utilizar este software e a arquitetura ADUC aqui implementada, você reconhece:
|
| 24 |
+
|
| 25 |
+
1. A natureza inovadora e a importância da arquitetura ADUC no campo da orquestração de prompts para IA.
|
| 26 |
+
2. Que a essência desta arquitetura, ou suas implementações derivadas, podem estar sujeitas a direitos de propriedade intelectual, incluindo patentes.
|
| 27 |
+
3. Que o uso comercial, a reprodução da lógica central da ADUC em sistemas independentes, ou a exploração direta da invenção sem o devido licenciamento podem infringir os direitos de patente pendente.
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
### **Patent Pending (IN ENGLISH):**
|
| 32 |
+
|
| 33 |
+
The method and system for prompt orchestration named **ADUC (Automated Discovery and Orchestration of Complex tasks)**, as described herein and implemented in this software, are currently in the process of being patented.
|
| 34 |
+
|
| 35 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 36 |
+
|
| 37 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 38 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 39 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 40 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 41 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 42 |
+
|
| 43 |
+
### **Acknowledgement and Implications (IN ENGLISH):**
|
| 44 |
+
|
| 45 |
+
By accessing or using this software and the ADUC architecture implemented herein, you acknowledge:
|
| 46 |
+
|
| 47 |
+
1. The innovative nature and significance of the ADUC architecture in the field of AI prompt orchestration.
|
| 48 |
+
2. That the essence of this architecture, or its derivative implementations, may be subject to intellectual property rights, including patents.
|
| 49 |
+
3. That commercial use, reproduction of ADUC's core logic in independent systems, or direct exploitation of the invention without proper licensing may infringe upon pending patent rights.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
## Licença AGPLv3
|
| 54 |
+
|
| 55 |
+
This program is free software: you can redistribute it and/or modify
|
| 56 |
+
it under the terms of the GNU Affero General Public License as published by
|
| 57 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 58 |
+
(at your option) any later version.
|
| 59 |
+
|
| 60 |
+
This program is distributed in the hope that it will be useful,
|
| 61 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 62 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 63 |
+
GNU Affero General Public License for more details.
|
| 64 |
+
|
| 65 |
+
You should have received a copy of the GNU Affero General Public License
|
| 66 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
**Contato para Consultas:**
|
| 71 |
+
|
| 72 |
+
Para mais informações sobre a arquitetura ADUC, o status do patenteamento, ou para discutir licenciamento para usos comerciais ou não conformes com a AGPLv3, por favor, entre em contato:
|
| 73 |
+
|
| 74 |
+
Carlos Rodrigues dos Santos
|
| 75 | |
| 76 |
+
Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
README.md
CHANGED
|
@@ -1,10 +1,212 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: purple
|
| 6 |
-
sdk:
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Euia-AducSdr
|
| 3 |
+
emoji: 🎥
|
| 4 |
+
colorFrom: indigo
|
| 5 |
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
app_file: app.py
|
| 8 |
+
pinned: true
|
| 9 |
+
license: agpl-3.0
|
| 10 |
+
setup_file: setup.sh
|
| 11 |
+
short_description: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 12 |
---
|
| 13 |
|
| 14 |
+
|
| 15 |
+
### 🇧🇷 Português
|
| 16 |
+
|
| 17 |
+
Uma implementação aberta e funcional da arquitetura ADUC-SDR (Arquitetura de Unificação Compositiva - Escala Dinâmica e Resiliente), projetada para a geração de vídeo coerente de longa duração. Este projeto materializa os princípios de fragmentação, navegação geométrica e um mecanismo de "eco causal 4bits memoria" para garantir a continuidade física e narrativa em sequências de vídeo geradas por múltiplos modelos de IA.
|
| 18 |
+
|
| 19 |
+
**Licença:** Este projeto é licenciado sob os termos da **GNU Affero General Public License v3.0**. Isto significa que se você usar este software (ou qualquer trabalho derivado) para fornecer um serviço através de uma rede, você é **obrigado a disponibilizar o código-fonte completo** da sua versão para os usuários desse serviço.
|
| 20 |
+
|
| 21 |
+
- **Copyright (C) 4 de Agosto de 2025, Carlos Rodrigues dos Santos**
|
| 22 |
+
- Uma cópia completa da licença pode ser encontrada no arquivo [LICENSE](LICENSE).
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
### 🇬🇧 English
|
| 27 |
+
|
| 28 |
+
An open and functional implementation of the ADUC-SDR (Architecture for Compositive Unification - Dynamic and Resilient Scaling) architecture, designed for long-form coherent video generation. This project materializes the principles of fragmentation, geometric navigation, and a "causal echo 4bits memori" mechanism to ensure physical and narrative continuity in video sequences generated by multiple AI models.
|
| 29 |
+
|
| 30 |
+
**License:** This project is licensed under the terms of the **GNU Affero General Public License v3.0**. This means that if you use this software (or any derivative work) to provide a service over a network, you are **required to make the complete source code** of your version available to the users of that service.
|
| 31 |
+
|
| 32 |
+
- **Copyright (C) August 4, 2025, Carlos Rodrigues dos Santos**
|
| 33 |
+
- A full copy of the license can be found in the [LICENSE](LICENSE) file.
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## **Aviso de Propriedade Intelectual e Patenteamento**
|
| 38 |
+
|
| 39 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 40 |
+
|
| 41 |
+
A arquitetura e o método **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste projeto e nas reivindicações associadas, estão **atualmente em processo de patenteamento**.
|
| 42 |
+
|
| 43 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, que incluem, mas não se limitam a:
|
| 44 |
+
|
| 45 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 46 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 47 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 48 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 49 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 50 |
+
|
| 51 |
+
Ao utilizar este software e a arquitetura ADUC aqui implementada, você reconhece a natureza inovadora desta arquitetura e que a **reprodução ou exploração da lógica central da ADUC em sistemas independentes pode infringir direitos de patente pendente.**
|
| 52 |
+
|
| 53 |
+
---
|
| 54 |
+
|
| 55 |
+
### **Patent Pending (IN ENGLISH):**
|
| 56 |
+
|
| 57 |
+
The **ADUC (Automated Discovery and Orchestration of Complex tasks)** architecture and method, as described in this project and its associated claims, are **currently in the process of being patented.**
|
| 58 |
+
|
| 59 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 60 |
+
|
| 61 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 62 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 63 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 64 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 65 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 66 |
+
|
| 67 |
+
By using this software and the ADUC architecture implemented herein, you acknowledge the innovative nature of this architecture and that **the reproduction or exploitation of ADUC's core logic in independent systems may infringe upon pending patent rights.**
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
### Detalhes Técnicos e Reivindicações da ADUC
|
| 72 |
+
|
| 73 |
+
#### 🇧🇷 Definição Curta (para Tese e Patente)
|
| 74 |
+
|
| 75 |
+
**ADUC** é um *framework pré-input* e *intermediário* de **gerenciamento de prompts** que:
|
| 76 |
+
|
| 77 |
+
1. **fragmenta** solicitações acima do limite de contexto de qualquer modelo,
|
| 78 |
+
2. **escala linearmente** (processo sequencial com memória persistida),
|
| 79 |
+
3. **distribui** sub-tarefas a **especialistas** (modelos/ferramentas heterogêneos), e
|
| 80 |
+
4. **realimenta** a próxima etapa com avaliação do que foi feito/esperado (LLM diretor).
|
| 81 |
+
|
| 82 |
+
Não é um modelo; é uma **camada orquestradora** plugável antes do input de modelos existentes (texto, imagem, áudio, vídeo), usando *tokens universais* e a tecnologia atual.
|
| 83 |
+
|
| 84 |
+
#### 🇬🇧 Short Definition (for Thesis and Patent)
|
| 85 |
+
|
| 86 |
+
**ADUC** is a *pre-input* and *intermediate* **prompt management framework** that:
|
| 87 |
+
|
| 88 |
+
1. **fragments** requests exceeding any model's context limit,
|
| 89 |
+
2. **scales linearly** (sequential process with persisted memory),
|
| 90 |
+
3. **distributes** sub-tasks to **specialists** (heterogeneous models/tools), and
|
| 91 |
+
4. **feeds back** to the next step with an evaluation of what was done/expected (director LLM).
|
| 92 |
+
|
| 93 |
+
It is not a model; it is a pluggable **orchestration layer** before the input of existing models (text, image, audio, video), using *universal tokens* and current technology.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
#### 🇧🇷 Elementos Essenciais (Telegráfico)
|
| 98 |
+
|
| 99 |
+
* **Agnóstico a modelos:** opera com qualquer LLM/difusor/API.
|
| 100 |
+
* **Pré-input manager:** recebe pedido do usuário, **divide** em blocos ≤ limite de tokens, **prioriza**, **agenda** e **roteia**.
|
| 101 |
+
* **Memória persistida:** resultados/latentes/“eco” viram **estado compartilhado** para o próximo bloco (nada é ignorado).
|
| 102 |
+
* **Especialistas:** *routers* decidem quem faz o quê (ex.: “descrição → LLM-A”, “keyframe → Img-B”, “vídeo → Vid-C”).
|
| 103 |
+
* **Controle de qualidade:** LLM diretor compara *o que fez* × *o que deveria* × *o que falta* e **regenera objetivos** do próximo fragmento.
|
| 104 |
+
* **Custo/latência-aware:** planeja pela **VRAM/tempo/custo**, não tenta “abraçar tudo de uma vez”.
|
| 105 |
+
|
| 106 |
+
#### 🇬🇧 Essential Elements (Telegraphic)
|
| 107 |
+
|
| 108 |
+
* **Model-agnostic:** operates with any LLM/diffuser/API.
|
| 109 |
+
* **Pre-input manager:** receives user request, **divides** into blocks ≤ token limit, **prioritizes**, **schedules**, and **routes**.
|
| 110 |
+
* **Persisted memory:** results/latents/“echo” become **shared state** for the next block (nothing is ignored).
|
| 111 |
+
* **Specialists:** *routers* decide who does what (e.g., “description → LLM-A”, “keyframe → Img-B”, “video → Vid-C”).
|
| 112 |
+
* **Quality control:** director LLM compares *what was done* × *what should be done* × *what is missing* and **regenerates objectives** for the next fragment.
|
| 113 |
+
* **Cost/latency-aware:** plans by **VRAM/time/cost**, does not try to “embrace everything at once”.
|
| 114 |
+
|
| 115 |
+
---
|
| 116 |
+
|
| 117 |
+
#### 🇧🇷 Reivindicações Independentes (Método e Sistema)
|
| 118 |
+
|
| 119 |
+
**Reivindicação Independente (Método) — Versão Enxuta:**
|
| 120 |
+
|
| 121 |
+
1. **Método** de **orquestração de prompts** para execução de tarefas acima do limite de contexto de modelos de IA, compreendendo:
|
| 122 |
+
(a) **receber** uma solicitação que excede um limite de tokens;
|
| 123 |
+
(b) **analisar** a solicitação por um **LLM diretor** e **fragmentá-la** em sub-tarefas ≤ limite;
|
| 124 |
+
(c) **selecionar** especialistas de execução para cada sub-tarefa com base em capacidades declaradas;
|
| 125 |
+
(d) **gerar** prompts específicos por sub-tarefa em **tokens universais**, incluindo referências ao **estado persistido** de execuções anteriores;
|
| 126 |
+
(e) **executar sequencialmente** as sub-tarefas e **persistir** suas saídas como memória (incluindo latentes/eco/artefatos);
|
| 127 |
+
(f) **avaliar** automaticamente a saída versus metas declaradas e **regenerar objetivos** do próximo fragmento;
|
| 128 |
+
(g) **iterar** (b)–(f) até que os critérios de completude sejam atendidos, produzindo o resultado agregado;
|
| 129 |
+
em que o framework **escala linearmente** no tempo e armazenamento físico, **independente** da janela de contexto dos modelos subjacentes.
|
| 130 |
+
|
| 131 |
+
**Reivindicação Independente (Sistema):**
|
| 132 |
+
|
| 133 |
+
2. **Sistema** de orquestração de prompts, compreendendo: um **planejador LLM diretor**; um **roteador de especialistas**; um **banco de estado persistido** (incl. memória cinética para vídeo); um **gerador de prompts universais**; e um **módulo de avaliação/realimentação**, acoplados por uma **API pré-input** a modelos heterogêneos.
|
| 134 |
+
|
| 135 |
+
#### 🇬🇧 Independent Claims (Method and System)
|
| 136 |
+
|
| 137 |
+
**Independent Claim (Method) — Concise Version:**
|
| 138 |
+
|
| 139 |
+
1. A **method** for **prompt orchestration** for executing tasks exceeding AI model context limits, comprising:
|
| 140 |
+
(a) **receiving** a request that exceeds a token limit;
|
| 141 |
+
(b) **analyzing** the request by a **director LLM** and **fragmenting it** into sub-tasks ≤ the limit;
|
| 142 |
+
(c) **selecting** execution specialists for each sub-task based on declared capabilities;
|
| 143 |
+
(d) **generating** specific prompts per sub-task in **universal tokens**, including references to the **persisted state** of previous executions;
|
| 144 |
+
(e) **sequentially executing** the sub-tasks and **persisting** their outputs as memory (including latents/echo/artifacts);
|
| 145 |
+
(f) **automatically evaluating** the output against declared goals and **regenerating objectives** for the next fragment;
|
| 146 |
+
(g) **iterating** (b)–(f) until completion criteria are met, producing the aggregated result;
|
| 147 |
+
wherein the framework **scales linearly** in time and physical storage, **independent** of the context window of the underlying models.
|
| 148 |
+
|
| 149 |
+
**Independent Claim (System):**
|
| 150 |
+
|
| 151 |
+
2. A prompt orchestration **system**, comprising: a **director LLM planner**; a **specialist router**; a **persisted state bank** (incl. kinetic memory for video); a **universal prompt generator**; and an **evaluation/feedback module**, coupled via a **pre-input API** to heterogeneous models.
|
| 152 |
+
|
| 153 |
+
---
|
| 154 |
+
|
| 155 |
+
#### 🇧🇷 Dependentes Úteis
|
| 156 |
+
|
| 157 |
+
* (3) Onde o roteamento considera **custo/latência/VRAM** e metas de qualidade.
|
| 158 |
+
* (4) Onde o banco de estado inclui **eco cinético** para vídeo (últimos *n* frames/latentes/fluxo).
|
| 159 |
+
* (5) Onde a avaliação usa métricas específicas por domínio (Lflow, consistência semântica, etc.).
|
| 160 |
+
* (6) Onde *tokens universais* padronizam instruções entre especialistas.
|
| 161 |
+
* (7) Onde a orquestração decide **cut vs continuous** e **corte regenerativo** (Déjà-Vu) ao editar vídeo.
|
| 162 |
+
* (8) Onde o sistema **nunca descarta** conteúdo excedente: **reagenda** em novos fragmentos.
|
| 163 |
+
|
| 164 |
+
#### 🇬🇧 Useful Dependents
|
| 165 |
+
|
| 166 |
+
* (3) Wherein routing considers **cost/latency/VRAM** and quality goals.
|
| 167 |
+
* (4) Wherein the state bank includes **kinetic echo** for video (last *n* frames/latents/flow).
|
| 168 |
+
* (5) Wherein evaluation uses domain-specific metrics (Lflow, semantic consistency, etc.).
|
| 169 |
+
* (6) Wherein *universal tokens* standardize instructions between specialists.
|
| 170 |
+
* (7) Wherein orchestration decides **cut vs continuous** and **regenerative cut** (Déjà-Vu) when editing video.
|
| 171 |
+
* (8) Wherein the system **never discards** excess content: it **reschedules** it in new fragments.
|
| 172 |
+
|
| 173 |
+
---
|
| 174 |
+
|
| 175 |
+
#### 🇧🇷 Como isso conversa com SDR (Vídeo)
|
| 176 |
+
|
| 177 |
+
* **Eco Cinético**: é um **tipo de estado persistido** consumido pelo próximo passo.
|
| 178 |
+
* **Déjà-Vu (Corte Regenerativo)**: é **uma política de orquestração** aplicada quando há edição; ADUC decide, monta os prompts certos e chama o especialista de vídeo.
|
| 179 |
+
* **Cut vs Continuous**: decisão do **diretor** com base em estado + metas; ADUC roteia e garante a sobreposição/remoção final.
|
| 180 |
+
|
| 181 |
+
#### 🇬🇧 How this Converses with SDR (Video)
|
| 182 |
+
|
| 183 |
+
* **Kinetic Echo**: is a **type of persisted state** consumed by the next step.
|
| 184 |
+
* **Déjà-Vu (Regenerative Cut)**: is an **orchestration policy** applied during editing; ADUC decides, crafts the right prompts, and calls the video specialist.
|
| 185 |
+
* **Cut vs Continuous**: decision made by the **director** based on state + goals; ADUC routes and ensures the final overlap/removal.
|
| 186 |
+
|
| 187 |
+
---
|
| 188 |
+
|
| 189 |
+
#### 🇧🇷 Mensagem Clara ao Usuário (Experiência)
|
| 190 |
+
|
| 191 |
+
> “Seu pedido excede o limite X do modelo Y. Em vez de truncar silenciosamente, o **ADUC** dividirá e **entregará 100%** do conteúdo por etapas coordenadas.”
|
| 192 |
+
|
| 193 |
+
Isso é diferencial prático e jurídico: **não-obviedade** por transformar limite de contexto em **pipeline controlado**, com **persistência de estado** e **avaliação iterativa**.
|
| 194 |
+
|
| 195 |
+
#### 🇬🇧 Clear User Message (Experience)
|
| 196 |
+
|
| 197 |
+
> "Your request exceeds model Y's limit X. Instead of silently truncating, **ADUC** will divide and **deliver 100%** of the content through coordinated steps."
|
| 198 |
+
|
| 199 |
+
This is a practical and legal differentiator: **non-obviousness** by transforming context limits into a **controlled pipeline**, with **state persistence** and **iterative evaluation**.
|
| 200 |
+
|
| 201 |
+
---
|
| 202 |
+
|
| 203 |
+
### Contact / Contato / Contacto
|
| 204 |
+
|
| 205 |
+
- **Author / Autor:** Carlos Rodrigues dos Santos
|
| 206 |
+
- **Email:** [email protected]
|
| 207 |
+
- **GitHub:** [https://github.com/carlex22/Aduc-sdr](https://github.com/carlex22/Aduc-sdr)
|
| 208 |
+
- **Hugging Face Spaces:**
|
| 209 |
+
- [Ltx-SuperTime-60Secondos](https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/)
|
| 210 |
+
- [Novinho](https://huggingface.co/spaces/Carlexxx/Novinho/)
|
| 211 |
+
|
| 212 |
+
---
|
aduc_framework/__init__.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/__init__.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 6.2.0 (Alinhamento Final de API da Fábrica)
|
| 6 |
+
# - Alinha a assinatura da função `create_aduc_instance` e a chamada ao
|
| 7 |
+
# construtor do AducSdr para usar `workspace_root` consistentemente.
|
| 8 |
+
# - Resolve o `TypeError` na inicialização da aplicação.
|
| 9 |
+
|
| 10 |
+
import logging
|
| 11 |
+
|
| 12 |
+
from .aduc_sdr import AducSdr
|
| 13 |
+
from .types import (
|
| 14 |
+
GenerationState,
|
| 15 |
+
PreProductionParams,
|
| 16 |
+
ProductionParams,
|
| 17 |
+
GenerationParameters,
|
| 18 |
+
MediaRef,
|
| 19 |
+
Ato,
|
| 20 |
+
Scene,
|
| 21 |
+
KeyframeData,
|
| 22 |
+
VideoData
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
# <<< CORREÇÃO APLICADA AQUI >>>
|
| 28 |
+
def create_aduc_instance(workspace_root: str) -> AducSdr:
|
| 29 |
+
"""
|
| 30 |
+
Ponto de entrada de fábrica para criar uma instância do Aduc Framework.
|
| 31 |
+
|
| 32 |
+
Args:
|
| 33 |
+
workspace_root (str): O diretório raiz onde todas as pastas de
|
| 34 |
+
projetos serão criadas.
|
| 35 |
+
"""
|
| 36 |
+
logger.info(f"Fábrica ADUC: Criando instância de AducSdr com workspace_root em '{workspace_root}'...")
|
| 37 |
+
# Passa o argumento com o nome correto para o construtor do AducSdr
|
| 38 |
+
instance = AducSdr(workspace_root=workspace_root)
|
| 39 |
+
logger.info("Fábrica ADUC: Instância AducSdr criada e pronta para uso.")
|
| 40 |
+
return instance
|
| 41 |
+
|
| 42 |
+
logger.info("Módulo 'aduc_framework' carregado. Use 'create_aduc_instance()' para começar.")
|
| 43 |
+
|
| 44 |
+
# Define a API pública do pacote principal
|
| 45 |
+
__all__ = [
|
| 46 |
+
"create_aduc_instance",
|
| 47 |
+
"AducSdr",
|
| 48 |
+
"GenerationState",
|
| 49 |
+
"PreProductionParams",
|
| 50 |
+
"ProductionParams",
|
| 51 |
+
"GenerationParameters",
|
| 52 |
+
"MediaRef",
|
| 53 |
+
"Ato",
|
| 54 |
+
"Scene",
|
| 55 |
+
"KeyframeData",
|
| 56 |
+
"VideoData",
|
| 57 |
+
]
|
aduc_framework/aduc_sdr.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/aduc_sdr.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 16.0.0 (Maestro de Produção Integrado)
|
| 4 |
+
# - Implementa o método `task_produce_movie` para delegar a produção de vídeo
|
| 5 |
+
# ao especialista `Planner5D`, ativando o pipeline de produção iterativo.
|
| 6 |
+
# - Atua como a ponte final entre a interface do usuário (UI) e o complexo
|
| 7 |
+
# fluxo de trabalho de geração de vídeo cena a cena.
|
| 8 |
+
# - Utiliza o sistema de `yield from` para transmitir de forma eficiente as
|
| 9 |
+
# atualizações de estado do `Planner5D` de volta para a UI.
|
| 10 |
+
|
| 11 |
+
import logging
|
| 12 |
+
import os
|
| 13 |
+
from typing import Generator
|
| 14 |
+
|
| 15 |
+
# Importa os especialistas de alto nível (Engenheiros)
|
| 16 |
+
from .engineers.composer_2D import composer_2d_singleton as Composer2D
|
| 17 |
+
from .engineers.planner_5D import planner_5d_singleton as Planner5D
|
| 18 |
+
|
| 19 |
+
# Importa as estruturas de dados (o DNA Digital)
|
| 20 |
+
from .types import GenerationState, PreProductionParams, ProductionParams, MediaRef
|
| 21 |
+
from .director import AducDirector
|
| 22 |
+
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
class AducSdr:
|
| 26 |
+
"""
|
| 27 |
+
O Maestro do framework ADUC-SDR. Orquestra os especialistas (Engineers)
|
| 28 |
+
e gerencia o fluxo de dados através do Diretor (estado persistente).
|
| 29 |
+
"""
|
| 30 |
+
def __init__(self, workspace_root: str):
|
| 31 |
+
self.workspace_root = workspace_root
|
| 32 |
+
self.director: AducDirector | None = None
|
| 33 |
+
self.composer_2d = Composer2D
|
| 34 |
+
self.planner_5d = Planner5D
|
| 35 |
+
logger.info("ADUC-SDR Maestro (Arquitetura V2) inicializado e pronto.")
|
| 36 |
+
|
| 37 |
+
def load_project(self, project_name: str):
|
| 38 |
+
"""Carrega um projeto existente ou cria um novo."""
|
| 39 |
+
project_path = os.path.join(self.workspace_root, project_name)
|
| 40 |
+
self.director = AducDirector(project_path=project_path)
|
| 41 |
+
logger.info(f"Projeto '{project_name}' carregado no Diretor.")
|
| 42 |
+
|
| 43 |
+
def _ensure_project_loaded(self):
|
| 44 |
+
"""Garante que um projeto foi carregado antes de executar tarefas."""
|
| 45 |
+
if not self.director:
|
| 46 |
+
raise RuntimeError("Nenhum projeto foi carregado. Chame `aduc.load_project(project_name)` primeiro.")
|
| 47 |
+
|
| 48 |
+
def get_current_state(self) -> GenerationState:
|
| 49 |
+
"""Retorna o estado completo e atual do projeto."""
|
| 50 |
+
self._ensure_project_loaded()
|
| 51 |
+
return self.director.get_full_state()
|
| 52 |
+
|
| 53 |
+
def process_image_for_story(self, image_path: str, filename: str) -> str:
|
| 54 |
+
"""Processa uma imagem de referência para um formato padrão."""
|
| 55 |
+
self._ensure_project_loaded()
|
| 56 |
+
from PIL import Image
|
| 57 |
+
size = 480; quality = 40
|
| 58 |
+
img = Image.open(image_path).convert("RGB"); img.thumbnail((size, size), Image.Resampling.LANCZOS)
|
| 59 |
+
background = Image.new('RGB', (size, size), (0, 0, 0)); img_w, img_h = img.size; offset = ((size - img_w) // 2, (size - img_h) // 2)
|
| 60 |
+
background.paste(img, offset)
|
| 61 |
+
processed_path = os.path.join(self.director.project_path, filename)
|
| 62 |
+
background.save(processed_path, 'JPEG', quality=quality)
|
| 63 |
+
return processed_path
|
| 64 |
+
|
| 65 |
+
def _process_and_yield_updates(self, generator: Generator[GenerationState, None, None]):
|
| 66 |
+
"""
|
| 67 |
+
Loop genérico para processar atualizações de um especialista, salvar o estado
|
| 68 |
+
e repassar para a UI.
|
| 69 |
+
"""
|
| 70 |
+
for updated_dna in generator:
|
| 71 |
+
self.director.load_state_from_dict(updated_dna.model_dump())
|
| 72 |
+
|
| 73 |
+
if self.director.state.should_checkpoint():
|
| 74 |
+
checkpoint_dir = os.path.join(self.director.project_path, "checkpoints")
|
| 75 |
+
path = self.director.state.create_checkpoint(checkpoint_dir)
|
| 76 |
+
logger.info(f"Checkpoint do projeto salvo em: {path}")
|
| 77 |
+
|
| 78 |
+
self.director.save_state()
|
| 79 |
+
yield self.director.get_full_state()
|
| 80 |
+
|
| 81 |
+
def task_run_story_and_keyframes(self, params: PreProductionParams) -> Generator[GenerationState, None, None]:
|
| 82 |
+
"""
|
| 83 |
+
Orquestra a pré-produção (Fase 1), delegando ao Composer2D para criar o storyboard.
|
| 84 |
+
"""
|
| 85 |
+
self._ensure_project_loaded()
|
| 86 |
+
logger.info("Maestro: Iniciando Pré-Produção (Storyboard) com o Composer2D...")
|
| 87 |
+
|
| 88 |
+
initial_state = self.director.get_full_state()
|
| 89 |
+
initial_state.parametros_geracao.pre_producao = params
|
| 90 |
+
initial_state.midias_referencia = [
|
| 91 |
+
MediaRef(id=i, tag=f"<IMG{i}>", caminho=path)
|
| 92 |
+
for i, path in enumerate(params.ref_paths)
|
| 93 |
+
]
|
| 94 |
+
|
| 95 |
+
initial_state.texto_global_historia = None
|
| 96 |
+
initial_state.ativos_catalogados = None
|
| 97 |
+
initial_state.storyboard_producao = []
|
| 98 |
+
initial_state.chat_history.append({
|
| 99 |
+
"role": "Sistema",
|
| 100 |
+
"content": f"Iniciando pré-produção. {len(params.ref_paths)} imagens de referência foram tageadas para uso pela IA."
|
| 101 |
+
})
|
| 102 |
+
|
| 103 |
+
pre_production_generator = self.composer_2d.compose_storyboard(initial_state)
|
| 104 |
+
|
| 105 |
+
yield from self._process_and_yield_updates(pre_production_generator)
|
| 106 |
+
logger.info("Maestro: Pré-Produção (Fase 1) concluída.")
|
| 107 |
+
|
| 108 |
+
def task_produce_movie(self, params: ProductionParams) -> Generator[GenerationState, None, None]:
|
| 109 |
+
"""
|
| 110 |
+
Orquestra a produção completa do filme (Fase 2), delegando ao Planner5D.
|
| 111 |
+
"""
|
| 112 |
+
self._ensure_project_loaded()
|
| 113 |
+
logger.info("Maestro: Iniciando Produção de Vídeo com o Planner5D...")
|
| 114 |
+
|
| 115 |
+
# 1. Obter o estado atual, que já contém o storyboard da pré-produção.
|
| 116 |
+
current_state = self.director.get_full_state()
|
| 117 |
+
|
| 118 |
+
# 2. Atualizar o estado com os novos parâmetros de produção vindos da UI.
|
| 119 |
+
if current_state.parametros_geracao:
|
| 120 |
+
current_state.parametros_geracao.producao = params
|
| 121 |
+
|
| 122 |
+
current_state.chat_history.append({
|
| 123 |
+
"role": "Sistema",
|
| 124 |
+
"content": f"Parâmetros de produção recebidos. O Diretor de Produção (Planner5D) está assumindo o controle."
|
| 125 |
+
})
|
| 126 |
+
|
| 127 |
+
# Salva os parâmetros no dna.json antes de iniciar o processo longo.
|
| 128 |
+
self.director.save_state()
|
| 129 |
+
|
| 130 |
+
# 3. Chamar o especialista Planner5D com o estado atualizado.
|
| 131 |
+
# Ele agora irá controlar o loop de Deformes3D e Deformes4D.
|
| 132 |
+
production_generator = self.planner_5d.produce_movie_by_scene(current_state)
|
| 133 |
+
|
| 134 |
+
# 4. Processar as atualizações, salvar o estado e repassar para a UI em tempo real.
|
| 135 |
+
yield from self._process_and_yield_updates(production_generator)
|
| 136 |
+
|
| 137 |
+
logger.info("Maestro: Produção de vídeo (Fase 2) concluída.")
|
aduc_framework/director.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/director.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 4.0.0 (Gerenciamento de Estado Persistente)
|
| 6 |
+
#
|
| 7 |
+
# - O Diretor agora gerencia um arquivo `dna.json` dentro de um diretório de projeto.
|
| 8 |
+
# - No `__init__`, ele tenta carregar um estado existente do arquivo ou cria um novo.
|
| 9 |
+
# - O método `save_state()` permite que o Maestro ADUC comande a persistência
|
| 10 |
+
# do estado atual em disco em marcos importantes do processo.
|
| 11 |
+
|
| 12 |
+
import logging
|
| 13 |
+
import os
|
| 14 |
+
import json
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from typing import List, Dict, Any
|
| 17 |
+
|
| 18 |
+
from .types import GenerationState, PreProductionParams, Scene, Ato, MediaRef
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
class AducDirector:
|
| 23 |
+
"""
|
| 24 |
+
Representa o Diretor de Cena, responsável por gerenciar o estado
|
| 25 |
+
persistente de um único projeto (`dna.json`).
|
| 26 |
+
"""
|
| 27 |
+
def __init__(self, project_path: str):
|
| 28 |
+
self.project_path = Path(project_path)
|
| 29 |
+
self.dna_file_path = self.project_path / "dna.json"
|
| 30 |
+
|
| 31 |
+
self.project_path.mkdir(parents=True, exist_ok=True)
|
| 32 |
+
|
| 33 |
+
self.state: GenerationState = self._load_or_initialize_state()
|
| 34 |
+
logger.info(f"AducDirector inicializado para o projeto em '{self.project_path}'.")
|
| 35 |
+
|
| 36 |
+
def _load_or_initialize_state(self) -> GenerationState:
|
| 37 |
+
"""Carrega o estado do dna.json ou cria um novo se não existir."""
|
| 38 |
+
if self.dna_file_path.exists():
|
| 39 |
+
try:
|
| 40 |
+
logger.info(f"Encontrado dna.json existente. Carregando estado...")
|
| 41 |
+
with open(self.dna_file_path, 'r', encoding='utf-8') as f:
|
| 42 |
+
state_dict = json.load(f)
|
| 43 |
+
return GenerationState(**state_dict)
|
| 44 |
+
except (json.JSONDecodeError, TypeError) as e:
|
| 45 |
+
logger.error(f"Falha ao carregar ou validar dna.json: {e}. Criando um novo estado.")
|
| 46 |
+
return GenerationState(workspace_dir=str(self.project_path))
|
| 47 |
+
else:
|
| 48 |
+
logger.info("Nenhum dna.json encontrado. Criando novo estado de geração.")
|
| 49 |
+
return GenerationState(workspace_dir=str(self.project_path))
|
| 50 |
+
|
| 51 |
+
def save_state(self):
|
| 52 |
+
"""Salva o estado atual (self.state) no arquivo dna.json."""
|
| 53 |
+
logger.info(f"Persistindo estado atual para '{self.dna_file_path}'...")
|
| 54 |
+
with open(self.dna_file_path, 'w', encoding='utf-8') as f:
|
| 55 |
+
f.write(self.state.model_dump_json(indent=2))
|
| 56 |
+
logger.info("Estado salvo com sucesso.")
|
| 57 |
+
|
| 58 |
+
def get_full_state(self) -> GenerationState:
|
| 59 |
+
"""Retorna o objeto de estado Pydantic completo."""
|
| 60 |
+
return self.state
|
| 61 |
+
|
| 62 |
+
def load_state_from_dict(self, state_dict: Dict[str, Any]):
|
| 63 |
+
"""Carrega o estado a partir de um dicionário (ex: vindo de um planner)."""
|
| 64 |
+
try:
|
| 65 |
+
self.state = GenerationState(**state_dict)
|
| 66 |
+
logger.info("Diretor: Estado em memória atualizado com sucesso a partir de um dicionário.")
|
| 67 |
+
except Exception as e:
|
| 68 |
+
logger.error(f"Diretor: Falha ao carregar estado a partir do dicionário: {e}", exc_info=True)
|
| 69 |
+
|
| 70 |
+
def update_parameters(self, stage: str, params: Any):
|
| 71 |
+
"""Atualiza o nó de parâmetros no estado de geração."""
|
| 72 |
+
if hasattr(self.state.parametros_geracao, stage):
|
| 73 |
+
setattr(self.state.parametros_geracao, stage, params)
|
| 74 |
+
else:
|
| 75 |
+
logger.warning(f"Tentativa de atualizar parâmetros para um estágio desconhecido: '{stage}'")
|
| 76 |
+
|
| 77 |
+
def update_state_from_pre_production_dna(self, params: PreProductionParams, final_dna_json: Dict[str, Any]):
|
| 78 |
+
"""Analisa o dicionário bruto do Composer e o usa para popular o estado."""
|
| 79 |
+
logger.info("Diretor: Recebendo DNA da pré-produção para popular o estado.")
|
| 80 |
+
|
| 81 |
+
self.state.parametros_geracao.pre_producao = params
|
| 82 |
+
self.state.prompt_geral = final_dna_json.get("global_prompt", "")
|
| 83 |
+
|
| 84 |
+
ref_paths = final_dna_json.get("initial_media_paths", [])
|
| 85 |
+
self.state.midias_referencia = [MediaRef(id=i, caminho=path) for i, path in enumerate(ref_paths)]
|
| 86 |
+
|
| 87 |
+
self.state.scenes = []
|
| 88 |
+
scenes_data = final_dna_json.get("scenes", [])
|
| 89 |
+
|
| 90 |
+
for scene_dict in scenes_data:
|
| 91 |
+
ato_objects = [
|
| 92 |
+
Ato(id=ato_dict.get("act_id", i), resumo_ato=ato_dict.get("context", ""))
|
| 93 |
+
for i, ato_dict in enumerate(scene_dict.get("acts", []))
|
| 94 |
+
]
|
| 95 |
+
scene_object = Scene(id=scene_dict.get("scene_id"), atos=ato_objects)
|
| 96 |
+
self.state.scenes.append(scene_object)
|
| 97 |
+
|
| 98 |
+
logger.info(f"Diretor: Estado populado com sucesso. {len(self.state.scenes)} cenas carregadas.")
|
aduc_framework/engineers/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR para geração de vídeo coerente.
|
| 2 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 3 |
+
#
|
| 4 |
+
# Contato:
|
| 5 |
+
# Carlos Rodrigues dos Santos
|
| 6 | |
| 7 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 8 |
+
#
|
| 9 |
+
# Repositórios e Projetos Relacionados:
|
| 10 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 11 |
+
#
|
| 12 |
+
# This program is free software: you can redistribute it and/or modify
|
| 13 |
+
# it under the terms of the GNU Affero General Public License as published by
|
| 14 |
+
# the Free Software Foundation, either version 3 of the License, or
|
| 15 |
+
# (at your option) any later version.
|
| 16 |
+
#
|
| 17 |
+
# This program is distributed in the hope that it will be useful,
|
| 18 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 19 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 20 |
+
# GNU Affero General Public License for more details.
|
| 21 |
+
#
|
| 22 |
+
# You should have received a copy of the GNU Affero General Public License
|
| 23 |
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
aduc_framework/engineers/NOTICE.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTICE
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2025 Carlos Rodrigues dos Santos. All rights reserved.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Aviso de Propriedade Intelectual e Licenciamento
|
| 8 |
+
|
| 9 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 10 |
+
|
| 11 |
+
O método e o sistema de orquestração de prompts denominados **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste documento e implementados neste software, estão atualmente em processo de patenteamento.
|
| 12 |
+
|
| 13 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, incluindo, mas não se limitando a:
|
| 14 |
+
|
| 15 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 16 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 17 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 18 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 19 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 20 |
+
|
| 21 |
+
### **Reconhecimento e Implicações (EM PORTUGUÊS):**
|
| 22 |
+
|
| 23 |
+
Ao acessar ou utilizar este software e a arquitetura ADUC aqui implementada, você reconhece:
|
| 24 |
+
|
| 25 |
+
1. A natureza inovadora e a importância da arquitetura ADUC no campo da orquestração de prompts para IA.
|
| 26 |
+
2. Que a essência desta arquitetura, ou suas implementações derivadas, podem estar sujeitas a direitos de propriedade intelectual, incluindo patentes.
|
| 27 |
+
3. Que o uso comercial, a reprodução da lógica central da ADUC em sistemas independentes, ou a exploração direta da invenção sem o devido licenciamento podem infringir os direitos de patente pendente.
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
### **Patent Pending (IN ENGLISH):**
|
| 32 |
+
|
| 33 |
+
The method and system for prompt orchestration named **ADUC (Automated Discovery and Orchestration of Complex tasks)**, as described herein and implemented in this software, are currently in the process of being patented.
|
| 34 |
+
|
| 35 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 36 |
+
|
| 37 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 38 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 39 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 40 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 41 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 42 |
+
|
| 43 |
+
### **Acknowledgement and Implications (IN ENGLISH):**
|
| 44 |
+
|
| 45 |
+
By accessing or using this software and the ADUC architecture implemented herein, you acknowledge:
|
| 46 |
+
|
| 47 |
+
1. The innovative nature and significance of the ADUC architecture in the field of AI prompt orchestration.
|
| 48 |
+
2. That the essence of this architecture, or its derivative implementations, may be subject to intellectual property rights, including patents.
|
| 49 |
+
3. That commercial use, reproduction of ADUC's core logic in independent systems, or direct exploitation of the invention without proper licensing may infringe upon pending patent rights.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
## Licença AGPLv3
|
| 54 |
+
|
| 55 |
+
This program is free software: you can redistribute it and/or modify
|
| 56 |
+
it under the terms of the GNU Affero General Public License as published by
|
| 57 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 58 |
+
(at your option) any later version.
|
| 59 |
+
|
| 60 |
+
This program is distributed in the hope that it will be useful,
|
| 61 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 62 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 63 |
+
GNU Affero General Public License for more details.
|
| 64 |
+
|
| 65 |
+
You should have received a copy of the GNU Affero General Public License
|
| 66 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
**Contato para Consultas:**
|
| 71 |
+
|
| 72 |
+
Para mais informações sobre a arquitetura ADUC, o status do patenteamento, ou para discutir licenciamento para usos comerciais ou não conformes com a AGPLv3, por favor, entre em contato:
|
| 73 |
+
|
| 74 |
+
Carlos Rodrigues dos Santos
|
| 75 | |
| 76 |
+
Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
aduc_framework/engineers/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Euia-AducSdr
|
| 3 |
+
emoji: 🎥
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
app_file: app.py
|
| 8 |
+
pinned: true
|
| 9 |
+
license: agpl-3.0
|
| 10 |
+
short_description: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
### 🇧🇷 Português
|
| 15 |
+
|
| 16 |
+
Uma implementação aberta e funcional da arquitetura ADUC-SDR (Arquitetura de Unificação Compositiva - Escala Dinâmica e Resiliente), projetada para a geração de vídeo coerente de longa duração. Este projeto materializa os princípios de fragmentação, navegação geométrica e um mecanismo de "eco causal 4bits memoria" para garantir a continuidade física e narrativa em sequências de vídeo geradas por múltiplos modelos de IA.
|
| 17 |
+
|
| 18 |
+
**Licença:** Este projeto é licenciado sob os termos da **GNU Affero General Public License v3.0**. Isto significa que se você usar este software (ou qualquer trabalho derivado) para fornecer um serviço através de uma rede, você é **obrigado a disponibilizar o código-fonte completo** da sua versão para os usuários desse serviço.
|
| 19 |
+
|
| 20 |
+
- **Copyright (C) 4 de Agosto de 2025, Carlos Rodrigues dos Santos**
|
| 21 |
+
- Uma cópia completa da licença pode ser encontrada no arquivo [LICENSE](LICENSE).
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
### 🇬🇧 English
|
| 26 |
+
|
| 27 |
+
An open and functional implementation of the ADUC-SDR (Architecture for Compositive Unification - Dynamic and Resilient Scaling) architecture, designed for long-form coherent video generation. This project materializes the principles of fragmentation, geometric navigation, and a "causal echo 4bits memori" mechanism to ensure physical and narrative continuity in video sequences generated by multiple AI models.
|
| 28 |
+
|
| 29 |
+
**License:** This project is licensed under the terms of the **GNU Affero General Public License v3.0**. This means that if you use this software (or any derivative work) to provide a service over a network, you are **required to make the complete source code** of your version available to the users of that service.
|
| 30 |
+
|
| 31 |
+
- **Copyright (C) August 4, 2025, Carlos Rodrigues dos Santos**
|
| 32 |
+
- A full copy of the license can be found in the [LICENSE](LICENSE) file.
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
## **Aviso de Propriedade Intelectual e Patenteamento**
|
| 37 |
+
|
| 38 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 39 |
+
|
| 40 |
+
A arquitetura e o método **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste projeto e nas reivindicações associadas, estão **atualmente em processo de patenteamento**.
|
| 41 |
+
|
| 42 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, que incluem, mas não se limitam a:
|
| 43 |
+
|
| 44 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 45 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 46 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 47 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 48 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 49 |
+
|
| 50 |
+
Ao utilizar este software e a arquitetura ADUC aqui implementada, você reconhece a natureza inovadora desta arquitetura e que a **reprodução ou exploração da lógica central da ADUC em sistemas independentes pode infringir direitos de patente pendente.**
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
### **Patent Pending (IN ENGLISH):**
|
| 55 |
+
|
| 56 |
+
The **ADUC (Automated Discovery and Orchestration of Complex tasks)** architecture and method, as described in this project and its associated claims, are **currently in the process of being patented.**
|
| 57 |
+
|
| 58 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 59 |
+
|
| 60 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 61 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 62 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 63 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 64 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 65 |
+
|
| 66 |
+
By using this software and the ADUC architecture implemented herein, you acknowledge the innovative nature of this architecture and that **the reproduction or exploitation of ADUC's core logic in independent systems may infringe upon pending patent rights.**
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
### Detalhes Técnicos e Reivindicações da ADUC
|
| 71 |
+
|
| 72 |
+
#### 🇧🇷 Definição Curta (para Tese e Patente)
|
| 73 |
+
|
| 74 |
+
**ADUC** é um *framework pré-input* e *intermediário* de **gerenciamento de prompts** que:
|
| 75 |
+
|
| 76 |
+
1. **fragmenta** solicitações acima do limite de contexto de qualquer modelo,
|
| 77 |
+
2. **escala linearmente** (processo sequencial com memória persistida),
|
| 78 |
+
3. **distribui** sub-tarefas a **especialistas** (modelos/ferramentas heterogêneos), e
|
| 79 |
+
4. **realimenta** a próxima etapa com avaliação do que foi feito/esperado (LLM diretor).
|
| 80 |
+
|
| 81 |
+
Não é um modelo; é uma **camada orquestradora** plugável antes do input de modelos existentes (texto, imagem, áudio, vídeo), usando *tokens universais* e a tecnologia atual.
|
| 82 |
+
|
| 83 |
+
#### 🇬🇧 Short Definition (for Thesis and Patent)
|
| 84 |
+
|
| 85 |
+
**ADUC** is a *pre-input* and *intermediate* **prompt management framework** that:
|
| 86 |
+
|
| 87 |
+
1. **fragments** requests exceeding any model's context limit,
|
| 88 |
+
2. **scales linearly** (sequential process with persisted memory),
|
| 89 |
+
3. **distributes** sub-tasks to **specialists** (heterogeneous models/tools), and
|
| 90 |
+
4. **feeds back** to the next step with an evaluation of what was done/expected (director LLM).
|
| 91 |
+
|
| 92 |
+
It is not a model; it is a pluggable **orchestration layer** before the input of existing models (text, image, audio, video), using *universal tokens* and current technology.
|
| 93 |
+
|
| 94 |
+
---
|
| 95 |
+
|
| 96 |
+
#### 🇧🇷 Elementos Essenciais (Telegráfico)
|
| 97 |
+
|
| 98 |
+
* **Agnóstico a modelos:** opera com qualquer LLM/difusor/API.
|
| 99 |
+
* **Pré-input manager:** recebe pedido do usuário, **divide** em blocos ≤ limite de tokens, **prioriza**, **agenda** e **roteia**.
|
| 100 |
+
* **Memória persistida:** resultados/latentes/“eco” viram **estado compartilhado** para o próximo bloco (nada é ignorado).
|
| 101 |
+
* **Especialistas:** *routers* decidem quem faz o quê (ex.: “descrição → LLM-A”, “keyframe → Img-B”, “vídeo → Vid-C”).
|
| 102 |
+
* **Controle de qualidade:** LLM diretor compara *o que fez* × *o que deveria* × *o que falta* e **regenera objetivos** do próximo fragmento.
|
| 103 |
+
* **Custo/latência-aware:** planeja pela **VRAM/tempo/custo**, não tenta “abraçar tudo de uma vez”.
|
| 104 |
+
|
| 105 |
+
#### 🇬🇧 Essential Elements (Telegraphic)
|
| 106 |
+
|
| 107 |
+
* **Model-agnostic:** operates with any LLM/diffuser/API.
|
| 108 |
+
* **Pre-input manager:** receives user request, **divides** into blocks ≤ token limit, **prioritizes**, **schedules**, and **routes**.
|
| 109 |
+
* **Persisted memory:** results/latents/“echo” become **shared state** for the next block (nothing is ignored).
|
| 110 |
+
* **Specialists:** *routers* decide who does what (e.g., “description → LLM-A”, “keyframe → Img-B”, “video → Vid-C”).
|
| 111 |
+
* **Quality control:** director LLM compares *what was done* × *what should be done* × *what is missing* and **regenerates objectives** for the next fragment.
|
| 112 |
+
* **Cost/latency-aware:** plans by **VRAM/time/cost**, does not try to “embrace everything at once”.
|
| 113 |
+
|
| 114 |
+
---
|
| 115 |
+
|
| 116 |
+
#### 🇧🇷 Reivindicações Independentes (Método e Sistema)
|
| 117 |
+
|
| 118 |
+
**Reivindicação Independente (Método) — Versão Enxuta:**
|
| 119 |
+
|
| 120 |
+
1. **Método** de **orquestração de prompts** para execução de tarefas acima do limite de contexto de modelos de IA, compreendendo:
|
| 121 |
+
(a) **receber** uma solicitação que excede um limite de tokens;
|
| 122 |
+
(b) **analisar** a solicitação por um **LLM diretor** e **fragmentá-la** em sub-tarefas ≤ limite;
|
| 123 |
+
(c) **selecionar** especialistas de execução para cada sub-tarefa com base em capacidades declaradas;
|
| 124 |
+
(d) **gerar** prompts específicos por sub-tarefa em **tokens universais**, incluindo referências ao **estado persistido** de execuções anteriores;
|
| 125 |
+
(e) **executar sequencialmente** as sub-tarefas e **persistir** suas saídas como memória (incluindo latentes/eco/artefatos);
|
| 126 |
+
(f) **avaliar** automaticamente a saída versus metas declaradas e **regenerar objetivos** do próximo fragmento;
|
| 127 |
+
(g) **iterar** (b)–(f) até que os critérios de completude sejam atendidos, produzindo o resultado agregado;
|
| 128 |
+
em que o framework **escala linearmente** no tempo e armazenamento físico, **independente** da janela de contexto dos modelos subjacentes.
|
| 129 |
+
|
| 130 |
+
**Reivindicação Independente (Sistema):**
|
| 131 |
+
|
| 132 |
+
2. **Sistema** de orquestração de prompts, compreendendo: um **planejador LLM diretor**; um **roteador de especialistas**; um **banco de estado persistido** (incl. memória cinética para vídeo); um **gerador de prompts universais**; e um **módulo de avaliação/realimentação**, acoplados por uma **API pré-input** a modelos heterogêneos.
|
| 133 |
+
|
| 134 |
+
#### 🇬🇧 Independent Claims (Method and System)
|
| 135 |
+
|
| 136 |
+
**Independent Claim (Method) — Concise Version:**
|
| 137 |
+
|
| 138 |
+
1. A **method** for **prompt orchestration** for executing tasks exceeding AI model context limits, comprising:
|
| 139 |
+
(a) **receiving** a request that exceeds a token limit;
|
| 140 |
+
(b) **analyzing** the request by a **director LLM** and **fragmenting it** into sub-tasks ≤ the limit;
|
| 141 |
+
(c) **selecting** execution specialists for each sub-task based on declared capabilities;
|
| 142 |
+
(d) **generating** specific prompts per sub-task in **universal tokens**, including references to the **persisted state** of previous executions;
|
| 143 |
+
(e) **sequentially executing** the sub-tasks and **persisting** their outputs as memory (including latents/echo/artifacts);
|
| 144 |
+
(f) **automatically evaluating** the output against declared goals and **regenerating objectives** for the next fragment;
|
| 145 |
+
(g) **iterating** (b)–(f) until completion criteria are met, producing the aggregated result;
|
| 146 |
+
wherein the framework **scales linearly** in time and physical storage, **independent** of the context window of the underlying models.
|
| 147 |
+
|
| 148 |
+
**Independent Claim (System):**
|
| 149 |
+
|
| 150 |
+
2. A prompt orchestration **system**, comprising: a **director LLM planner**; a **specialist router**; a **persisted state bank** (incl. kinetic memory for video); a **universal prompt generator**; and an **evaluation/feedback module**, coupled via a **pre-input API** to heterogeneous models.
|
| 151 |
+
|
| 152 |
+
---
|
| 153 |
+
|
| 154 |
+
#### 🇧🇷 Dependentes Úteis
|
| 155 |
+
|
| 156 |
+
* (3) Onde o roteamento considera **custo/latência/VRAM** e metas de qualidade.
|
| 157 |
+
* (4) Onde o banco de estado inclui **eco cinético** para vídeo (últimos *n* frames/latentes/fluxo).
|
| 158 |
+
* (5) Onde a avaliação usa métricas específicas por domínio (Lflow, consistência semântica, etc.).
|
| 159 |
+
* (6) Onde *tokens universais* padronizam instruções entre especialistas.
|
| 160 |
+
* (7) Onde a orquestração decide **cut vs continuous** e **corte regenerativo** (Déjà-Vu) ao editar vídeo.
|
| 161 |
+
* (8) Onde o sistema **nunca descarta** conteúdo excedente: **reagenda** em novos fragmentos.
|
| 162 |
+
|
| 163 |
+
#### 🇬🇧 Useful Dependents
|
| 164 |
+
|
| 165 |
+
* (3) Wherein routing considers **cost/latency/VRAM** and quality goals.
|
| 166 |
+
* (4) Wherein the state bank includes **kinetic echo** for video (last *n* frames/latents/flow).
|
| 167 |
+
* (5) Wherein evaluation uses domain-specific metrics (Lflow, semantic consistency, etc.).
|
| 168 |
+
* (6) Wherein *universal tokens* standardize instructions between specialists.
|
| 169 |
+
* (7) Wherein orchestration decides **cut vs continuous** and **regenerative cut** (Déjà-Vu) when editing video.
|
| 170 |
+
* (8) Wherein the system **never discards** excess content: it **reschedules** it in new fragments.
|
| 171 |
+
|
| 172 |
+
---
|
| 173 |
+
|
| 174 |
+
#### 🇧🇷 Como isso conversa com SDR (Vídeo)
|
| 175 |
+
|
| 176 |
+
* **Eco Cinético**: é um **tipo de estado persistido** consumido pelo próximo passo.
|
| 177 |
+
* **Déjà-Vu (Corte Regenerativo)**: é **uma política de orquestração** aplicada quando há edição; ADUC decide, monta os prompts certos e chama o especialista de vídeo.
|
| 178 |
+
* **Cut vs Continuous**: decisão do **diretor** com base em estado + metas; ADUC roteia e garante a sobreposição/remoção final.
|
| 179 |
+
|
| 180 |
+
#### 🇬🇧 How this Converses with SDR (Video)
|
| 181 |
+
|
| 182 |
+
* **Kinetic Echo**: is a **type of persisted state** consumed by the next step.
|
| 183 |
+
* **Déjà-Vu (Regenerative Cut)**: is an **orchestration policy** applied during editing; ADUC decides, crafts the right prompts, and calls the video specialist.
|
| 184 |
+
* **Cut vs Continuous**: decision made by the **director** based on state + goals; ADUC routes and ensures the final overlap/removal.
|
| 185 |
+
|
| 186 |
+
---
|
| 187 |
+
|
| 188 |
+
#### 🇧🇷 Mensagem Clara ao Usuário (Experiência)
|
| 189 |
+
|
| 190 |
+
> “Seu pedido excede o limite X do modelo Y. Em vez de truncar silenciosamente, o **ADUC** dividirá e **entregará 100%** do conteúdo por etapas coordenadas.”
|
| 191 |
+
|
| 192 |
+
Isso é diferencial prático e jurídico: **não-obviedade** por transformar limite de contexto em **pipeline controlado**, com **persistência de estado** e **avaliação iterativa**.
|
| 193 |
+
|
| 194 |
+
#### 🇬🇧 Clear User Message (Experience)
|
| 195 |
+
|
| 196 |
+
> "Your request exceeds model Y's limit X. Instead of silently truncating, **ADUC** will divide and **deliver 100%** of the content through coordinated steps."
|
| 197 |
+
|
| 198 |
+
This is a practical and legal differentiator: **non-obviousness** by transforming context limits into a **controlled pipeline**, with **state persistence** and **iterative evaluation**.
|
| 199 |
+
|
| 200 |
+
---
|
| 201 |
+
|
| 202 |
+
### Contact / Contato / Contacto
|
| 203 |
+
|
| 204 |
+
- **Author / Autor:** Carlos Rodrigues dos Santos
|
| 205 |
+
- **Email:** [email protected]
|
| 206 |
+
- **GitHub:** [https://github.com/carlex22/Aduc-sdr](https://github.com/carlex22/Aduc-sdr)
|
| 207 |
+
- **Hugging Face Spaces:**
|
| 208 |
+
- [Ltx-SuperTime-60Secondos](https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/)
|
| 209 |
+
- [Novinho](https://huggingface.co/spaces/Carlexxx/Novinho/)
|
| 210 |
+
|
| 211 |
+
---
|
aduc_framework/engineers/__init__.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/__init__.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Expõe os singletons de todos os engenheiros (Planners e Deformes)
|
| 6 |
+
# para que o orquestrador e outros componentes possam importá-los
|
| 7 |
+
# a partir de um único local.
|
| 8 |
+
|
| 9 |
+
# Importa os Planners (Gerentes)
|
| 10 |
+
|
| 11 |
+
from .planner_3d import planner_3d_singleton
|
| 12 |
+
from .planner_4d import planner_4d_singleton
|
| 13 |
+
from .planner_5D import planner_5d_singleton
|
| 14 |
+
|
| 15 |
+
# Importa o Composer (Motor de Execução de LLM)
|
| 16 |
+
from .composer import composer_singleton
|
| 17 |
+
from .composer_2D import composer_2d_singleton
|
| 18 |
+
from .neura_link import neura_link_singleton
|
| 19 |
+
|
| 20 |
+
# Importa os Motores de Geração (Trabalhadores Especialistas)
|
| 21 |
+
from .deformes3D import deformes3d_engine_singleton
|
| 22 |
+
from .deformes4D import deformes4d_engine_singleton
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Define a lista de componentes que são parte da "API pública" deste sub-pacote
|
| 26 |
+
__all__ = [
|
| 27 |
+
"planner_5d_singleton",
|
| 28 |
+
"neura_link_singleton",
|
| 29 |
+
"composer_2d_singleton",
|
| 30 |
+
"planner_3d_singleton",
|
| 31 |
+
"planner_4d_singleton",
|
| 32 |
+
"composer_singleton",
|
| 33 |
+
"deformes3d_engine_singleton",
|
| 34 |
+
"Deformes4DEngine",
|
| 35 |
+
]
|
aduc_framework/engineers/composer.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/composer.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 4.1.0 (Hub Cognitivo Consolidado)
|
| 6 |
+
#
|
| 7 |
+
# - A arquitetura está estável e completa. O Composer atua como o hub
|
| 8 |
+
# central de comunicação com os LLMs.
|
| 9 |
+
# - O método `execute_plan` é usado pelo Planner2D para a criação do roteiro.
|
| 10 |
+
# - O método `execute_cognitive_task` é uma ferramenta genérica usada por
|
| 11 |
+
# outros especialistas (como Planner4D e Deformes3D) para solicitar
|
| 12 |
+
# tarefas de raciocínio pontuais ao LLM, como decidir um movimento de
|
| 13 |
+
# câmera ou um plano de composição de imagem.
|
| 14 |
+
|
| 15 |
+
import logging
|
| 16 |
+
import json
|
| 17 |
+
import re
|
| 18 |
+
import yaml
|
| 19 |
+
from pathlib import Path
|
| 20 |
+
from PIL import Image
|
| 21 |
+
from typing import List, Dict, Any, Generator, Optional, Callable
|
| 22 |
+
|
| 23 |
+
from .prompt_engine import prompt_engine_singleton
|
| 24 |
+
from ..managers.llama_multimodal_manager import llama_multimodal_manager_singleton
|
| 25 |
+
from ..managers.gemini_manager import gemini_manager_singleton
|
| 26 |
+
|
| 27 |
+
logger = logging.getLogger(__name__)
|
| 28 |
+
|
| 29 |
+
def robust_json_parser(raw_text: str) -> dict:
|
| 30 |
+
"""
|
| 31 |
+
Analisa um objeto JSON de uma string que pode conter texto extra.
|
| 32 |
+
"""
|
| 33 |
+
logger.debug(f"COMPOSER(JSON_PARSER): Tentando parsear JSON (primeiros 500 chars):\n---\n{raw_text[:500]}\n---")
|
| 34 |
+
match = re.search(r'```json\s*(\{.*?\})\s*```', raw_text, re.DOTALL)
|
| 35 |
+
if match:
|
| 36 |
+
json_str = match.group(1); logger.debug("JSON explícito encontrado.")
|
| 37 |
+
return json.loads(json_str)
|
| 38 |
+
try:
|
| 39 |
+
start_index = raw_text.find('{'); end_index = raw_text.rfind('}')
|
| 40 |
+
if start_index != -1 and end_index != -1 and end_index > start_index:
|
| 41 |
+
json_str = raw_text[start_index : end_index + 1]; logger.debug("JSON por delimitadores '{...}' encontrado.")
|
| 42 |
+
return json.loads(json_str)
|
| 43 |
+
except json.JSONDecodeError: pass
|
| 44 |
+
logger.warning("Nenhum JSON válido encontrado nos métodos primários. Tentando parsear o texto inteiro.")
|
| 45 |
+
return json.loads(raw_text)
|
| 46 |
+
|
| 47 |
+
class Composer:
|
| 48 |
+
"""
|
| 49 |
+
O Composer é o hub central de comunicação com o Large Language Model (LLM).
|
| 50 |
+
Ele executa tanto planos de trabalho de várias etapas quanto tarefas cognitivas únicas.
|
| 51 |
+
"""
|
| 52 |
+
def __init__(self):
|
| 53 |
+
logger.info("COMPOSER: Lendo config.yaml para selecionar o LLM Engine...")
|
| 54 |
+
with open("config.yaml", 'r') as f:
|
| 55 |
+
config = yaml.safe_load(f)
|
| 56 |
+
|
| 57 |
+
self.provider = config.get('specialists', {}).get('llm_engine', {}).get('provider', 'llama_multimodal')
|
| 58 |
+
|
| 59 |
+
if self.provider == 'gemini':
|
| 60 |
+
self.llm_manager = gemini_manager_singleton
|
| 61 |
+
logger.info("COMPOSER: Motor de LLM configurado para usar 'Gemini'.")
|
| 62 |
+
else:
|
| 63 |
+
self.llm_manager = llama_multimodal_manager_singleton
|
| 64 |
+
logger.info("COMPOSER: Motor de LLM configurado para usar 'Llama' (padrão).")
|
| 65 |
+
|
| 66 |
+
prompt_engine_singleton.set_provider(self.provider)
|
| 67 |
+
self.task_templates = self._load_task_templates()
|
| 68 |
+
logger.info(f"Composer inicializado com {len(self.task_templates)} templates de tarefa.")
|
| 69 |
+
|
| 70 |
+
def _load_task_templates(self) -> Dict[str, str]:
|
| 71 |
+
templates = {}
|
| 72 |
+
template_dir = Path(__file__).resolve().parent.parent / "prompts" / "task_templates"
|
| 73 |
+
if not template_dir.is_dir():
|
| 74 |
+
raise FileNotFoundError(f"Diretório de templates de tarefa não encontrado: {template_dir}")
|
| 75 |
+
for task_file in template_dir.glob("*.txt"):
|
| 76 |
+
task_id = task_file.stem
|
| 77 |
+
with open(task_file, 'r', encoding='utf-8') as f:
|
| 78 |
+
templates[task_id] = f.read()
|
| 79 |
+
return templates
|
| 80 |
+
|
| 81 |
+
def _talk_to_llm(self, generic_prompt: str, images: Optional[List[Image.Image]] = None, expected_format="text") -> Any:
|
| 82 |
+
final_model_prompt = prompt_engine_singleton.translate(
|
| 83 |
+
generic_prompt_content=generic_prompt, has_image=bool(images)
|
| 84 |
+
)
|
| 85 |
+
logger.info(f"COMPOSER: PROMPT FINAL SENDO ENVIADO para ({self.provider}):\n--- INÍCIO DO PROMPT ---\n{final_model_prompt}\n--- FIM DO PROMPT ---")
|
| 86 |
+
response_raw = self.llm_manager.process_turn(prompt_text=final_model_prompt, image_list=images)
|
| 87 |
+
logger.info(f"COMPOSER: RESPOSTA BRUTA RECEBIDA de ({self.provider}):\n--- INÍCIO DA RESPOSTA BRUTA ---\n{response_raw}\n--- FIM DA RESPOSTA BRUTA ---")
|
| 88 |
+
|
| 89 |
+
if expected_format == "json":
|
| 90 |
+
try:
|
| 91 |
+
return robust_json_parser(response_raw)
|
| 92 |
+
except (json.JSONDecodeError, ValueError) as e:
|
| 93 |
+
raise ValueError(f"O LLM ({self.provider}) retornou um formato JSON inválido. Erro: {e}")
|
| 94 |
+
return response_raw
|
| 95 |
+
|
| 96 |
+
def execute_cognitive_task(self, task_id: str, template_data: Dict[str, Any], images: Optional[List[Image.Image]] = None) -> Any:
|
| 97 |
+
"""
|
| 98 |
+
Executa uma única tarefa cognitiva (de "pensamento") e retorna o resultado.
|
| 99 |
+
"""
|
| 100 |
+
logger.info(f"COMPOSER: Executando tarefa cognitiva: {task_id}")
|
| 101 |
+
generic_template = self.task_templates.get(task_id)
|
| 102 |
+
if not generic_template:
|
| 103 |
+
raise ValueError(f"Template para a tarefa cognitiva '{task_id}' não foi encontrado.")
|
| 104 |
+
|
| 105 |
+
prompt_content = generic_template
|
| 106 |
+
for key, value in template_data.items():
|
| 107 |
+
prompt_content = prompt_content.replace(f"{{{key}}}", str(value))
|
| 108 |
+
|
| 109 |
+
expected_format = "json" if "JSON REQUIRED" in generic_template.upper() else "text"
|
| 110 |
+
|
| 111 |
+
response = self._talk_to_llm(generic_prompt=prompt_content, images=images, expected_format=expected_format)
|
| 112 |
+
|
| 113 |
+
if expected_format == "text":
|
| 114 |
+
return response.strip().replace("\"", "")
|
| 115 |
+
return response
|
| 116 |
+
|
| 117 |
+
def execute_plan(self, execution_plan: List[Dict[str, Any]], initial_data: Dict[str, Any]) -> Generator[Dict[str, Any], None, None]:
|
| 118 |
+
"""
|
| 119 |
+
Executa um plano de trabalho de várias etapas para a criação do roteiro.
|
| 120 |
+
"""
|
| 121 |
+
dna = {"global_prompt": initial_data["global_prompt"], "initial_media_paths": initial_data["user_media_paths"], "continuous_story": "", "scenes": []}
|
| 122 |
+
user_media = [Image.open(p) for p in initial_data["user_media_paths"]]
|
| 123 |
+
|
| 124 |
+
for i, task in enumerate(execution_plan):
|
| 125 |
+
try:
|
| 126 |
+
task_id = task['task_id']
|
| 127 |
+
yield {"status": "progress", "message": task.get('description', '')}
|
| 128 |
+
|
| 129 |
+
generic_template = self.task_templates.get(task_id)
|
| 130 |
+
if not generic_template:
|
| 131 |
+
raise ValueError(f"Template para a tarefa '{task_id}' não foi encontrado.")
|
| 132 |
+
|
| 133 |
+
prompt_content = generic_template
|
| 134 |
+
prompt_content = prompt_content.replace("{global_prompt}", str(dna.get("global_prompt", "")))
|
| 135 |
+
prompt_content = prompt_content.replace("{num_scenes}", str(task.get('inputs', {}).get("num_scenes", "")))
|
| 136 |
+
prompt_content = prompt_content.replace("{continuous_story}", str(dna.get("continuous_story", "")))
|
| 137 |
+
prompt_content = prompt_content.replace("{independent_scenes_json}", json.dumps({"scenes": dna.get("scenes", [])}, indent=2))
|
| 138 |
+
|
| 139 |
+
is_json_output = task_id in ["STEP_02_CREATE_INDEPENDENT_SCENES", "STEP_03_FRAGMENT_SCENES_INTO_ACTS", "STEP_04_FINAL_REVIEW"]
|
| 140 |
+
expected_format = "json" if is_json_output else "text"
|
| 141 |
+
|
| 142 |
+
response = self._talk_to_llm(prompt_content, user_media if i == 0 else None, expected_format)
|
| 143 |
+
|
| 144 |
+
if task_id == "STEP_01_CREATE_CONTINUOUS_STORY":
|
| 145 |
+
dna["continuous_story"] = response
|
| 146 |
+
elif task_id == "STEP_02_CREATE_INDEPENDENT_SCENES":
|
| 147 |
+
dna["scenes"] = response.get("scenes", [])
|
| 148 |
+
elif task_id == "STEP_03_FRAGMENT_SCENES_INTO_ACTS":
|
| 149 |
+
dna["scenes"] = response.get("scenes", [])
|
| 150 |
+
|
| 151 |
+
except Exception as e:
|
| 152 |
+
raise e
|
| 153 |
+
|
| 154 |
+
yield {"status": "complete", "message": "Execução do Composer concluída.", "dna": dna}
|
| 155 |
+
|
| 156 |
+
composer_singleton = Composer()
|
aduc_framework/engineers/composer_2D.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/composer_2D.py
|
| 2 |
+
|
| 3 |
+
# Versão 3.1.0 (Diálogo de Revisão Estruturado)
|
| 4 |
+
#
|
| 5 |
+
# - Implementa o fluxo de revisão em duas etapas (4.5 e 5) para
|
| 6 |
+
# maior qualidade e consistência do storyboard.
|
| 7 |
+
# - Etapa 4.5: A IA recebe o storyboard completo para uma análise global.
|
| 8 |
+
# - Etapa 5: A IA itera cena a cena para fazer um polimento fino,
|
| 9 |
+
# usando o contexto global absorvido na etapa anterior.
|
| 10 |
+
# - Utiliza os métodos de serialização do Pydantic (`model_dump(mode='json')` e
|
| 11 |
+
# `model_dump_json()`) para evitar TypeErrors com objetos datetime.
|
| 12 |
+
|
| 13 |
+
import logging
|
| 14 |
+
import json
|
| 15 |
+
from typing import Generator, Dict, Any, List
|
| 16 |
+
|
| 17 |
+
# --- Interface neural e tipos ---
|
| 18 |
+
from .neura_link import neura_link_singleton as NeuraLink
|
| 19 |
+
from ..types import GenerationState, CatalogoDeAtivos, Scene, Ato, AtivoCatalogado
|
| 20 |
+
|
| 21 |
+
logger = logging.getLogger(__name__)
|
| 22 |
+
|
| 23 |
+
class Composer2D:
|
| 24 |
+
"""
|
| 25 |
+
Orquestra um pipeline conversacional para gerar e refinar um storyboard,
|
| 26 |
+
usando um sistema de identificação de ativos universal e um diálogo de
|
| 27 |
+
revisão em várias etapas.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def compose_storyboard(self, dna: GenerationState) -> Generator[GenerationState, None, None]:
|
| 31 |
+
"""
|
| 32 |
+
Executa o pipeline completo de pré-produção, incluindo a fase de revisão.
|
| 33 |
+
"""
|
| 34 |
+
params = getattr(dna.parametros_geracao, "pre_producao", None)
|
| 35 |
+
if not params:
|
| 36 |
+
raise ValueError("Parâmetros de pré-produção ausentes no DNA (parametros_geracao.pre_producao requerido).")
|
| 37 |
+
|
| 38 |
+
num_scenes = params.num_scenes
|
| 39 |
+
duration_per_fragment = params.duration_per_fragment
|
| 40 |
+
global_prompt = params.prompt
|
| 41 |
+
|
| 42 |
+
refs = list(getattr(dna, "midias_referencia", []))
|
| 43 |
+
image_paths = [r.caminho for r in refs if r.caminho]
|
| 44 |
+
image_map_para_llm = [{"tag_referencia": r.tag} for r in refs]
|
| 45 |
+
|
| 46 |
+
NeuraLink.reset_memory()
|
| 47 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Memória da IA reiniciada. Iniciando roteiro..."})
|
| 48 |
+
yield dna
|
| 49 |
+
|
| 50 |
+
# --- ETAPAS 1-4: GERAÇÃO DO RASCUNHO INICIAL ---
|
| 51 |
+
|
| 52 |
+
# ETAPA 1: NARRATIVA
|
| 53 |
+
narrativa = NeuraLink.execute_task(
|
| 54 |
+
task_id="TASK_01_CREATE_CINEMATIC_NARRATIVE",
|
| 55 |
+
template_data={"global_prompt": global_prompt, "num_scenes": num_scenes},
|
| 56 |
+
image_paths=image_paths or None, expected_format="text", use_memory=True,
|
| 57 |
+
)
|
| 58 |
+
dna.texto_global_historia = (narrativa or "").strip()
|
| 59 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Narrativa global criada."})
|
| 60 |
+
yield dna
|
| 61 |
+
|
| 62 |
+
# ETAPA 2: CATÁLOGO DE ATIVOS
|
| 63 |
+
asset_catalog_dict = NeuraLink.execute_task(
|
| 64 |
+
task_id="TASK_02_SELECT_AND_CATALOG_ASSETS",
|
| 65 |
+
template_data={"image_map": json.dumps(image_map_para_llm, ensure_ascii=False, indent=2)},
|
| 66 |
+
expected_format="json", use_memory=True,
|
| 67 |
+
)
|
| 68 |
+
dna.ativos_catalogados = CatalogoDeAtivos(**(asset_catalog_dict or {}))
|
| 69 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Catálogo de ativos universais definido."})
|
| 70 |
+
yield dna
|
| 71 |
+
|
| 72 |
+
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}
|
| 73 |
+
|
| 74 |
+
def upsert_asset_from_llm(llm_obj: Dict[str, Any], asset_type: str) -> AtivoCatalogado:
|
| 75 |
+
asset_id = llm_obj.get("id")
|
| 76 |
+
if not asset_id: raise ValueError(f"Ativo do tipo '{asset_type}' recebido do LLM sem 'id'.")
|
| 77 |
+
if asset_id in all_assets_by_id: return all_assets_by_id[asset_id]
|
| 78 |
+
|
| 79 |
+
logger.info(f"IA propôs um novo ativo dinamicamente: {asset_id} (Tipo: {asset_type})")
|
| 80 |
+
novo_ativo = AtivoCatalogado(**llm_obj)
|
| 81 |
+
getattr(dna.ativos_catalogados, f"{asset_type}s", []).append(novo_ativo)
|
| 82 |
+
all_assets_by_id[novo_ativo.id] = novo_ativo
|
| 83 |
+
return novo_ativo
|
| 84 |
+
|
| 85 |
+
# ETAPA 3: STORYBOARD
|
| 86 |
+
storyboard_dict = NeuraLink.execute_task(
|
| 87 |
+
task_id="TASK_03_CREATE_DIRECTORS_STORYBOARD",
|
| 88 |
+
template_data={"num_scenes": num_scenes}, expected_format="json", use_memory=True,
|
| 89 |
+
)
|
| 90 |
+
scene_list = (storyboard_dict or {}).get("storyboard", [])
|
| 91 |
+
normalized_scenes: List[Scene] = []
|
| 92 |
+
for idx, s_dict in enumerate(scene_list):
|
| 93 |
+
if "id_cena" not in s_dict: s_dict["id_cena"] = idx + 1
|
| 94 |
+
canonical_cenario = upsert_asset_from_llm(s_dict.get("cenario_escolhido", {}), "cenario")
|
| 95 |
+
s_dict["cenario_escolhido"] = canonical_cenario.model_dump()
|
| 96 |
+
normalized_scenes.append(Scene(**s_dict))
|
| 97 |
+
dna.storyboard_producao = normalized_scenes
|
| 98 |
+
dna.chat_history.append({"role": "Composer2D", "content": f"Rascunho do storyboard com {len(normalized_scenes)} cenas criado."})
|
| 99 |
+
yield dna
|
| 100 |
+
|
| 101 |
+
# ETAPA 4: ATOS
|
| 102 |
+
for scene in dna.storyboard_producao:
|
| 103 |
+
acts_dict = NeuraLink.execute_task(
|
| 104 |
+
task_id="TASK_04_DETAIL_SCENE_INTO_ACTS",
|
| 105 |
+
template_data={"scene_directors_manual": scene.model_dump_json(indent=2), "max_duration_per_act_s": duration_per_fragment},
|
| 106 |
+
expected_format="json", use_memory=True,
|
| 107 |
+
)
|
| 108 |
+
scene.atos = [Ato(**a) for a in (acts_dict or {}).get("atos", [])]
|
| 109 |
+
dna.chat_history.append({"role": "Composer2D", "content": f"Cena {scene.id_cena}: atos detalhados."})
|
| 110 |
+
yield dna
|
| 111 |
+
|
| 112 |
+
# --- ETAPA 4.5: PRÉ-REVISÃO (CARREGAMENTO DE CONTEXTO OTIMIZADO) ---
|
| 113 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Rascunho finalizado. Apresentando à IA para análise global antes do polimento..."})
|
| 114 |
+
yield dna
|
| 115 |
+
|
| 116 |
+
full_storyboard_json = json.dumps(
|
| 117 |
+
[s.model_dump(mode='json') for s in dna.storyboard_producao],
|
| 118 |
+
indent=2,
|
| 119 |
+
ensure_ascii=False
|
| 120 |
+
)
|
| 121 |
+
confirmation_message = NeuraLink.execute_task(
|
| 122 |
+
task_id="TASK_4_5_PRE_REVIEW_CONTEXT",
|
| 123 |
+
template_data={"full_storyboard_json": full_storyboard_json},
|
| 124 |
+
expected_format="text", use_memory=True,
|
| 125 |
+
)
|
| 126 |
+
dna.chat_history.append({"role": "Supervisor de Roteiro (IA)", "content": confirmation_message})
|
| 127 |
+
yield dna
|
| 128 |
+
|
| 129 |
+
# --- ETAPA 5: REVISÃO DE CONTINUIDADE CENA A CENA (COM CONTEXTO IMPLÍCITO) ---
|
| 130 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Contexto carregado. Iniciando revisão detalhada cena a cena..."})
|
| 131 |
+
yield dna
|
| 132 |
+
|
| 133 |
+
refined_storyboard = []
|
| 134 |
+
for scene_to_review in dna.storyboard_producao:
|
| 135 |
+
dna.chat_history.append({"role": "Composer2D", "content": f"Polindo Cena {scene_to_review.id_cena}/{len(dna.storyboard_producao)}: '{scene_to_review.titulo_cena}'..."})
|
| 136 |
+
yield dna
|
| 137 |
+
|
| 138 |
+
scene_json_for_prompt = scene_to_review.model_dump_json(indent=2)
|
| 139 |
+
|
| 140 |
+
refined_scene_dict = NeuraLink.execute_task(
|
| 141 |
+
task_id="TASK_05_SCENE_CONTINUITY_REVIEW",
|
| 142 |
+
template_data={"scene_to_review_json": scene_json_for_prompt},
|
| 143 |
+
expected_format="json", use_memory=True,
|
| 144 |
+
)
|
| 145 |
+
try:
|
| 146 |
+
refined_scene = Scene(**refined_scene_dict)
|
| 147 |
+
refined_storyboard.append(refined_scene)
|
| 148 |
+
except Exception as e:
|
| 149 |
+
logger.warning(f"Falha ao validar cena refinada {scene_to_review.id_cena}. Mantendo a versão original. Erro: {e}")
|
| 150 |
+
refined_storyboard.append(scene_to_review)
|
| 151 |
+
|
| 152 |
+
dna.storyboard_producao = refined_storyboard
|
| 153 |
+
dna.chat_history.append({"role": "Composer2D", "content": "Revisão de continuidade concluída. Storyboard finalizado e polido."})
|
| 154 |
+
yield dna
|
| 155 |
+
|
| 156 |
+
logger.info("Pipeline completo do Composer2D (Geração e Revisão) concluído com sucesso.")
|
| 157 |
+
return
|
| 158 |
+
|
| 159 |
+
# Singleton
|
| 160 |
+
composer_2d_singleton = Composer2D()
|
aduc_framework/engineers/deformes3D.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/deformes3D.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 12.4.0 (Correção de Integração com VaeManager v2)
|
| 4 |
+
# - Remove a função auxiliar obsoleta `_pil_to_latent` que ainda tentava
|
| 5 |
+
# chamar o antigo método `vae_manager_singleton.encode()`.
|
| 6 |
+
# - Consolida o uso exclusivo do método `encode_batch` para toda a codificação
|
| 7 |
+
# de imagens, alinhando-se com a arquitetura de VAE persistente e otimizada.
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
import logging
|
| 11 |
+
import torch
|
| 12 |
+
import numpy as np
|
| 13 |
+
from PIL import Image
|
| 14 |
+
from typing import List, Dict, Any
|
| 15 |
+
|
| 16 |
+
from ..types import LatentConditioningItem, KeyframeGenerationJob
|
| 17 |
+
from ..managers.ltx_manager import ltx_manager_singleton
|
| 18 |
+
from ..managers.vae_manager import vae_manager_singleton
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
class Deformes3DEngine:
|
| 23 |
+
def __init__(self):
|
| 24 |
+
self.workspace_dir: str | None = None
|
| 25 |
+
logger.info("Deformes3DEngine (Pintor de Sequência) instanciado.")
|
| 26 |
+
|
| 27 |
+
def initialize(self, workspace_dir: str):
|
| 28 |
+
if self.workspace_dir is not None and self.workspace_dir == workspace_dir:
|
| 29 |
+
return
|
| 30 |
+
self.workspace_dir = workspace_dir
|
| 31 |
+
logger.info(f"Pintor 3D inicializado com workspace: {self.workspace_dir}.")
|
| 32 |
+
|
| 33 |
+
def generate_keyframes_from_job(
|
| 34 |
+
self,
|
| 35 |
+
job: KeyframeGenerationJob
|
| 36 |
+
) -> List[Dict[str, Any]]:
|
| 37 |
+
from .composer import composer_singleton as Composer
|
| 38 |
+
|
| 39 |
+
if not self.workspace_dir:
|
| 40 |
+
raise RuntimeError("Deformes3DEngine não foi inicializado.")
|
| 41 |
+
|
| 42 |
+
storyboard = job.storyboard
|
| 43 |
+
num_keyframes_to_generate = len(storyboard)
|
| 44 |
+
global_prompt = job.global_prompt
|
| 45 |
+
ref_id_to_path_map = job.ref_id_to_path_map
|
| 46 |
+
available_ref_ids = job.available_ref_ids
|
| 47 |
+
keyframe_prefix = job.keyframe_prefix
|
| 48 |
+
|
| 49 |
+
target_resolution_tuple = (512, 512)
|
| 50 |
+
current_base_image_path = job.ref_image_paths[0]
|
| 51 |
+
previous_prompt = ""
|
| 52 |
+
all_keyframes_data: List[Dict[str, Any]] = []
|
| 53 |
+
|
| 54 |
+
logger.info(f"Pintor 3D: Recebida ordem para gerar {num_keyframes_to_generate} keyframes para a cena '{keyframe_prefix}'.")
|
| 55 |
+
|
| 56 |
+
# --- FLUXO DE CODIFICAÇÃO EM LOTE OTIMIZADO ---
|
| 57 |
+
# 1. Coletar a imagens base
|
| 58 |
+
images_to_encode = [Image.open(current_base_image_path).convert("RGB")]
|
| 59 |
+
ref_weights = [0.4] # Peso fixo para a imagem anterior
|
| 60 |
+
encoded_latents = vae_manager_singleton.encode_batch(images_to_encode, target_resolution_tuple)
|
| 61 |
+
|
| 62 |
+
for i in range(num_keyframes_to_generate):
|
| 63 |
+
scene_index = i + 1
|
| 64 |
+
current_scene_narrative = storyboard[i]
|
| 65 |
+
future_scene_narrative = storyboard[i + 1] if (i + 1) < len(storyboard) else "A cena final."
|
| 66 |
+
logger.info(f"--> Planejando Keyframe {scene_index}/{num_keyframes_to_generate}...")
|
| 67 |
+
|
| 68 |
+
composition_plan = Composer.execute_cognitive_task(
|
| 69 |
+
task_id="COGNITIVE_01_PLAN_KEYFRAME",
|
| 70 |
+
template_data={
|
| 71 |
+
"historico_prompt": previous_prompt,
|
| 72 |
+
"cena_atual": current_scene_narrative,
|
| 73 |
+
"cena_futura": future_scene_narrative,
|
| 74 |
+
"available_ref_images": available_ref_ids,
|
| 75 |
+
},
|
| 76 |
+
images=[Image.open(current_base_image_path)]
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
img_prompt = composition_plan.get("composition_prompt", current_scene_narrative)
|
| 80 |
+
selected_references = composition_plan.get("reference_images", [])
|
| 81 |
+
logger.info(f"Plano do Diretor de Arte recebido. Prompt: '{img_prompt[:80]}...'. Referências: {len(selected_references)}")
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
#for ref in selected_references:
|
| 85 |
+
# image_path = ref_id_to_path_map.get(ref.get("id"))
|
| 86 |
+
# if image_path:
|
| 87 |
+
# images_to_encode.append(Image.open(image_path).convert("RGB"))
|
| 88 |
+
# ref_weights.append(ref.get("weight", 0.3))
|
| 89 |
+
|
| 90 |
+
# 2. Chamar o método em lote do VaeManager UMA ÚNICA VEZ
|
| 91 |
+
#logger.info(f"Codificando {len(images_to_encode)} imagens de referência em lote...")
|
| 92 |
+
#encoded_latents = vae_manager_singleton.encode_batch(images_to_encode, target_resolution_tuple)
|
| 93 |
+
#logger.info("Codificação em lote concluída.")
|
| 94 |
+
|
| 95 |
+
# 3. Construir os itens de condicionamento com os latentes já prontos
|
| 96 |
+
ltx_conditioning_items = []
|
| 97 |
+
for latent_tensor, weight in zip(encoded_latents, ref_weights):
|
| 98 |
+
ltx_conditioning_items.append(LatentConditioningItem(latent_tensor, 0, weight))
|
| 99 |
+
# --- FIM DA OTIMIZAÇÃO ---
|
| 100 |
+
|
| 101 |
+
ltx_base_params = {"guidance_scale": 2.0, "stg_scale": 0.015, "num_inference_steps": 25}
|
| 102 |
+
generated_latents, _ = ltx_manager_singleton.generate_latent_fragment(
|
| 103 |
+
height=target_resolution_tuple[0], width=target_resolution_tuple[1],
|
| 104 |
+
conditioning_items_data=ltx_conditioning_items,
|
| 105 |
+
motion_prompt=img_prompt,
|
| 106 |
+
video_total_frames=36, video_fps=24,
|
| 107 |
+
**ltx_base_params
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
final_latent = generated_latents[:, :, -1:, :, :]
|
| 111 |
+
enriched_pixel_tensor = vae_manager_singleton.decode(final_latent)
|
| 112 |
+
|
| 113 |
+
encoded_latents = [encoded_latents[0], final_latent]
|
| 114 |
+
ref_weights = [0.05,0.5]
|
| 115 |
+
|
| 116 |
+
pixel_path = os.path.join(self.workspace_dir, f"{keyframe_prefix}_kf{scene_index:03d}_pixel.png")
|
| 117 |
+
latent_path = os.path.join(self.workspace_dir, f"{keyframe_prefix}_kf{scene_index:03d}_latent.pt")
|
| 118 |
+
self.save_image_from_tensor(enriched_pixel_tensor, pixel_path)
|
| 119 |
+
torch.save(final_latent.cpu(), latent_path)
|
| 120 |
+
|
| 121 |
+
keyframe_data = {
|
| 122 |
+
"id": scene_index,
|
| 123 |
+
"caminho_pixel": pixel_path,
|
| 124 |
+
"caminho_latent": latent_path,
|
| 125 |
+
"prompt_keyframe": img_prompt
|
| 126 |
+
}
|
| 127 |
+
all_keyframes_data.append(keyframe_data)
|
| 128 |
+
|
| 129 |
+
current_base_image_path = pixel_path
|
| 130 |
+
previous_prompt = img_prompt
|
| 131 |
+
|
| 132 |
+
logger.info(f"Pintor 3D: Ordem de serviço para a cena '{keyframe_prefix}' concluída.")
|
| 133 |
+
return all_keyframes_data
|
| 134 |
+
|
| 135 |
+
# --- FUNÇÃO PROBLEMÁTICA REMOVIDA ---
|
| 136 |
+
# A função _pil_to_latent foi removida pois sua lógica foi
|
| 137 |
+
# centralizada e otimizada dentro do loop principal.
|
| 138 |
+
|
| 139 |
+
def save_image_from_tensor(self, pixel_tensor: torch.Tensor, path: str):
|
| 140 |
+
"""Salva um tensor de pixel como um arquivo de imagem."""
|
| 141 |
+
# Garante que o tensor está na CPU para manipulação com numpy/PIL
|
| 142 |
+
pixel_tensor_cpu = pixel_tensor.cpu()
|
| 143 |
+
tensor_chw = pixel_tensor_cpu.squeeze(0).squeeze(1)
|
| 144 |
+
tensor_hwc = tensor_chw.permute(1, 2, 0)
|
| 145 |
+
# Desnormaliza de [-1, 1] para [0, 1]
|
| 146 |
+
tensor_hwc = (tensor_hwc.clamp(-1, 1) + 1) / 2.0
|
| 147 |
+
# Converte para [0, 255] e tipo de imagem
|
| 148 |
+
image_np = (tensor_hwc.float().numpy() * 255).astype(np.uint8)
|
| 149 |
+
Image.fromarray(image_np).save(path)
|
| 150 |
+
|
| 151 |
+
# --- Instância Singleton ---
|
| 152 |
+
deformes3d_engine_singleton = Deformes3DEngine()
|
aduc_framework/engineers/deformes4D.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/deformes4D.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 16.0.0 (Integração de Lógica ADUC-SDR Canônica)
|
| 4 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 5 |
+
#
|
| 6 |
+
# - Refatora a lógica de geração de vídeo monolítica para o padrão de
|
| 7 |
+
# Especialista ADUC, operando dentro da classe Deformes4DEngine.
|
| 8 |
+
# - Integra o núcleo lógico de Poda Causal, Eco e Déjà-Vu (linhas 187-315
|
| 9 |
+
# do arquivo original) de forma intacta.
|
| 10 |
+
# - Substitui as chamadas de baixo nível por delegações aos managers
|
| 11 |
+
# especializados (VaeManager, LtxManager, VideoEncodeTool).
|
| 12 |
+
# - O método principal agora aceita um `VideoGenerationJob` do Planner5D.
|
| 13 |
+
|
| 14 |
+
import os
|
| 15 |
+
import time
|
| 16 |
+
import torch
|
| 17 |
+
import logging
|
| 18 |
+
from PIL import Image
|
| 19 |
+
import gc
|
| 20 |
+
from typing import Dict, Any
|
| 21 |
+
|
| 22 |
+
# Importa os managers e especialistas da arquitetura ADUC
|
| 23 |
+
from ..managers.ltx_manager import ltx_manager_singleton
|
| 24 |
+
from ..managers.vae_manager import vae_manager_singleton
|
| 25 |
+
from ..tools.video_encode_tool import video_encode_tool_singleton
|
| 26 |
+
from ..types import LatentConditioningItem, VideoGenerationJob
|
| 27 |
+
from ..engineers.composer import composer_singleton as Composer # Usado para decisões cinematográficas
|
| 28 |
+
|
| 29 |
+
logger = logging.getLogger(__name__)
|
| 30 |
+
|
| 31 |
+
class Deformes4DEngine:
|
| 32 |
+
"""
|
| 33 |
+
Implementa a Câmera (Ψ) e o Destilador (Δ) da arquitetura ADUC-SDR.
|
| 34 |
+
É responsável pela execução da geração de fragmentos de vídeo e pela
|
| 35 |
+
extração dos contextos causais (Eco e Déjà-Vu).
|
| 36 |
+
"""
|
| 37 |
+
def __init__(self):
|
| 38 |
+
self.workspace_dir: str | None = None
|
| 39 |
+
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
| 40 |
+
logger.info("Especialista Deformes4D (Executor ADUC-SDR: Câmera Ψ e Destilador Δ) inicializado.")
|
| 41 |
+
|
| 42 |
+
def initialize(self, workspace_dir: str):
|
| 43 |
+
if self.workspace_dir and self.workspace_dir == workspace_dir:
|
| 44 |
+
return
|
| 45 |
+
self.workspace_dir = workspace_dir
|
| 46 |
+
os.makedirs(self.workspace_dir, exist_ok=True)
|
| 47 |
+
logger.info(f"Executor de Cena 4D inicializado com workspace: {self.workspace_dir}.")
|
| 48 |
+
|
| 49 |
+
def _quantize_to_multiple(self, n, m):
|
| 50 |
+
if m == 0: return n
|
| 51 |
+
quantized = int(round(n / m) * m)
|
| 52 |
+
return m if n > 0 and quantized == 0 else quantized
|
| 53 |
+
|
| 54 |
+
# O método principal que o Planner5D chamará
|
| 55 |
+
def generate_movie_clip_from_job(self, job: VideoGenerationJob) -> Dict[str, Any]:
|
| 56 |
+
if not self.workspace_dir:
|
| 57 |
+
raise RuntimeError("Deformes4DEngine não foi inicializado.")
|
| 58 |
+
|
| 59 |
+
# Extrai os parâmetros da Ordem de Serviço
|
| 60 |
+
keyframes = job.keyframe_paths
|
| 61 |
+
global_prompt = job.global_prompt
|
| 62 |
+
storyboard = job.storyboard
|
| 63 |
+
seconds_per_fragment = job.duration_per_fragment
|
| 64 |
+
trim_percent = job.trim_percent
|
| 65 |
+
handler_strength = job.handler_strength
|
| 66 |
+
destination_convergence_strength = job.destination_convergence_strength
|
| 67 |
+
video_resolution = job.resolution
|
| 68 |
+
|
| 69 |
+
# Este método agora encapsula a lógica do seu 'generate_full_movie'
|
| 70 |
+
return self._execute_generation_logic(
|
| 71 |
+
keyframes, global_prompt, storyboard, seconds_per_fragment,
|
| 72 |
+
trim_percent, handler_strength, destination_convergence_strength,
|
| 73 |
+
video_resolution
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
def _execute_generation_logic(self, keyframes: list, global_prompt: str, storyboard: list,
|
| 77 |
+
seconds_per_fragment: float, trim_percent: int,
|
| 78 |
+
handler_strength: float, destination_convergence_strength: float,
|
| 79 |
+
video_resolution: int):
|
| 80 |
+
|
| 81 |
+
#####(atenção Gemini .. nao apagar ou modificar nada apartir dessa linha) #####
|
| 82 |
+
# manter o conteudo original da linha 187 ate 315
|
| 83 |
+
|
| 84 |
+
# 1. Definição dos Parâmetros da Geração com base na Tese
|
| 85 |
+
FPS = 32
|
| 86 |
+
FRAMES_PER_LATENT_CHUNK = 8
|
| 87 |
+
ECO_LATENT_CHUNKS = 2
|
| 88 |
+
|
| 89 |
+
total_frames_brutos = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
|
| 90 |
+
total_latents_brutos = total_frames_brutos // FRAMES_PER_LATENT_CHUNK
|
| 91 |
+
|
| 92 |
+
frames_a_podar = 15 #self._quantize_to_multiple(int(total_frames_brutos * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
|
| 93 |
+
latents_a_podar = frames_a_podar // FRAMES_PER_LATENT_CHUNK
|
| 94 |
+
|
| 95 |
+
if total_latents_brutos <= latents_a_podar:
|
| 96 |
+
#raise gr.Error(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
|
| 97 |
+
raise ValueError(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
|
| 98 |
+
|
| 99 |
+
DEJAVU_FRAME_TARGET = 23
|
| 100 |
+
DESTINATION_FRAME_TARGET = total_frames_brutos -8
|
| 101 |
+
|
| 102 |
+
logger.info("--- CONFIGURAÇÃO DA GERAÇÃO ADUC-SDR ---")
|
| 103 |
+
logger.info(f"Total de Latents por Geração Exploratória (V_bruto): {total_latents_brutos} ({total_frames_brutos} frames)")
|
| 104 |
+
logger.info(f"Latents a serem descartados (Poda Causal): {latents_a_podar} ({frames_a_podar} frames)")
|
| 105 |
+
logger.info(f"Chunks Latentes do Eco Causal (C): {ECO_LATENT_CHUNKS}")
|
| 106 |
+
logger.info(f"Frame alvo do Déjà-Vu (D): {DEJAVU_FRAME_TARGET}")
|
| 107 |
+
logger.info(f"Frame alvo do Destino (K): {DESTINATION_FRAME_TARGET}")
|
| 108 |
+
logger.info("------------------------------------------")
|
| 109 |
+
|
| 110 |
+
# 2. Inicialização do Estado
|
| 111 |
+
base_ltx_params = {"guidance_scale": 1.0, "stg_scale": 0.005, "rescaling_scale": 0.05, "num_inference_steps": 4, "image_cond_noise_scale": 0.05}
|
| 112 |
+
keyframe_paths = [item[0] if isinstance(item, tuple) else item for item in keyframes]
|
| 113 |
+
video_clips_paths, story_history = [], ""
|
| 114 |
+
target_resolution_tuple = (video_resolution, video_resolution)
|
| 115 |
+
|
| 116 |
+
eco_latent_for_next_loop = None
|
| 117 |
+
dejavu_latent_for_next_loop = None
|
| 118 |
+
|
| 119 |
+
if len(keyframe_paths) < 1:
|
| 120 |
+
#raise gr.Error(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
|
| 121 |
+
raise ValueError(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
|
| 122 |
+
|
| 123 |
+
num_transitions_to_generate = len(keyframe_paths) -1
|
| 124 |
+
|
| 125 |
+
# 3. Loop Principal de Geração de Fragmentos
|
| 126 |
+
for i in range(num_transitions_to_generate):
|
| 127 |
+
fragment_index = i + 1
|
| 128 |
+
logger.info(f"--- INICIANDO FRAGMENTO {fragment_index}/{num_transitions_to_generate} ---")
|
| 129 |
+
|
| 130 |
+
# 3.1. Consulta ao Maestro (Γ) para obter a intenção (Pᵢ)
|
| 131 |
+
start_keyframe_path = keyframe_paths[i]
|
| 132 |
+
|
| 133 |
+
if i+1 < num_transitions_to_generate:
|
| 134 |
+
destination_keyframe_path = keyframe_paths[i + 1]
|
| 135 |
+
else:
|
| 136 |
+
destination_keyframe_path = keyframe_paths[i]
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
decision = Composer.execute_cognitive_task(
|
| 140 |
+
task_id="TASK_08_IMPROVISE_CINEMATIC_PROMPT_ATO",
|
| 141 |
+
template_data={"current_act_narrative": storyboard[i]},
|
| 142 |
+
images=[Image.open(start_keyframe_path), Image.open(destination_keyframe_path)]
|
| 143 |
+
)
|
| 144 |
+
motion_prompt = decision.strip()
|
| 145 |
+
story_history += f"\n- Ato {fragment_index}: {motion_prompt}"
|
| 146 |
+
|
| 147 |
+
# 3.2. Montagem das Âncoras para a Fórmula Canônica Ψ({C, D, K}, P)
|
| 148 |
+
conditioning_items = []
|
| 149 |
+
logger.info(" [Ψ.1] Montando âncoras causais...")
|
| 150 |
+
|
| 151 |
+
if eco_latent_for_next_loop is None:
|
| 152 |
+
logger.info(" - Primeiro fragmento: Usando Keyframe inicial como âncora de partida.")
|
| 153 |
+
img_start = Image.open(start_keyframe_path).convert("RGB")
|
| 154 |
+
img_dest = Image.open(destination_keyframe_path).convert("RGB")
|
| 155 |
+
start_latent, dest_latent = vae_manager_singleton.encode_batch([img_start, img_dest], target_resolution_tuple)
|
| 156 |
+
conditioning_items.append(LatentConditioningItem(start_latent, 0, 1.0))
|
| 157 |
+
else:
|
| 158 |
+
logger.info(" - Âncora 1: Eco Causal (C) - Herança do passado.")
|
| 159 |
+
conditioning_items.append(LatentConditioningItem(eco_latent_for_next_loop, 0, 1.0))
|
| 160 |
+
logger.info(" - Âncora 2: Déjà-Vu (D) - Memória de um futuro idealizado.")
|
| 161 |
+
conditioning_items.append(LatentConditioningItem(dejavu_latent_for_next_loop, 23, 0.5))
|
| 162 |
+
img_dest = Image.open(destination_keyframe_path).convert("RGB")
|
| 163 |
+
dest_latent = vae_manager_singleton.encode_batch([img_dest], target_resolution_tuple)[0]
|
| 164 |
+
|
| 165 |
+
logger.info(" - Âncora 3: Destino (K) - Âncora geométrica/narrativa.")
|
| 166 |
+
conditioning_items.append(LatentConditioningItem(dest_latent, DESTINATION_FRAME_TARGET, 1.0))
|
| 167 |
+
|
| 168 |
+
# 3.3. Execução da Câmera (Ψ): Geração Exploratória para criar V_bruto
|
| 169 |
+
logger.info(f" [Ψ.2] Câmera (Ψ) executando a geração exploratória de {total_latents_brutos} chunks latentes...")
|
| 170 |
+
current_ltx_params = {**base_ltx_params, "motion_prompt": motion_prompt}
|
| 171 |
+
latents_brutos, _ = ltx_manager_singleton.generate_latent_fragment(
|
| 172 |
+
height=video_resolution, width=video_resolution,
|
| 173 |
+
conditioning_items_data=conditioning_items,
|
| 174 |
+
video_total_frames=total_frames_brutos+1,
|
| 175 |
+
**current_ltx_params
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
# 3.4. Execução do Destilador (Δ): Implementação do Ciclo de Poda Causal
|
| 179 |
+
logger.info(f" [Δ] Shape do tensor bruto para vídeo final: {latents_brutos.shape}")
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
#latents_video = latents_brutos[:, :, :-1, :, :]
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
# --- Workaround empírico preservado ---
|
| 186 |
+
eco_latent_for_next_loop = latents_brutos[:, :, -5:-2, :, :].clone()
|
| 187 |
+
dejavu_latent_for_next_loop = latents_brutos[:, :, -1:, :, :].clone()
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
latents_video = latents_brutos[:, :, :-4, :, :].clone()
|
| 191 |
+
|
| 192 |
+
logger.info(f" [Δ] Shape do tensor video para vídeo final: {latents_video.shape}")
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
#if i+1 < num_transitions_to_generate:
|
| 196 |
+
#latents_video = latents_video[:, :, :-4, :, :]
|
| 197 |
+
# logger.info(f"@@@@@@@@@@# Nao é ULTIMO poda -1")
|
| 198 |
+
# logger.info(f" [Δ] -1 Shape do tensor video para vídeo final: {latents_video.shape}")
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
#if i > 0:
|
| 202 |
+
# latents_video = latents_video[:, :, 2:, :, :]
|
| 203 |
+
# logger.info(f"@@@@@@@@@@# nao é o primeiro poda 1")
|
| 204 |
+
# logger.info(f" [Δ] 1 Shape do tensor video para vídeo final: {latents_video.shape}")
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
logger.info(f" [Δ] Shape do tensor para vídeo final: {latents_video.shape}")
|
| 209 |
+
logger.info(f" - (Δ.1) Déjà-Vu (D) destilado. Shape: {dejavu_latent_for_next_loop.shape if dejavu_latent_for_next_loop is not None else 'N/A'}")
|
| 210 |
+
logger.info(f" - (Δ.2) Eco Causal (C) extraído. Shape: {eco_latent_for_next_loop.shape if eco_latent_for_next_loop is not None else 'N/A'}")
|
| 211 |
+
|
| 212 |
+
# 3.5. Renderização e Armazenamento do Fragmento Final
|
| 213 |
+
base_name = f"fragment_{fragment_index}_{int(time.time())}"
|
| 214 |
+
pixel_tensor = vae_manager_singleton.decode(latents_video)
|
| 215 |
+
video_path = os.path.join(self.workspace_dir, f"{base_name}.mp4")
|
| 216 |
+
video_encode_tool_singleton.save_video_from_tensor(pixel_tensor, video_path, fps=FPS)
|
| 217 |
+
video_clips_paths.append(video_path)
|
| 218 |
+
logger.info(f"--- FRAGMENTO {fragment_index} FINALIZADO E SALVO EM: {video_path} ---")
|
| 219 |
+
|
| 220 |
+
# 4. Montagem Final do Filme
|
| 221 |
+
final_movie_path = os.path.join(self.workspace_dir, f"final_movie_silent_{int(time.time())}.mp4")
|
| 222 |
+
final_movie_path = video_encode_tool_singleton.concatenate_videos(video_clips_paths, final_movie_path, self.workspace_dir)
|
| 223 |
+
|
| 224 |
+
#####(atenção Gemini .. nao apagar ou modificar nada acima dessa linha) #####
|
| 225 |
+
|
| 226 |
+
logger.info(f"Filme completo salvo em: {final_movie_path}")
|
| 227 |
+
# Retorna o resultado no formato esperado pelo Planner5D
|
| 228 |
+
return {
|
| 229 |
+
"final_path": final_movie_path,
|
| 230 |
+
"video_data": {"id": 0, "caminho_pixel": final_movie_path} # ID 0 para o filme completo
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
# --- Instância Singleton ---
|
| 234 |
+
deformes4d_engine_singleton = Deformes4DEngine()
|
aduc_framework/engineers/neura_link.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/neura_link.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 2.0.0 (Interface Neural com Controle de Memória)
|
| 4 |
+
#
|
| 5 |
+
# Este componente substitui o antigo 'Composer'. Ele atua como o hub central
|
| 6 |
+
# de comunicação com o LLM, abstraindo a formatação de prompts, o envio
|
| 7 |
+
# de requisições e o parsing de respostas. Agora inclui um método `reset_memory`
|
| 8 |
+
# para dar ao orquestrador controle explícito sobre o ciclo de vida da conversa.
|
| 9 |
+
|
| 10 |
+
import logging
|
| 11 |
+
import json
|
| 12 |
+
import re
|
| 13 |
+
import yaml
|
| 14 |
+
import os
|
| 15 |
+
from PIL import Image
|
| 16 |
+
from typing import List, Dict, Any, Optional
|
| 17 |
+
|
| 18 |
+
# --- Importando os managers de LLM de baixo nível e o motor de prompt ---
|
| 19 |
+
from ..managers.gemini_manager import gemini_manager_singleton
|
| 20 |
+
from ..managers.llama_multimodal_manager import llama_multimodal_manager_singleton
|
| 21 |
+
from .prompt_engine import prompt_engine_singleton
|
| 22 |
+
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
def robust_json_parser(raw_text: str) -> dict:
|
| 26 |
+
"""
|
| 27 |
+
Analisa um objeto JSON de uma string que pode conter texto extra,
|
| 28 |
+
como blocos de código Markdown ou explicações do LLM.
|
| 29 |
+
"""
|
| 30 |
+
logger.debug(f"Neura_Link(JSON_PARSER): Tentando parsear JSON (primeiros 500 chars):\n---\n{raw_text[:500]}\n---")
|
| 31 |
+
match = re.search(r'```json\s*(\{.*?\})\s*```', raw_text, re.DOTALL)
|
| 32 |
+
if match:
|
| 33 |
+
json_str = match.group(1); logger.debug("JSON explícito encontrado.")
|
| 34 |
+
return json.loads(json_str)
|
| 35 |
+
try:
|
| 36 |
+
start_index = raw_text.find('{'); end_index = raw_text.rfind('}')
|
| 37 |
+
if start_index != -1 and end_index != -1 and end_index > start_index:
|
| 38 |
+
json_str = raw_text[start_index : end_index + 1]; logger.debug("JSON por delimitadores '{...}' encontrado.")
|
| 39 |
+
return json.loads(json_str)
|
| 40 |
+
except json.JSONDecodeError: pass
|
| 41 |
+
try:
|
| 42 |
+
return json.loads(raw_text)
|
| 43 |
+
except json.JSONDecodeError as e:
|
| 44 |
+
logger.error(f"Falha CRÍTICA no parser de JSON. O LLM retornou um texto inválido. Resposta bruta: \n{raw_text}")
|
| 45 |
+
raise ValueError(f"O LLM retornou um formato JSON inválido que não pôde ser corrigido. Erro: {e}")
|
| 46 |
+
|
| 47 |
+
class NeuraLink:
|
| 48 |
+
"""
|
| 49 |
+
Neura_Link é a interface de comunicação com a IA. Ele carrega templates,
|
| 50 |
+
formata prompts, envia requisições e processa as respostas, com controle
|
| 51 |
+
sobre a memória da sessão.
|
| 52 |
+
"""
|
| 53 |
+
def __init__(self):
|
| 54 |
+
logger.info("Neura_Link: Lendo config.yaml para selecionar o LLM Engine...")
|
| 55 |
+
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 56 |
+
|
| 57 |
+
self.provider = config.get('specialists', {}).get('llm_engine', {}).get('provider', 'gemini')
|
| 58 |
+
|
| 59 |
+
if self.provider == 'llama_multimodal':
|
| 60 |
+
self.llm_manager = llama_multimodal_manager_singleton
|
| 61 |
+
logger.info("Neura_Link: Motor de LLM configurado para usar 'Llama'.")
|
| 62 |
+
else:
|
| 63 |
+
self.llm_manager = gemini_manager_singleton
|
| 64 |
+
logger.info("Neura_Link: Motor de LLM configurado para usar 'Gemini' (padrão).")
|
| 65 |
+
|
| 66 |
+
prompt_engine_singleton.set_provider(self.provider)
|
| 67 |
+
self.task_templates = self._load_task_templates()
|
| 68 |
+
logger.info(f"Neura_Link inicializado com {len(self.task_templates)} templates de tarefa.")
|
| 69 |
+
|
| 70 |
+
def _load_task_templates(self) -> Dict[str, str]:
|
| 71 |
+
"""Carrega todos os arquivos .txt do diretório de templates de tarefa."""
|
| 72 |
+
templates = {}
|
| 73 |
+
try:
|
| 74 |
+
template_dir = os.path.join(os.path.dirname(__file__), '..', 'prompts', 'task_templates')
|
| 75 |
+
for task_file in os.listdir(template_dir):
|
| 76 |
+
if task_file.endswith(".txt"):
|
| 77 |
+
task_id = os.path.splitext(task_file)[0]
|
| 78 |
+
with open(os.path.join(template_dir, task_file), 'r', encoding='utf-8') as f:
|
| 79 |
+
templates[task_id] = f.read()
|
| 80 |
+
except FileNotFoundError:
|
| 81 |
+
raise FileNotFoundError(f"Diretório de templates de tarefa não encontrado. Verifique a estrutura do projeto.")
|
| 82 |
+
return templates
|
| 83 |
+
|
| 84 |
+
def reset_memory(self):
|
| 85 |
+
"""
|
| 86 |
+
Envia um comando para o manager de LLM subjacente reiniciar sua
|
| 87 |
+
sessão de chat, garantindo um contexto limpo para uma nova operação.
|
| 88 |
+
"""
|
| 89 |
+
logger.info("Neura_Link: Solicitando reinicialização da memória da sessão de chat.")
|
| 90 |
+
# Chama o manager com o gatilho `restart_session` e um prompt vazio.
|
| 91 |
+
# O manager está programado para não fazer uma chamada à API se o prompt for vazio.
|
| 92 |
+
self.llm_manager.process_turn(prompt_text="", restart_session=True)
|
| 93 |
+
|
| 94 |
+
def execute_task(self, task_id: str, template_data: Dict[str, Any], image_paths: Optional[List[str]] = None, expected_format: str = "text", use_memory: bool = False) -> Any:
|
| 95 |
+
"""
|
| 96 |
+
Executa uma única tarefa cognitiva, orquestrando todo o processo.
|
| 97 |
+
"""
|
| 98 |
+
logger.info(f"Neura_Link: Executando tarefa '{task_id}'...")
|
| 99 |
+
|
| 100 |
+
generic_template = self.task_templates.get(task_id)
|
| 101 |
+
if not generic_template:
|
| 102 |
+
raise ValueError(f"Neura_Link: Template para a tarefa '{task_id}' não foi encontrado.")
|
| 103 |
+
|
| 104 |
+
prompt_content = generic_template
|
| 105 |
+
for key, value in template_data.items():
|
| 106 |
+
prompt_content = prompt_content.replace(f"{{{key}}}", str(value))
|
| 107 |
+
|
| 108 |
+
images = [Image.open(p) for p in image_paths] if image_paths else None
|
| 109 |
+
|
| 110 |
+
final_model_prompt = prompt_engine_singleton.translate(
|
| 111 |
+
generic_prompt_content=prompt_content, has_image=bool(images)
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
logger.info(f"Neura_Link: Enviando prompt finalizado para o provedor '{self.provider}'. Use Memory: {use_memory}")
|
| 115 |
+
response_raw = self.llm_manager.process_turn(
|
| 116 |
+
prompt_text=final_model_prompt,
|
| 117 |
+
image_list=images,
|
| 118 |
+
use_memory=use_memory
|
| 119 |
+
)
|
| 120 |
+
logger.info(f"Neura_Link: Resposta bruta recebida do provedor '{self.provider}'.")
|
| 121 |
+
|
| 122 |
+
if expected_format == "json":
|
| 123 |
+
return robust_json_parser(response_raw)
|
| 124 |
+
else:
|
| 125 |
+
return response_raw.strip()
|
| 126 |
+
|
| 127 |
+
# --- Instância Singleton ---
|
| 128 |
+
neura_link_singleton = NeuraLink()
|
aduc_framework/engineers/planner_2d.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/planner_2d.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 2.1.0 (Focused 3-Step Plan)
|
| 6 |
+
# Esta versão desativa temporariamente a Etapa 4 (revisão) para focar na
|
| 7 |
+
# geração da estrutura de atos, que é o insumo para a próxima fase.
|
| 8 |
+
# A mensagem final para o orquestrador foi aprimorada.
|
| 9 |
+
|
| 10 |
+
import logging
|
| 11 |
+
from typing import List, Dict, Any, Generator, Optional, Callable
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
class Planner2D:
|
| 16 |
+
"""
|
| 17 |
+
O Planner2D cria o plano de trabalho estratégico (o `execution_plan`)
|
| 18 |
+
para a fase de pré-produção, focando na construção progressiva da narrativa.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
def generate_execution_plan(
|
| 22 |
+
self,
|
| 23 |
+
global_prompt: str,
|
| 24 |
+
num_scenes: int,
|
| 25 |
+
max_duration_per_act: float,
|
| 26 |
+
fast_mode: bool,
|
| 27 |
+
callback: Optional[Callable] = None
|
| 28 |
+
) -> Generator[Dict[str, Any], None, List[Dict[str, Any]]]:
|
| 29 |
+
"""
|
| 30 |
+
Cria e emite o plano de execução para a pré-produção.
|
| 31 |
+
"""
|
| 32 |
+
message = "Planner2D (Arquiteto): Criando novo plano de execução focado em narrativa..."
|
| 33 |
+
logger.info(message)
|
| 34 |
+
if callback: callback(0.0, desc="Arquiteto iniciando o planejamento...")
|
| 35 |
+
yield {"status": "planning", "progress": 0.0, "message": message}
|
| 36 |
+
|
| 37 |
+
execution_plan: List[Dict[str, Any]]
|
| 38 |
+
|
| 39 |
+
if fast_mode:
|
| 40 |
+
logger.info("Planner2D: Modo Rápido ativado. Consolidando tarefas de planejamento.")
|
| 41 |
+
execution_plan = [
|
| 42 |
+
{
|
| 43 |
+
"task_id": "FAST_01_GENERATE_SCENES_AND_ACTS",
|
| 44 |
+
"description": "(Modo Rápido) Gerando a estrutura completa de Cenas e Atos...",
|
| 45 |
+
"inputs": {"global_prompt": global_prompt, "num_scenes": num_scenes},
|
| 46 |
+
"status": "pending"
|
| 47 |
+
}
|
| 48 |
+
]
|
| 49 |
+
else:
|
| 50 |
+
# --- MUDANÇA PRINCIPAL AQUI ---
|
| 51 |
+
logger.info("Planner2D: Modo Detalhado ativado com 3 etapas (Etapa 4 desativada temporariamente).")
|
| 52 |
+
execution_plan = [
|
| 53 |
+
{
|
| 54 |
+
"task_id": "STEP_01_CREATE_CONTINUOUS_STORY",
|
| 55 |
+
"description": "Etapa 1/3: Criando a história contínua...",
|
| 56 |
+
"inputs": {"global_prompt": global_prompt, "num_scenes": num_scenes},
|
| 57 |
+
"status": "pending"
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"task_id": "STEP_02_CREATE_INDEPENDENT_SCENES",
|
| 61 |
+
"description": "Etapa 2/3: Refinando cada parágrafo em uma cena independente...",
|
| 62 |
+
"inputs": {},
|
| 63 |
+
"status": "pending"
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"task_id": "STEP_03_FRAGMENT_SCENES_INTO_ACTS",
|
| 67 |
+
"description": "Etapa 3/3: Detalhando cada cena em atos independentes...",
|
| 68 |
+
"inputs": {},
|
| 69 |
+
"status": "pending"
|
| 70 |
+
},
|
| 71 |
+
# {
|
| 72 |
+
# "task_id": "STEP_04_FINAL_REVIEW",
|
| 73 |
+
# "description": "Etapa 4/4: Realizando a revisão final de coerência...",
|
| 74 |
+
# "inputs": {"global_prompt": global_prompt},
|
| 75 |
+
# "status": "pending"
|
| 76 |
+
# }
|
| 77 |
+
]
|
| 78 |
+
|
| 79 |
+
# --- MENSAGEM FINAL APRIMORADA ---
|
| 80 |
+
message = f"Planner2D (Arquiteto): Plano de pré-produção finalizado com {len(execution_plan)} etapas. Entregando para o Composer (Encarregado) executar."
|
| 81 |
+
logger.info(message)
|
| 82 |
+
if callback:
|
| 83 |
+
callback(0.1, desc="Plano estratégico concluído. Iniciando execução...")
|
| 84 |
+
|
| 85 |
+
# Este yield informa ao Orquestrador que o planejamento terminou e entrega o plano final.
|
| 86 |
+
yield {"status": "planning_complete", "progress": 0.1, "message": message, "plan": execution_plan}
|
| 87 |
+
|
| 88 |
+
return execution_plan
|
| 89 |
+
|
| 90 |
+
# --- Instância Singleton ---
|
| 91 |
+
planner_2d_singleton = Planner2D()
|
aduc_framework/engineers/planner_3d.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/planner_3d.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 4.1.0 (DNA Seguro por Cena)
|
| 6 |
+
#
|
| 7 |
+
# - Remove o acúmulo global de keyframes e a divisão por índices.
|
| 8 |
+
# - Atualiza cada cena diretamente com os keyframes gerados pelo Painter.
|
| 9 |
+
# - Garante que o DNA final nunca fique “bagunçado”.
|
| 10 |
+
# - Foco na simplicidade: Planner orquestra, Painter gera, DNA organiza.
|
| 11 |
+
|
| 12 |
+
import logging
|
| 13 |
+
import json
|
| 14 |
+
from typing import List, Dict, Any, Generator
|
| 15 |
+
|
| 16 |
+
from .deformes3D import deformes3d_engine_singleton
|
| 17 |
+
from ..types import KeyframeGenerationJob
|
| 18 |
+
|
| 19 |
+
logger = logging.getLogger(__name__)
|
| 20 |
+
|
| 21 |
+
class Planner3D:
|
| 22 |
+
"""
|
| 23 |
+
Gerente de projeto para a fase de keyframes.
|
| 24 |
+
Prepara e despacha ordens de serviço para o Painter (Deformes3D),
|
| 25 |
+
e injeta os keyframes diretamente no DNA cena a cena.
|
| 26 |
+
"""
|
| 27 |
+
def generate_keyframes_from_dna(
|
| 28 |
+
self,
|
| 29 |
+
generation_state: Dict[str, Any],
|
| 30 |
+
initial_chat_history: List[Dict[str, Any]]
|
| 31 |
+
) -> Generator[Dict[str, Any], None, None]:
|
| 32 |
+
|
| 33 |
+
painter = deformes3d_engine_singleton
|
| 34 |
+
chat_history = initial_chat_history.copy()
|
| 35 |
+
chat_history.append({
|
| 36 |
+
"role": "Planner3D",
|
| 37 |
+
"content": "Roteiro recebido. Iniciando criação de keyframes..."
|
| 38 |
+
})
|
| 39 |
+
yield {"chat": chat_history, "gallery": [], "dna": generation_state}
|
| 40 |
+
|
| 41 |
+
# 1. Extrai dados globais
|
| 42 |
+
scenes_list = generation_state.get("scenes", [])
|
| 43 |
+
ref_image_paths = [ref['caminho'] for ref in generation_state.get("midias_referencia", [])]
|
| 44 |
+
global_prompt = generation_state.get("prompt_geral", "")
|
| 45 |
+
pre_prod_params = generation_state.get("parametros_geracao", {}).get("pre_producao", {})
|
| 46 |
+
|
| 47 |
+
if not scenes_list or not ref_image_paths:
|
| 48 |
+
logger.warning("Planner3D: Nenhuma cena ou imagem de referência encontrada no DNA.")
|
| 49 |
+
yield {"chat": chat_history, "status": "keyframes_complete"}
|
| 50 |
+
return
|
| 51 |
+
|
| 52 |
+
# Mapeamento fixo de imagens de referência
|
| 53 |
+
ref_id_to_path_map = {i: path for i, path in enumerate(ref_image_paths)}
|
| 54 |
+
available_ref_ids = list(ref_id_to_path_map.keys())
|
| 55 |
+
|
| 56 |
+
# 2. Loop de cenas
|
| 57 |
+
for scene_idx, scene_data in enumerate(scenes_list):
|
| 58 |
+
scene_id = scene_data.get("id")
|
| 59 |
+
chat_history.append({
|
| 60 |
+
"role": "Planner3D",
|
| 61 |
+
"content": f"Desenhando a Cena {scene_id}/{len(scenes_list)}..."
|
| 62 |
+
})
|
| 63 |
+
yield {"chat": chat_history}
|
| 64 |
+
|
| 65 |
+
storyboard_da_cena = [ato.get("resumo_ato", "") for ato in scene_data.get("atos", [])]
|
| 66 |
+
if not storyboard_da_cena:
|
| 67 |
+
logger.info(f"Planner3D: Cena {scene_id} sem atos, pulando.")
|
| 68 |
+
continue
|
| 69 |
+
|
| 70 |
+
# 3. Cria ordem de serviço
|
| 71 |
+
job = KeyframeGenerationJob(
|
| 72 |
+
storyboard=storyboard_da_cena,
|
| 73 |
+
global_prompt=global_prompt,
|
| 74 |
+
ref_image_paths=ref_image_paths,
|
| 75 |
+
ref_id_to_path_map=ref_id_to_path_map,
|
| 76 |
+
available_ref_ids=available_ref_ids,
|
| 77 |
+
keyframe_prefix=f"scene_{scene_id:02d}",
|
| 78 |
+
params=pre_prod_params
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
# 4. Delega para o Painter
|
| 82 |
+
scene_keyframes = painter.generate_keyframes_from_job(job=job)
|
| 83 |
+
|
| 84 |
+
# 5. Injeta diretamente no DNA
|
| 85 |
+
scene_data["keyframes"] = scene_keyframes
|
| 86 |
+
|
| 87 |
+
if scene_keyframes:
|
| 88 |
+
chat_history.append({
|
| 89 |
+
"role": "Planner3D",
|
| 90 |
+
"content": f"Cena {scene_id} concluída ({len(scene_keyframes)} keyframes)."
|
| 91 |
+
})
|
| 92 |
+
yield {"chat": chat_history}
|
| 93 |
+
|
| 94 |
+
logger.info("Planner3D: Geração de todos os keyframes concluída.")
|
| 95 |
+
|
| 96 |
+
yield {
|
| 97 |
+
"chat": chat_history,
|
| 98 |
+
"gallery": [kf["caminho_pixel"] for scene in scenes_list for kf in scene.get("keyframes", [])],
|
| 99 |
+
"status": "keyframes_complete",
|
| 100 |
+
"dna": generation_state
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
planner_3d_singleton = Planner3D()
|
aduc_framework/engineers/planner_4d.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/planner_4d.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 6.5.0 (Montagem com Pontes de Transição)
|
| 6 |
+
#
|
| 7 |
+
# - Implementa uma nova etapa de pós-produção antes da concatenação final.
|
| 8 |
+
# - Para cada par de clipes de cena, o planejador agora extrai o último frame
|
| 9 |
+
# do primeiro clipe e o primeiro frame do segundo.
|
| 10 |
+
# - Usa esses frames para gerar um pequeno vídeo de "ponte de transição".
|
| 11 |
+
# - O filme final é montado intercalando os clipes originais com essas novas
|
| 12 |
+
# transições, resultando em um produto final mais suave e profissional.
|
| 13 |
+
|
| 14 |
+
import logging
|
| 15 |
+
import os
|
| 16 |
+
import time
|
| 17 |
+
from typing import List, Dict, Any, Generator
|
| 18 |
+
|
| 19 |
+
from ..types import VideoGenerationJob, VideoData
|
| 20 |
+
from ..tools.video_encode_tool import video_encode_tool_singleton
|
| 21 |
+
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
class Planner4D:
|
| 25 |
+
"""
|
| 26 |
+
Atua como o Diretor de Fotografia, orquestrando a renderização de cenas
|
| 27 |
+
e a montagem final com transições inteligentes.
|
| 28 |
+
"""
|
| 29 |
+
def _quantize_to_multiple(self, n: int, m: int) -> int:
|
| 30 |
+
if m == 0: return n
|
| 31 |
+
quantized = int(round(n / m) * m)
|
| 32 |
+
return m if n > 0 and quantized == 0 else quantized
|
| 33 |
+
|
| 34 |
+
def produce_movie_by_scene(
|
| 35 |
+
self,
|
| 36 |
+
generation_state: Dict[str, Any],
|
| 37 |
+
initial_chat_history: List[Dict[str, Any]]
|
| 38 |
+
) -> Generator[Dict[str, Any], None, None]:
|
| 39 |
+
|
| 40 |
+
from .deformes4D import deformes4d_engine_singleton as editor
|
| 41 |
+
|
| 42 |
+
workspace_dir = generation_state.get("workspace_dir", "deformes_workspace")
|
| 43 |
+
editor.initialize(workspace_dir)
|
| 44 |
+
|
| 45 |
+
chat_history = initial_chat_history.copy()
|
| 46 |
+
chat_history.append({"role": "Planner4D", "content": "Produção iniciada. Renderizando clipes de cena..."})
|
| 47 |
+
yield {"chat": chat_history}
|
| 48 |
+
|
| 49 |
+
scenes = generation_state.get("scenes", [])
|
| 50 |
+
pre_prod_params = generation_state.get("parametros_geracao", {}).get("pre_producao", {})
|
| 51 |
+
prod_params = generation_state.get("parametros_geracao", {}).get("producao", {})
|
| 52 |
+
global_prompt = generation_state.get("prompt_geral", "")
|
| 53 |
+
|
| 54 |
+
FPS = 24
|
| 55 |
+
FRAMES_PER_LATENT_CHUNK = 8
|
| 56 |
+
seconds_per_fragment = pre_prod_params.get('duration_per_fragment', 4.0)
|
| 57 |
+
trim_percent = prod_params.get('trim_percent', 50)
|
| 58 |
+
|
| 59 |
+
total_frames_brutos_pixels = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
|
| 60 |
+
pixels_a_podar = self._quantize_to_multiple(int(total_frames_brutos_pixels * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
|
| 61 |
+
latent_frames_a_podar = pixels_a_podar // FRAMES_PER_LATENT_CHUNK
|
| 62 |
+
|
| 63 |
+
all_scene_clips_paths: List[str] = []
|
| 64 |
+
|
| 65 |
+
# --- ETAPA 1: GERAÇÃO DOS CLIPES DE CENA ---
|
| 66 |
+
for i, scene in enumerate(scenes):
|
| 67 |
+
# ... (Lógica de geração de job e chamada ao editor permanece a mesma)
|
| 68 |
+
scene_id = scene.get('id')
|
| 69 |
+
chat_history.append({"role": "Planner4D", "content": f"Preparando para filmar a Cena {scene_id}/{len(scenes)}..."})
|
| 70 |
+
yield {"chat": chat_history}
|
| 71 |
+
storyboard_da_cena = [ato.get("resumo_ato", "") for ato in scene.get("atos", [])]
|
| 72 |
+
keyframes_para_cena = scene.get("keyframes", [])
|
| 73 |
+
if len(keyframes_para_cena) < 2:
|
| 74 |
+
chat_history.append({"role": "Planner4D", "content": f"Cena {scene_id} pulada (keyframes insuficientes)."})
|
| 75 |
+
yield {"chat": chat_history}
|
| 76 |
+
continue
|
| 77 |
+
job = VideoGenerationJob(
|
| 78 |
+
scene_id=scene_id, global_prompt=global_prompt, storyboard=storyboard_da_cena,
|
| 79 |
+
keyframe_paths=[kf['caminho_pixel'] for kf in keyframes_para_cena],
|
| 80 |
+
video_resolution=pre_prod_params.get('resolution', 480),
|
| 81 |
+
handler_strength=prod_params.get('handler_strength', 0.5),
|
| 82 |
+
destination_convergence_strength=prod_params.get('destination_convergence_strength', 0.75),
|
| 83 |
+
guidance_scale=prod_params.get('guidance_scale', 2.0),
|
| 84 |
+
stg_scale=prod_params.get('stg_scale', 0.025),
|
| 85 |
+
num_inference_steps=prod_params.get('inference_steps', 20),
|
| 86 |
+
total_frames_brutos=total_frames_brutos_pixels, latents_a_podar=pixels_a_podar,
|
| 87 |
+
latent_frames_a_podar=latent_frames_a_podar,
|
| 88 |
+
DEJAVU_FRAME_TARGET=pixels_a_podar - 1 if pixels_a_podar > 0 else 0,
|
| 89 |
+
DESTINATION_FRAME_TARGET=total_frames_brutos_pixels - 1,
|
| 90 |
+
)
|
| 91 |
+
clip_result = editor.generate_movie_clip_from_job(job=job)
|
| 92 |
+
if clip_result and clip_result.get("final_path"):
|
| 93 |
+
final_clip_path = clip_result["final_path"]
|
| 94 |
+
all_scene_clips_paths.append(final_clip_path)
|
| 95 |
+
video_data_obj = VideoData(**clip_result["video_data"]).model_dump()
|
| 96 |
+
if "videos" not in generation_state["scenes"][i]:
|
| 97 |
+
generation_state["scenes"][i]["videos"] = []
|
| 98 |
+
generation_state["scenes"][i]["videos"].append(video_data_obj)
|
| 99 |
+
chat_history.append({"role": "Planner4D", "content": f"Cena {scene_id} filmada com sucesso!"})
|
| 100 |
+
yield {"chat": chat_history, "final_video_path": final_clip_path, "dna": generation_state}
|
| 101 |
+
|
| 102 |
+
if not all_scene_clips_paths:
|
| 103 |
+
chat_history.append({"role": "Planner4D", "content": "Nenhum clipe de cena foi gerado."})
|
| 104 |
+
yield {"chat": chat_history, "status": "production_complete"}
|
| 105 |
+
return
|
| 106 |
+
|
| 107 |
+
# --- ETAPA 2: CRIAÇÃO DE PONTES DE TRANSIÇÃO (NOVA LÓGICA) ---
|
| 108 |
+
chat_history.append({"role": "Planner4D", "content": "Todas as cenas filmadas. Criando transições suaves..."})
|
| 109 |
+
yield {"chat": chat_history}
|
| 110 |
+
|
| 111 |
+
final_concat_list = []
|
| 112 |
+
temp_frames_to_delete = []
|
| 113 |
+
|
| 114 |
+
if len(all_scene_clips_paths) > 1:
|
| 115 |
+
# Adiciona o primeiro clipe e entra no loop para criar pontes
|
| 116 |
+
final_concat_list.append(all_scene_clips_paths[0])
|
| 117 |
+
for i in range(len(all_scene_clips_paths) - 1):
|
| 118 |
+
clip_a_path = all_scene_clips_paths[i]
|
| 119 |
+
clip_b_path = all_scene_clips_paths[i+1]
|
| 120 |
+
|
| 121 |
+
# Define caminhos para os frames temporários
|
| 122 |
+
last_frame_path = os.path.join(workspace_dir, f"temp_last_frame_{i}.png")
|
| 123 |
+
first_frame_path = os.path.join(workspace_dir, f"temp_first_frame_{i+1}.png")
|
| 124 |
+
temp_frames_to_delete.extend([last_frame_path, first_frame_path])
|
| 125 |
+
|
| 126 |
+
# Extrai os frames
|
| 127 |
+
video_encode_tool_singleton.extract_last_frame(clip_a_path, last_frame_path)
|
| 128 |
+
video_encode_tool_singleton.extract_first_frame(clip_b_path, first_frame_path)
|
| 129 |
+
|
| 130 |
+
# Cria a ponte de transição
|
| 131 |
+
bridge_video_path = video_encode_tool_singleton.create_transition_bridge(
|
| 132 |
+
start_image_path=last_frame_path,
|
| 133 |
+
end_image_path=first_frame_path,
|
| 134 |
+
duration=0.3, # 1 segundo de transição
|
| 135 |
+
fps=FPS,
|
| 136 |
+
target_resolution=(pre_prod_params.get('resolution', 480), pre_prod_params.get('resolution', 480)),
|
| 137 |
+
workspace_dir=workspace_dir
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
# Adiciona a ponte e o próximo clipe à lista de montagem
|
| 141 |
+
final_concat_list.append(bridge_video_path)
|
| 142 |
+
final_concat_list.append(clip_b_path)
|
| 143 |
+
else:
|
| 144 |
+
# Se houver apenas um clipe, não há transições a criar
|
| 145 |
+
final_concat_list = all_scene_clips_paths
|
| 146 |
+
|
| 147 |
+
# --- ETAPA 3: MONTAGEM FINAL ---
|
| 148 |
+
chat_history.append({"role": "Planner4D", "content": "Transições criadas. Montando o filme final..."})
|
| 149 |
+
yield {"chat": chat_history}
|
| 150 |
+
|
| 151 |
+
final_video_path = video_encode_tool_singleton.concatenate_videos(
|
| 152 |
+
video_paths=final_concat_list,
|
| 153 |
+
output_path=f"{workspace_dir}/filme_final_com_transicoes.mp4",
|
| 154 |
+
workspace_dir=workspace_dir
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
# Limpa os frames temporários
|
| 158 |
+
for frame_path in temp_frames_to_delete:
|
| 159 |
+
if os.path.exists(frame_path):
|
| 160 |
+
os.remove(frame_path)
|
| 161 |
+
|
| 162 |
+
# Atualiza o DNA com o resultado final
|
| 163 |
+
final_movie_data = VideoData(id=0, caminho_pixel=final_video_path, prompt_video=[]).model_dump()
|
| 164 |
+
generation_state["filme_final"] = final_movie_data
|
| 165 |
+
generation_state["chat_history"] = chat_history
|
| 166 |
+
chat_history.append({"role": "Maestro", "content": "Produção concluída! O filme final está pronto."})
|
| 167 |
+
|
| 168 |
+
logger.info(f"Planner4D: Produção finalizada. Enviando filme '{final_video_path}' para a UI.")
|
| 169 |
+
yield {
|
| 170 |
+
"chat": chat_history,
|
| 171 |
+
"final_video_path": final_video_path,
|
| 172 |
+
"status": "production_complete",
|
| 173 |
+
"dna": generation_state
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
planner_4d_singleton = Planner4D()
|
aduc_framework/engineers/planner_5D.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/planner_5D.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 1.1.0 (Diretor de Produção Alinhado)
|
| 4 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 5 |
+
#
|
| 6 |
+
# - Versão finalizada e alinhada com os especialistas Deformes v12+ e VaeManager v2.
|
| 7 |
+
# - Cria as Ordens de Serviço (Jobs) usando as definições mais recentes de `types.py`.
|
| 8 |
+
# - Delega os cálculos técnicos (ex: poda de frames) para os especialistas,
|
| 9 |
+
# focando-se na orquestração de alto nível e no gerenciamento do loop de feedback visual.
|
| 10 |
+
|
| 11 |
+
import logging
|
| 12 |
+
import os
|
| 13 |
+
from typing import Generator, Dict, Any
|
| 14 |
+
|
| 15 |
+
# Importa os especialistas que serão dirigidos
|
| 16 |
+
from ..engineers.deformes3D import deformes3d_engine_singleton as Deformes3D
|
| 17 |
+
from ..engineers.deformes4D import deformes4d_engine_singleton as Deformes4D
|
| 18 |
+
from ..tools.video_encode_tool import video_encode_tool_singleton as VideoTool
|
| 19 |
+
|
| 20 |
+
# Importa as estruturas de dados (o "DNA" e as "Ordens de Serviço")
|
| 21 |
+
from ..types import (
|
| 22 |
+
GenerationState,
|
| 23 |
+
KeyframeGenerationJob,
|
| 24 |
+
VideoGenerationJob,
|
| 25 |
+
KeyframeData,
|
| 26 |
+
VideoData
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
logger = logging.getLogger(__name__)
|
| 30 |
+
|
| 31 |
+
class Planner5D:
|
| 32 |
+
"""
|
| 33 |
+
Atua como o Diretor de Produção. Orquestra a geração de keyframes e vídeos
|
| 34 |
+
cena a cena, implementando um loop de feedback para garantir continuidade visual.
|
| 35 |
+
"""
|
| 36 |
+
def __init__(self):
|
| 37 |
+
self.dna: GenerationState | None = None
|
| 38 |
+
self.workspace: str | None = None
|
| 39 |
+
self.production_params: Dict[str, Any] = {}
|
| 40 |
+
self.pre_prod_params: Dict[str, Any] = {}
|
| 41 |
+
|
| 42 |
+
def produce_movie_by_scene(self, dna: GenerationState) -> Generator[GenerationState, None, None]:
|
| 43 |
+
"""
|
| 44 |
+
Ponto de entrada para a produção do filme. Executa o loop de produção cena a cena.
|
| 45 |
+
"""
|
| 46 |
+
self.dna = dna
|
| 47 |
+
self.workspace = self.dna.workspace_dir
|
| 48 |
+
|
| 49 |
+
if not (self.dna.parametros_geracao and self.dna.parametros_geracao.pre_producao and self.dna.parametros_geracao.producao):
|
| 50 |
+
raise ValueError("Parâmetros de pré-produção ou produção ausentes no DNA. O processo não pode continuar.")
|
| 51 |
+
|
| 52 |
+
self.pre_prod_params = self.dna.parametros_geracao.pre_producao.model_dump()
|
| 53 |
+
self.production_params = self.dna.parametros_geracao.producao.model_dump()
|
| 54 |
+
|
| 55 |
+
Deformes3D.initialize(self.workspace)
|
| 56 |
+
Deformes4D.initialize(self.workspace)
|
| 57 |
+
|
| 58 |
+
all_final_scene_clips = []
|
| 59 |
+
last_scene_final_frame_path = None
|
| 60 |
+
|
| 61 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": "Ok, storyboard recebido. Iniciando a produção do filme, cena por cena..."})
|
| 62 |
+
yield self.dna
|
| 63 |
+
|
| 64 |
+
for scene_idx, scene in enumerate(self.dna.storyboard_producao):
|
| 65 |
+
scene_title = scene.titulo_cena or f"Cena {scene.id_cena}"
|
| 66 |
+
logger.info(f"--- PLANNER 5D: Iniciando Cena {scene.id_cena}: '{scene_title}' ---")
|
| 67 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"Preparando para filmar a Cena {scene.id_cena}: '{scene_title}'..."})
|
| 68 |
+
yield self.dna
|
| 69 |
+
|
| 70 |
+
# ETAPA 1: GERAÇÃO DE KEYFRAMES
|
| 71 |
+
reference_paths = [ref.caminho for ref in self.dna.midias_referencia if ref.caminho]
|
| 72 |
+
ref_id_to_path_map = {i: path for i, path in enumerate(reference_paths)}
|
| 73 |
+
|
| 74 |
+
keyframe_job = KeyframeGenerationJob(
|
| 75 |
+
storyboard=[ato.NARRATIVA_VISUAL for ato in scene.atos],
|
| 76 |
+
global_prompt=self.dna.texto_global_historia or "",
|
| 77 |
+
ref_image_paths=reference_paths,
|
| 78 |
+
ref_id_to_path_map=ref_id_to_path_map,
|
| 79 |
+
available_ref_ids=list(ref_id_to_path_map.keys()),
|
| 80 |
+
keyframe_prefix=f"s{scene.id_cena:02d}",
|
| 81 |
+
params=self.pre_prod_params
|
| 82 |
+
)
|
| 83 |
+
generated_keyframes_data = Deformes3D.generate_keyframes_from_job(keyframe_job)
|
| 84 |
+
self.dna.storyboard_producao[scene_idx].keyframes = [KeyframeData(**kf) for kf in generated_keyframes_data]
|
| 85 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena}: {len(generated_keyframes_data)} keyframes criados."})
|
| 86 |
+
yield self.dna
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
for scene_idx, scene in enumerate(self.dna.storyboard_producao):
|
| 90 |
+
scene_title = scene.titulo_cena or f"Cena {scene.id_cena}"
|
| 91 |
+
logger.info(f"--- PLANNER 5D: Iniciando Gravação de Cena {scene.id_cena}: '{scene_title}' ---")
|
| 92 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"Filmando a Cena {scene.id_cena}: '{scene_title}'..."})
|
| 93 |
+
yield self.dna
|
| 94 |
+
|
| 95 |
+
# ETAPA 2: GERAÇÃO DO VÍDEO
|
| 96 |
+
if len(generated_keyframes_data) < 1:
|
| 97 |
+
logger.warning(f"Cena {scene.id_cena} tem menos de 1 keyframes. Pulando geração de vídeo.")
|
| 98 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"AVISO: Cena {scene.id_cena} não pôde ser filmada (keyframes insuficientes)."})
|
| 99 |
+
yield self.dna
|
| 100 |
+
continue
|
| 101 |
+
|
| 102 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena}: Luz, câmera, ação! Gerando o clipe de vídeo..."})
|
| 103 |
+
yield self.dna
|
| 104 |
+
|
| 105 |
+
# Criando a Ordem de Serviço para Deformes4D com os parâmetros de alto nível
|
| 106 |
+
video_job = VideoGenerationJob(
|
| 107 |
+
scene_id=scene.id_cena,
|
| 108 |
+
global_prompt=self.dna.texto_global_historia or "",
|
| 109 |
+
storyboard=[ato.NARRATIVA_VISUAL for ato in scene.atos],
|
| 110 |
+
keyframe_paths=[kf['caminho_pixel'] for kf in generated_keyframes_data],
|
| 111 |
+
**self.pre_prod_params,
|
| 112 |
+
**self.production_params
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
video_result = Deformes4D.generate_movie_clip_from_job(video_job)
|
| 116 |
+
scene_clip_path = video_result.get("final_path")
|
| 117 |
+
|
| 118 |
+
if scene_clip_path:
|
| 119 |
+
all_final_scene_clips.append(scene_clip_path)
|
| 120 |
+
video_data = video_result.get("video_data", {})
|
| 121 |
+
self.dna.storyboard_producao[scene_idx].video_gerado = VideoData(**video_data)
|
| 122 |
+
self.dna.caminho_filme_final_bruto = scene_clip_path
|
| 123 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena} filmada! O resultado será usado como ponto de partida para a próxima cena."})
|
| 124 |
+
yield self.dna
|
| 125 |
+
|
| 126 |
+
# ETAPA FINAL: MONTAGEM DO FILME
|
| 127 |
+
if not all_final_scene_clips:
|
| 128 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": "Produção falhou. Nenhum clipe de vídeo foi gerado."})
|
| 129 |
+
yield self.dna
|
| 130 |
+
return
|
| 131 |
+
|
| 132 |
+
self.dna.chat_history.append({"role": "Planner5D", "content": "Todas as cenas foram filmadas. Iniciando a montagem final do filme..."})
|
| 133 |
+
yield self.dna
|
| 134 |
+
|
| 135 |
+
final_movie_path = os.path.join(self.workspace, "filme_final.mp4")
|
| 136 |
+
final_movie_path = VideoTool.concatenate_videos(all_final_scene_clips, final_movie_path, self.workspace)
|
| 137 |
+
|
| 138 |
+
self.dna.caminho_filme_final_bruto = final_movie_path
|
| 139 |
+
self.dna.chat_history.append({"role": "Maestro", "content": "Produção concluída! O filme final está pronto para ser assistido."})
|
| 140 |
+
yield self.dna
|
| 141 |
+
|
| 142 |
+
# --- Instância Singleton ---
|
| 143 |
+
planner_5d_singleton = Planner5D()
|
aduc_framework/engineers/prompt_engine.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/engineers/prompt_engine.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (Provider-Aware Prompt Translator)
|
| 6 |
+
#
|
| 7 |
+
# O PromptEngine atua como o tradutor entre a lógica de tarefa agnóstica
|
| 8 |
+
# do Composer e o formato de prompt específico exigido por um LLM.
|
| 9 |
+
# Esta versão é ciente do provedor (Llama, Gemini, etc.) e aplica os
|
| 10 |
+
# templates de formatação apenas quando necessário (ex: para o Llama),
|
| 11 |
+
# usando um prompt genérico direto para outros (ex: Gemini).
|
| 12 |
+
|
| 13 |
+
import logging
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Dict
|
| 16 |
+
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
class PromptEngine:
|
| 20 |
+
"""
|
| 21 |
+
O PromptEngine traduz prompts genéricos para o formato específico do
|
| 22 |
+
modelo de linguagem alvo. Ele seleciona o template correto com base
|
| 23 |
+
no provedor de LLM ativo e na presença de imagens na requisição.
|
| 24 |
+
"""
|
| 25 |
+
def __init__(self):
|
| 26 |
+
"""
|
| 27 |
+
Inicializa o PromptEngine em um estado "em espera". O provedor
|
| 28 |
+
e os templates são carregados posteriormente pelo Composer.
|
| 29 |
+
"""
|
| 30 |
+
self.provider: str | None = None
|
| 31 |
+
self.model_map_name: str = "llama_3_2_vision" # Padrão para Llama
|
| 32 |
+
self.template_with_image: str | None = None
|
| 33 |
+
self.template_text_only: str | None = None
|
| 34 |
+
logger.info("PromptEngine inicializado (aguardando configuração do provedor pelo Composer).")
|
| 35 |
+
|
| 36 |
+
def _load_model_template(self, map_name: str, template_file: str) -> str:
|
| 37 |
+
"""
|
| 38 |
+
Carrega um arquivo de template de um mapa de modelo específico.
|
| 39 |
+
"""
|
| 40 |
+
map_path = Path(__file__).resolve().parent.parent / "prompts" / "model_maps" / map_name / template_file
|
| 41 |
+
if not map_path.is_file():
|
| 42 |
+
raise FileNotFoundError(f"Template de modelo '{template_file}' não encontrado no mapa '{map_name}' em: {map_path}")
|
| 43 |
+
|
| 44 |
+
with open(map_path, 'r', encoding='utf-8') as f:
|
| 45 |
+
return f.read()
|
| 46 |
+
|
| 47 |
+
def set_provider(self, provider: str):
|
| 48 |
+
"""
|
| 49 |
+
Configura o provedor de LLM ativo (ex: 'llama_multimodal', 'gemini')
|
| 50 |
+
e carrega os templates de formatação específicos, se necessário.
|
| 51 |
+
Este método é chamado pelo Composer durante sua inicialização.
|
| 52 |
+
"""
|
| 53 |
+
if self.provider == provider:
|
| 54 |
+
return # Evita recarregamentos desnecessários
|
| 55 |
+
|
| 56 |
+
self.provider = provider
|
| 57 |
+
logger.info(f"PromptEngine: Provedor de LLM definido como '{self.provider}'.")
|
| 58 |
+
|
| 59 |
+
# Carrega os templates específicos do Llama apenas se ele for o provedor.
|
| 60 |
+
# Para o Gemini, nenhum template é necessário, então os atributos permanecem None.
|
| 61 |
+
if self.provider == 'llama_multimodal':
|
| 62 |
+
logger.info(f"Carregando templates para o mapa de modelo '{self.model_map_name}'...")
|
| 63 |
+
self.template_with_image = self._load_model_template(self.model_map_name, "image_template.txt")
|
| 64 |
+
self.template_text_only = self._load_model_template(self.model_map_name, "text_template.txt")
|
| 65 |
+
else:
|
| 66 |
+
self.template_with_image = None
|
| 67 |
+
self.template_text_only = None
|
| 68 |
+
|
| 69 |
+
def translate(self, generic_prompt_content: str, has_image: bool) -> str:
|
| 70 |
+
"""
|
| 71 |
+
Envolve o conteúdo do prompt genérico com o template específico do modelo,
|
| 72 |
+
mas somente se o provedor ativo (Llama) exigir. Para provedores como o
|
| 73 |
+
Gemini, retorna o prompt genérico sem modificação.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
generic_prompt_content (str): O conteúdo do prompt agnóstico gerado pelo Composer.
|
| 77 |
+
has_image (bool): Sinaliza se a tarefa atual inclui um contexto visual.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
str: O prompt final, formatado e pronto para ser enviado ao LLM.
|
| 81 |
+
"""
|
| 82 |
+
if self.provider is None:
|
| 83 |
+
raise RuntimeError("PromptEngine: O provedor não foi definido. Chame set_provider() antes de usar.")
|
| 84 |
+
|
| 85 |
+
# --- LÓGICA CONDICIONAL ---
|
| 86 |
+
# Se o provedor for Gemini, ele usa o "prompt padrão genérico" sem modificação.
|
| 87 |
+
if self.provider == 'gemini':
|
| 88 |
+
logger.debug("PROMPT ENGINE (Gemini): Retornando prompt genérico sem aplicar template.")
|
| 89 |
+
return generic_prompt_content
|
| 90 |
+
|
| 91 |
+
# A lógica original do Llama é executada para o provedor padrão ('llama_multimodal')
|
| 92 |
+
logger.debug(f"--- PROMPT ENGINE ({self.provider}): INICIANDO TRADUÇÃO (Imagem: {has_image}) ---")
|
| 93 |
+
|
| 94 |
+
template = self.template_with_image if has_image else self.template_text_only
|
| 95 |
+
if not template:
|
| 96 |
+
raise RuntimeError(f"PromptEngine: Template para o provedor '{self.provider}' não foi carregado corretamente.")
|
| 97 |
+
|
| 98 |
+
try:
|
| 99 |
+
# Insere o conteúdo genérico dentro do template específico do modelo
|
| 100 |
+
final_prompt = template.format(generic_prompt_content=generic_prompt_content)
|
| 101 |
+
logger.debug(f"--- PROMPT ENGINE ({self.provider}): TRADUÇÃO CONCLUÍDA ---")
|
| 102 |
+
return final_prompt
|
| 103 |
+
except KeyError as e:
|
| 104 |
+
logger.error(f"PROMPT ENGINE: Erro de chave durante a tradução! Chave não encontrada: {e}", exc_info=True)
|
| 105 |
+
logger.error("Verifique se o template do modelo contém apenas o placeholder '{generic_prompt_content}'.")
|
| 106 |
+
raise e
|
| 107 |
+
|
| 108 |
+
# --- Instância Singleton ---
|
| 109 |
+
# A instância é criada vazia e será configurada dinamicamente pelo Composer.
|
| 110 |
+
prompt_engine_singleton = PromptEngine()
|
aduc_framework/managers/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Euia-AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR para geração de vídeo coerente.
|
| 2 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 3 |
+
#
|
| 4 |
+
# Contato:
|
| 5 |
+
# Carlos Rodrigues dos Santos
|
| 6 | |
| 7 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 8 |
+
#
|
| 9 |
+
# Repositórios e Projetos Relacionados:
|
| 10 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 11 |
+
# Hugging Face (Ltx-SuperTime-60Secondos): https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/
|
| 12 |
+
# Hugging Face (Novinho): https://huggingface.co/spaces/Carlexxx/Novinho/
|
| 13 |
+
#
|
| 14 |
+
# This program is free software: you can redistribute it and/or modify
|
| 15 |
+
# it under the terms of the GNU Affero General Public License as published by
|
| 16 |
+
# the Free Software Foundation, either version 3 of the License, or
|
| 17 |
+
# (at your option) any later version.
|
| 18 |
+
#
|
| 19 |
+
# This program is distributed in the hope that it will be useful,
|
| 20 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 21 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 22 |
+
# GNU Affero General Public License for more details.
|
| 23 |
+
#
|
| 24 |
+
# You should have received a copy of the GNU Affero General Public License
|
| 25 |
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
aduc_framework/managers/LICENSE.txt
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
aduc_framework/managers/NOTICE.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTICE
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2025 Carlos Rodrigues dos Santos. All rights reserved.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Aviso de Propriedade Intelectual e Licenciamento
|
| 8 |
+
|
| 9 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 10 |
+
|
| 11 |
+
O método e o sistema de orquestração de prompts denominados **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste documento e implementados neste software, estão atualmente em processo de patenteamento.
|
| 12 |
+
|
| 13 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, incluindo, mas não se limitando a:
|
| 14 |
+
|
| 15 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 16 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 17 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 18 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 19 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 20 |
+
|
| 21 |
+
### **Reconhecimento e Implicações (EM PORTUGUÊS):**
|
| 22 |
+
|
| 23 |
+
Ao acessar ou utilizar este software e a arquitetura ADUC aqui implementada, você reconhece:
|
| 24 |
+
|
| 25 |
+
1. A natureza inovadora e a importância da arquitetura ADUC no campo da orquestração de prompts para IA.
|
| 26 |
+
2. Que a essência desta arquitetura, ou suas implementações derivadas, podem estar sujeitas a direitos de propriedade intelectual, incluindo patentes.
|
| 27 |
+
3. Que o uso comercial, a reprodução da lógica central da ADUC em sistemas independentes, ou a exploração direta da invenção sem o devido licenciamento podem infringir os direitos de patente pendente.
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
### **Patent Pending (IN ENGLISH):**
|
| 32 |
+
|
| 33 |
+
The method and system for prompt orchestration named **ADUC (Automated Discovery and Orchestration of Complex tasks)**, as described herein and implemented in this software, are currently in the process of being patented.
|
| 34 |
+
|
| 35 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 36 |
+
|
| 37 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 38 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 39 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 40 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 41 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 42 |
+
|
| 43 |
+
### **Acknowledgement and Implications (IN ENGLISH):**
|
| 44 |
+
|
| 45 |
+
By accessing or using this software and the ADUC architecture implemented herein, you acknowledge:
|
| 46 |
+
|
| 47 |
+
1. The innovative nature and significance of the ADUC architecture in the field of AI prompt orchestration.
|
| 48 |
+
2. That the essence of this architecture, or its derivative implementations, may be subject to intellectual property rights, including patents.
|
| 49 |
+
3. That commercial use, reproduction of ADUC's core logic in independent systems, or direct exploitation of the invention without proper licensing may infringe upon pending patent rights.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
**Contato para Consultas:**
|
| 55 |
+
|
| 56 |
+
Para mais informações sobre a arquitetura ADUC, o status do patenteamento, ou para discutir licenciamento para usos comerciais ou não conformes com a AGPLv3, por favor, entre em contato:
|
| 57 |
+
|
| 58 |
+
Carlos Rodrigues dos Santos
|
| 59 | |
| 60 |
+
Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
aduc_framework/managers/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🛠️ managers/ - Ferramentas de IA de Terceiros para orquestração ADUC-SDR
|
| 2 |
+
|
| 3 |
+
Esta pasta contém implementações adaptadas de modelos e utilitários de IA de terceiros, que servem como "especialistas" ou "ferramentas" de baixo nível para a arquitetura ADUC-SDR.
|
| 4 |
+
|
| 5 |
+
**IMPORTANTE:** O conteúdo desta pasta é de autoria de seus respectivos idealizadores e desenvolvedores originais. Esta pasta **NÃO FAZ PARTE** do projeto principal ADUC-SDR em termos de sua arquitetura inovadora. Ela serve como um repositório para as **dependências diretas e modificadas** que os `Deformes enginers` (os estágios do "foguete" ADUC-SDR) invocam para realizar tarefas específicas (geração de imagem, vídeo, áudio).
|
| 6 |
+
|
| 7 |
+
As modificações realizadas nos arquivos aqui presentes visam principalmente:
|
| 8 |
+
1. **Adaptação de Interfaces:** Padronizar as interfaces para que se encaixem no fluxo de orquestração do ADUC-SDR.
|
| 9 |
+
2. **Gerenciamento de Recursos:** Integrar lógicas de carregamento/descarregamento de modelos (GPU management) e configurações via arquivos YAML.
|
| 10 |
+
3. **Otimização de Fluxo:** Ajustar as pipelines para aceitar formatos de entrada mais eficientes (ex: tensores pré-codificados em vez de caminhos de mídia, pulando etapas de codificação/decodificação redundantes).
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## 📄 Licenciamento
|
| 15 |
+
|
| 16 |
+
O conteúdo original dos projetos listados abaixo é licenciado sob a **Licença Apache 2.0**, ou outra licença especificada pelos autores originais. Todas as modificações e o uso desses arquivos dentro da estrutura `helpers/` do projeto ADUC-SDR estão em conformidade com os termos da **Licença Apache 2.0**.
|
| 17 |
+
|
| 18 |
+
As licenças originais dos projetos podem ser encontradas nas suas respectivas fontes ou nos subdiretórios `incl_licenses/` dentro de cada módulo adaptado.
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
## 🛠️ API dos Helpers e Guia de Uso
|
| 23 |
+
|
| 24 |
+
Esta seção detalha como cada helper (agente especialista) deve ser utilizado dentro do ecossistema ADUC-SDR. Todos os agentes são instanciados como **singletons** no `hardware_manager.py` para garantir o gerenciamento centralizado de recursos de GPU.
|
| 25 |
+
|
| 26 |
+
### **gemini_helpers.py (GeminiAgent)**
|
| 27 |
+
|
| 28 |
+
* **Propósito:** Atua como o "Oráculo de Síntese Adaptativo", responsável por todas as tarefas de processamento de linguagem natural, como criação de storyboards, geração de prompts, e tomada de decisões narrativas.
|
| 29 |
+
* **Singleton Instance:** `gemini_agent_singleton`
|
| 30 |
+
* **Construtor:** `GeminiAgent()`
|
| 31 |
+
* Lê `configs/gemini_config.yaml` para obter o nome do modelo, parâmetros de inferência e caminhos de templates de prompt. A chave da API é lida da variável de ambiente `GEMINI_API_KEY`.
|
| 32 |
+
* **Métodos Públicos:**
|
| 33 |
+
* `generate_storyboard(prompt: str, num_keyframes: int, ref_image_paths: list[str])`
|
| 34 |
+
* **Inputs:**
|
| 35 |
+
* `prompt`: A ideia geral do filme (string).
|
| 36 |
+
* `num_keyframes`: O número de cenas a serem geradas (int).
|
| 37 |
+
* `ref_image_paths`: Lista de caminhos para as imagens de referência (list[str]).
|
| 38 |
+
* **Output:** `tuple[list[str], str]` (Uma tupla contendo a lista de strings do storyboard e um relatório textual da operação).
|
| 39 |
+
* `select_keyframes_from_pool(storyboard: list, base_image_paths: list[str], pool_image_paths: list[str])`
|
| 40 |
+
* **Inputs:**
|
| 41 |
+
* `storyboard`: A lista de strings do storyboard gerado.
|
| 42 |
+
* `base_image_paths`: Imagens de referência base (list[str]).
|
| 43 |
+
* `pool_image_paths`: O "banco de imagens" de onde selecionar (list[str]).
|
| 44 |
+
* **Output:** `tuple[list[str], str]` (Uma tupla contendo a lista de caminhos de imagens selecionadas e um relatório textual).
|
| 45 |
+
* `get_anticipatory_keyframe_prompt(...)`
|
| 46 |
+
* **Inputs:** Contexto narrativo e visual para gerar um prompt de imagem.
|
| 47 |
+
* **Output:** `tuple[str, str]` (Uma tupla contendo o prompt gerado para o modelo de imagem e um relatório textual).
|
| 48 |
+
* `get_initial_motion_prompt(...)`
|
| 49 |
+
* **Inputs:** Contexto narrativo e visual para a primeira transição de vídeo.
|
| 50 |
+
* **Output:** `tuple[str, str]` (Uma tupla contendo o prompt de movimento gerado e um relatório textual).
|
| 51 |
+
* `get_transition_decision(...)`
|
| 52 |
+
* **Inputs:** Contexto narrativo e visual para uma transição de vídeo intermediária.
|
| 53 |
+
* **Output:** `tuple[dict, str]` (Uma tupla contendo um dicionário `{"transition_type": "...", "motion_prompt": "..."}` e um relatório textual).
|
| 54 |
+
* `generate_audio_prompts(...)`
|
| 55 |
+
* **Inputs:** Contexto narrativo global.
|
| 56 |
+
* **Output:** `tuple[dict, str]` (Uma tupla contendo um dicionário `{"music_prompt": "...", "sfx_prompt": "..."}` e um relatório textual).
|
| 57 |
+
|
| 58 |
+
### **flux_kontext_helpers.py (FluxPoolManager)**
|
| 59 |
+
|
| 60 |
+
* **Propósito:** Especialista em geração de imagens de alta qualidade (keyframes) usando a pipeline FluxKontext. Gerencia um pool de workers para otimizar o uso de múltiplas GPUs.
|
| 61 |
+
* **Singleton Instance:** `flux_kontext_singleton`
|
| 62 |
+
* **Construtor:** `FluxPoolManager(device_ids: list[str], flux_config_file: str)`
|
| 63 |
+
* Lê `configs/flux_config.yaml`.
|
| 64 |
+
* **Método Público:**
|
| 65 |
+
* `generate_image(prompt: str, reference_images: list[Image.Image], width: int, height: int, seed: int = 42, callback: callable = None)`
|
| 66 |
+
* **Inputs:**
|
| 67 |
+
* `prompt`: Prompt textual para guiar a geração (string).
|
| 68 |
+
* `reference_images`: Lista de objetos `PIL.Image` como referência visual.
|
| 69 |
+
* `width`, `height`: Dimensões da imagem de saída (int).
|
| 70 |
+
* `seed`: Semente para reprodutibilidade (int).
|
| 71 |
+
* `callback`: Função de callback opcional para monitorar o progresso.
|
| 72 |
+
* **Output:** `PIL.Image.Image` (O objeto da imagem gerada).
|
| 73 |
+
|
| 74 |
+
### **dreamo_helpers.py (DreamOAgent)**
|
| 75 |
+
|
| 76 |
+
* **Propósito:** Especialista em geração de imagens de alta qualidade (keyframes) usando a pipeline DreamO, com capacidades avançadas de edição e estilo a partir de referências.
|
| 77 |
+
* **Singleton Instance:** `dreamo_agent_singleton`
|
| 78 |
+
* **Construtor:** `DreamOAgent(device_id: str = None)`
|
| 79 |
+
* Lê `configs/dreamo_config.yaml`.
|
| 80 |
+
* **Método Público:**
|
| 81 |
+
* `generate_image(prompt: str, reference_images: list[Image.Image], width: int, height: int)`
|
| 82 |
+
* **Inputs:**
|
| 83 |
+
* `prompt`: Prompt textual para guiar a geração (string).
|
| 84 |
+
* `reference_images`: Lista de objetos `PIL.Image` como referência visual. A lógica interna atribui a primeira imagem como `style` e as demais como `ip`.
|
| 85 |
+
* `width`, `height`: Dimensões da imagem de saída (int).
|
| 86 |
+
* **Output:** `PIL.Image.Image` (O objeto da imagem gerada).
|
| 87 |
+
|
| 88 |
+
### **ltx_manager_helpers.py (LtxPoolManager)**
|
| 89 |
+
|
| 90 |
+
* **Propósito:** Especialista na geração de fragmentos de vídeo no espaço latente usando a pipeline LTX-Video. Gerencia um pool de workers para otimizar o uso de múltiplas GPUs.
|
| 91 |
+
* **Singleton Instance:** `ltx_manager_singleton`
|
| 92 |
+
* **Construtor:** `LtxPoolManager(device_ids: list[str], ltx_model_config_file: str, ltx_global_config_file: str)`
|
| 93 |
+
* Lê o `ltx_global_config_file` e o `ltx_model_config_file` para configurar a pipeline.
|
| 94 |
+
* **Método Público:**
|
| 95 |
+
* `generate_latent_fragment(**kwargs)`
|
| 96 |
+
* **Inputs:** Dicionário de keyword arguments (`kwargs`) contendo todos os parâmetros da pipeline LTX, incluindo:
|
| 97 |
+
* `height`, `width`: Dimensões do vídeo (int).
|
| 98 |
+
* `video_total_frames`: Número total de frames a serem gerados (int).
|
| 99 |
+
* `video_fps`: Frames por segundo (int).
|
| 100 |
+
* `motion_prompt`: Prompt de movimento (string).
|
| 101 |
+
* `conditioning_items_data`: Lista de objetos `LatentConditioningItem` contendo os tensores latentes de condição.
|
| 102 |
+
* `guidance_scale`, `stg_scale`, `num_inference_steps`, etc.
|
| 103 |
+
* **Output:** `tuple[torch.Tensor, tuple]` (Uma tupla contendo o tensor latente gerado e os valores de padding utilizados).
|
| 104 |
+
|
| 105 |
+
### **mmaudio_helper.py (MMAudioAgent)**
|
| 106 |
+
|
| 107 |
+
* **Propósito:** Especialista em geração de áudio para um determinado fragmento de vídeo.
|
| 108 |
+
* **Singleton Instance:** `mmaudio_agent_singleton`
|
| 109 |
+
* **Construtor:** `MMAudioAgent(workspace_dir: str, device_id: str = None, mmaudio_config_file: str)`
|
| 110 |
+
* Lê `configs/mmaudio_config.yaml`.
|
| 111 |
+
* **Método Público:**
|
| 112 |
+
* `generate_audio_for_video(video_path: str, prompt: str, negative_prompt: str, duration_seconds: float)`
|
| 113 |
+
* **Inputs:**
|
| 114 |
+
* `video_path`: Caminho para o arquivo de vídeo silencioso (string).
|
| 115 |
+
* `prompt`: Prompt textual para guiar a geração de áudio (string).
|
| 116 |
+
* `negative_prompt`: Prompt negativo para áudio (string).
|
| 117 |
+
* `duration_seconds`: Duração exata do vídeo (float).
|
| 118 |
+
* **Output:** `str` (O caminho para o novo arquivo de vídeo com a faixa de áudio integrada).
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
### **seedvr_helpers.py (SeedVrManager)**
|
| 122 |
+
|
| 123 |
+
* **Propósito:** Especialista em pós-produção de vídeo, aplicando super-resolução com IA (`Video Super-Resolution`) para adicionar detalhes finos, nitidez e texturas realistas a um vídeo já renderizado.
|
| 124 |
+
* **Singleton Instance:** `seedvr_manager_singleton`
|
| 125 |
+
* **Construtor:** `SeedVrManager(workspace_dir: str, device_id: str = None)`
|
| 126 |
+
* Lê `configs/seedvr_config.yaml`.
|
| 127 |
+
* **Método Público:**
|
| 128 |
+
* `process_video(input_video_path: str, output_video_path: str, prompt: str, model_version: str = '7B', steps: int = 100, seed: int = 666)`
|
| 129 |
+
* **Inputs:**
|
| 130 |
+
* `input_video_path`: Caminho para o vídeo de entrada a ser aprimorado (string).
|
| 131 |
+
* `output_video_path`: Caminho onde o vídeo finalizado será salvo (string).
|
| 132 |
+
* `prompt`: Um prompt de estilo geral para guiar o aprimoramento (string).
|
| 133 |
+
* `model_version`: A versão do modelo a ser usada, '3B' ou '7B' (string).
|
| 134 |
+
* `steps`: Número de passos de inferência para o processo de aprimoramento (int).
|
| 135 |
+
* `seed`: Semente para reprodutibilidade (int).
|
| 136 |
+
* **Output:** `str` (O caminho para o vídeo finalizado em alta definição).
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## 🔗 Projetos Originais e Atribuições
|
| 141 |
+
(A seção de atribuições e licenças permanece a mesma que definimos anteriormente)
|
| 142 |
+
|
| 143 |
+
### DreamO
|
| 144 |
+
* **Repositório Original:** [https://github.com/bytedance/DreamO](https://github.com/bytedance/DreamO)
|
| 145 |
+
...
|
| 146 |
+
|
| 147 |
+
### LTX-Video
|
| 148 |
+
* **Repositório Original:** [https://github.com/Lightricks/LTX-Video](https://github.com/Lightricks/LTX-Video)
|
| 149 |
+
...
|
| 150 |
+
|
| 151 |
+
### MMAudio
|
| 152 |
+
* **Repositório Original:** [https://github.com/hkchengrex/MMAudio](https://github.com/hkchengrex/MMAudio)
|
| 153 |
+
...
|
| 154 |
+
|
| 155 |
+
### SeedVr
|
| 156 |
+
* **Repositório Original:** [https://github.com/ByteDance-Seed/SeedVR](https://github.com/ByteDance-Seed/SeedVR)
|
aduc_framework/managers/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Em: aduc_framework/managers/__init__.py
|
| 2 |
+
|
| 3 |
+
# Expõe os singletons dos managers de especialistas de baixo nível.
|
| 4 |
+
|
| 5 |
+
from .gemini_manager import gemini_manager_singleton
|
| 6 |
+
|
| 7 |
+
# ORDEM CRÍTICA: LTX Manager precisa ser inicializado primeiro, pois o VAE Manager
|
| 8 |
+
# depende dele para obter a instância do modelo VAE.
|
| 9 |
+
from .ltx_manager import ltx_manager_singleton
|
| 10 |
+
from .vae_manager import vae_manager_singleton
|
| 11 |
+
|
| 12 |
+
from .latent_enhancer_manager import latent_enhancer_specialist_singleton
|
| 13 |
+
from .mmaudio_manager import mmaudio_manager_singleton
|
| 14 |
+
from .seedvr_manager import seedvr_manager_singleton
|
| 15 |
+
from .llama_multimodal_manager import llama_multimodal_manager_singleton
|
| 16 |
+
|
| 17 |
+
__all__ = [
|
| 18 |
+
"gemini_manager_singleton",
|
| 19 |
+
"ltx_manager_singleton",
|
| 20 |
+
"vae_manager_singleton", # A ordem aqui não importa, apenas a ordem dos imports acima
|
| 21 |
+
"latent_enhancer_specialist_singleton",
|
| 22 |
+
"mmaudio_manager_singleton",
|
| 23 |
+
"seedvr_manager_singleton",
|
| 24 |
+
"llama_multimodal_manager_singleton",
|
| 25 |
+
]
|
aduc_framework/managers/config.yaml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# config.yaml
|
| 2 |
+
# Configuração central para a aplicação Deformes4D e seus especialistas.
|
| 3 |
+
|
| 4 |
+
application:
|
| 5 |
+
workspace_dir: "deformes_workspace"
|
| 6 |
+
|
| 7 |
+
# Configuração para Hugging Face Spaces
|
| 8 |
+
sdk: gradio
|
| 9 |
+
app_file: app.py
|
| 10 |
+
|
| 11 |
+
specialists:
|
| 12 |
+
flux:
|
| 13 |
+
# Define quantas GPUs o pool do Flux deve tentar alocar.
|
| 14 |
+
# Se não houver GPUs suficientes, o hardware_manager lançará um erro.
|
| 15 |
+
# Se 0, usará a CPU.
|
| 16 |
+
gpus_required: 4
|
| 17 |
+
|
| 18 |
+
ltx:
|
| 19 |
+
# Define quantas GPUs o pool do LTX deve tentar alocar.
|
| 20 |
+
gpus_required: 4
|
| 21 |
+
|
| 22 |
+
# Aponta para o arquivo de configuração específico do modelo LTX.
|
| 23 |
+
# Alterado para usar o modelo 0.9.8-dev.
|
| 24 |
+
config_file: "ltxv-13b-0.9.8-distilled.yaml"
|
aduc_framework/managers/flux_kontext_manager.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# flux_kontext_helpers.py (ADUC: O Especialista Pintor - com suporte a callback)
|
| 2 |
+
# AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 3 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Contato:
|
| 6 |
+
# Carlos Rodrigues dos Santos
|
| 7 | |
| 8 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 9 |
+
#
|
| 10 |
+
# Repositórios e Projetos Relacionados:
|
| 11 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 12 |
+
#
|
| 13 |
+
#
|
| 14 |
+
# PENDING PATENT NOTICE: Please see NOTICE.md.
|
| 15 |
+
#
|
| 16 |
+
# Version 1.0.1
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
from PIL import Image, ImageOps
|
| 20 |
+
import gc
|
| 21 |
+
from diffusers import FluxKontextPipeline
|
| 22 |
+
import huggingface_hub
|
| 23 |
+
import os
|
| 24 |
+
import threading
|
| 25 |
+
import yaml
|
| 26 |
+
import logging
|
| 27 |
+
|
| 28 |
+
from ..tools.hardware_manager import hardware_manager
|
| 29 |
+
|
| 30 |
+
logger = logging.getLogger(__name__)
|
| 31 |
+
|
| 32 |
+
class FluxWorker:
|
| 33 |
+
"""Representa uma única instância do pipeline FluxKontext em um dispositivo."""
|
| 34 |
+
def __init__(self, device_id='cuda:0'):
|
| 35 |
+
self.cpu_device = torch.device('cpu')
|
| 36 |
+
self.device = torch.device(device_id if torch.cuda.is_available() else 'cpu')
|
| 37 |
+
self.pipe = None
|
| 38 |
+
self._load_pipe_to_cpu()
|
| 39 |
+
|
| 40 |
+
def _load_pipe_to_cpu(self):
|
| 41 |
+
if self.pipe is None:
|
| 42 |
+
logger.info(f"FLUX Worker ({self.device}): Carregando modelo para a CPU...")
|
| 43 |
+
self.pipe = FluxKontextPipeline.from_pretrained(
|
| 44 |
+
"black-forest-labs/FLUX.1-Kontext-dev", torch_dtype=torch.bfloat16
|
| 45 |
+
).to(self.cpu_device)
|
| 46 |
+
logger.info(f"FLUX Worker ({self.device}): Modelo pronto na CPU.")
|
| 47 |
+
|
| 48 |
+
def to_gpu(self):
|
| 49 |
+
if self.device.type == 'cpu': return
|
| 50 |
+
logger.info(f"FLUX Worker: Movendo modelo para a GPU {self.device}...")
|
| 51 |
+
self.pipe.to(self.device)
|
| 52 |
+
|
| 53 |
+
def to_cpu(self):
|
| 54 |
+
if self.device.type == 'cpu': return
|
| 55 |
+
logger.info(f"FLUX Worker: Descarregando modelo da GPU {self.device}...")
|
| 56 |
+
self.pipe.to(self.cpu_device)
|
| 57 |
+
gc.collect()
|
| 58 |
+
if torch.cuda.is_available(): torch.cuda.empty_cache()
|
| 59 |
+
|
| 60 |
+
def _create_composite_reference(self, images: list[Image.Image], target_width: int, target_height: int) -> Image.Image:
|
| 61 |
+
if not images: return None
|
| 62 |
+
valid_images = [img.convert("RGB") for img in images if img is not None]
|
| 63 |
+
if not valid_images: return None
|
| 64 |
+
if len(valid_images) == 1:
|
| 65 |
+
if valid_images[0].size != (target_width, target_height):
|
| 66 |
+
return ImageOps.fit(valid_images[0], (target_width, target_height), Image.Resampling.LANCZOS)
|
| 67 |
+
return valid_images[0]
|
| 68 |
+
|
| 69 |
+
base_height = valid_images[0].height
|
| 70 |
+
resized_for_concat = []
|
| 71 |
+
for img in valid_images:
|
| 72 |
+
if img.height != base_height:
|
| 73 |
+
aspect_ratio = img.width / img.height
|
| 74 |
+
new_width = int(base_height * aspect_ratio)
|
| 75 |
+
resized_for_concat.append(img.resize((new_width, base_height), Image.Resampling.LANCZOS))
|
| 76 |
+
else:
|
| 77 |
+
resized_for_concat.append(img)
|
| 78 |
+
|
| 79 |
+
total_width = sum(img.width for img in resized_for_concat)
|
| 80 |
+
concatenated = Image.new('RGB', (total_width, base_height))
|
| 81 |
+
x_offset = 0
|
| 82 |
+
for img in resized_for_concat:
|
| 83 |
+
concatenated.paste(img, (x_offset, 0))
|
| 84 |
+
x_offset += img.width
|
| 85 |
+
|
| 86 |
+
#final_reference = ImageOps.fit(concatenated, (target_width, target_height), Image.Resampling.LANCZOS)
|
| 87 |
+
return concatenated
|
| 88 |
+
|
| 89 |
+
@torch.inference_mode()
|
| 90 |
+
def generate_image_internal(self, reference_images: list[Image.Image], prompt: str, target_width: int, target_height: int, seed: int, callback: callable = None):
|
| 91 |
+
composite_reference = self._create_composite_reference(reference_images, target_width, target_height)
|
| 92 |
+
|
| 93 |
+
num_steps = 12 # Valor fixo otimizado
|
| 94 |
+
|
| 95 |
+
logger.info(f"\n===== [CHAMADA AO PIPELINE FLUX em {self.device}] =====\n"
|
| 96 |
+
f" - Prompt: '{prompt}'\n"
|
| 97 |
+
f" - Resolução: {target_width}x{target_height}, Seed: {seed}, Passos: {num_steps}\n"
|
| 98 |
+
f" - Nº de Imagens na Composição: {len(reference_images)}\n"
|
| 99 |
+
f"==========================================")
|
| 100 |
+
|
| 101 |
+
generated_image = self.pipe(
|
| 102 |
+
image=composite_reference,
|
| 103 |
+
prompt=prompt,
|
| 104 |
+
guidance_scale=2.5,
|
| 105 |
+
width=target_width,
|
| 106 |
+
height=target_height,
|
| 107 |
+
num_inference_steps=num_steps,
|
| 108 |
+
generator=torch.Generator(device="cpu").manual_seed(seed),
|
| 109 |
+
callback_on_step_end=callback,
|
| 110 |
+
callback_on_step_end_tensor_inputs=["latents"] if callback else None
|
| 111 |
+
).images[0]
|
| 112 |
+
|
| 113 |
+
return generated_image
|
| 114 |
+
|
| 115 |
+
class FluxPoolManager:
|
| 116 |
+
def __init__(self, device_ids):
|
| 117 |
+
logger.info(f"FLUX POOL MANAGER: Criando workers para os dispositivos: {device_ids}")
|
| 118 |
+
self.workers = [FluxWorker(device_id) for device_id in device_ids]
|
| 119 |
+
self.current_worker_index = 0
|
| 120 |
+
self.lock = threading.Lock()
|
| 121 |
+
self.last_cleanup_thread = None
|
| 122 |
+
|
| 123 |
+
def _cleanup_worker_thread(self, worker):
|
| 124 |
+
logger.info(f"FLUX CLEANUP THREAD: Iniciando limpeza de {worker.device} em background...")
|
| 125 |
+
worker.to_cpu()
|
| 126 |
+
|
| 127 |
+
def generate_image(self, reference_images, prompt, width, height, seed=42, callback=None):
|
| 128 |
+
worker_to_use = None
|
| 129 |
+
try:
|
| 130 |
+
with self.lock:
|
| 131 |
+
if self.last_cleanup_thread and self.last_cleanup_thread.is_alive():
|
| 132 |
+
self.last_cleanup_thread.join()
|
| 133 |
+
worker_to_use = self.workers[self.current_worker_index]
|
| 134 |
+
previous_worker_index = (self.current_worker_index - 1 + len(self.workers)) % len(self.workers)
|
| 135 |
+
worker_to_cleanup = self.workers[previous_worker_index]
|
| 136 |
+
cleanup_thread = threading.Thread(target=self._cleanup_worker_thread, args=(worker_to_cleanup,))
|
| 137 |
+
cleanup_thread.start()
|
| 138 |
+
self.last_cleanup_thread = cleanup_thread
|
| 139 |
+
worker_to_use.to_gpu()
|
| 140 |
+
self.current_worker_index = (self.current_worker_index + 1) % len(self.workers)
|
| 141 |
+
|
| 142 |
+
logger.info(f"FLUX POOL MANAGER: Gerando imagem em {worker_to_use.device}...")
|
| 143 |
+
return worker_to_use.generate_image_internal(
|
| 144 |
+
reference_images=reference_images,
|
| 145 |
+
prompt=prompt,
|
| 146 |
+
target_width=width,
|
| 147 |
+
target_height=height,
|
| 148 |
+
seed=seed,
|
| 149 |
+
callback=callback
|
| 150 |
+
)
|
| 151 |
+
except Exception as e:
|
| 152 |
+
logger.error(f"FLUX POOL MANAGER: Erro durante a geração: {e}", exc_info=True)
|
| 153 |
+
raise e
|
| 154 |
+
finally:
|
| 155 |
+
pass
|
| 156 |
+
|
| 157 |
+
# --- Instanciação Singleton Dinâmica ---
|
| 158 |
+
logger.info("Lendo config.yaml para inicializar o FluxKontext Pool Manager...")
|
| 159 |
+
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 160 |
+
hf_token = os.getenv('HF_TOKEN');
|
| 161 |
+
if hf_token: huggingface_hub.login(token=hf_token)
|
| 162 |
+
flux_gpus_required = config['specialists']['flux']['gpus_required']
|
| 163 |
+
flux_device_ids = hardware_manager.allocate_gpus('Flux', flux_gpus_required)
|
| 164 |
+
flux_kontext_singleton = FluxPoolManager(device_ids=flux_device_ids)
|
| 165 |
+
logger.info("Especialista de Imagem (Flux) pronto.")
|
aduc_framework/managers/gemini_manager.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/managers/gemini_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 3.0.0 (Com Gerenciamento de Memória Flexível)
|
| 4 |
+
#
|
| 5 |
+
# Esta versão evolui o GeminiManager para suportar conversas com estado
|
| 6 |
+
# (com memória), mantendo a capacidade de operar sem estado.
|
| 7 |
+
# Introduz um gatilho `restart_session=True` para controle explícito
|
| 8 |
+
# sobre o ciclo de vida da memória, evitando contaminação de contexto.
|
| 9 |
+
|
| 10 |
+
import os
|
| 11 |
+
import logging
|
| 12 |
+
from PIL import Image
|
| 13 |
+
import google.generativeai as genai
|
| 14 |
+
from typing import List, Any, Optional
|
| 15 |
+
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
class GeminiManager:
|
| 19 |
+
"""
|
| 20 |
+
Gerencia interações com a API do Google Gemini, com suporte opcional
|
| 21 |
+
para sessões de chat com memória e um gatilho de reinicialização.
|
| 22 |
+
"""
|
| 23 |
+
def __init__(self):
|
| 24 |
+
"""
|
| 25 |
+
Inicializa o manager. Carrega a chave da API e configura o modelo.
|
| 26 |
+
"""
|
| 27 |
+
self.api_key = os.environ.get("GEMINI_API_KEY")
|
| 28 |
+
self.model = None
|
| 29 |
+
self.chat_session = None # Armazena a sessão de chat com memória ativa
|
| 30 |
+
|
| 31 |
+
if self.api_key:
|
| 32 |
+
try:
|
| 33 |
+
genai.configure(api_key=self.api_key)
|
| 34 |
+
# Usando um modelo recente e capaz
|
| 35 |
+
self.model = genai.GenerativeModel('gemini-2.0-flash')
|
| 36 |
+
logger.info("GeminiManager (V3 com Memória) inicializado com sucesso.")
|
| 37 |
+
except Exception as e:
|
| 38 |
+
logger.error(f"Falha ao configurar a API do Gemini. Verifique a chave e as permissões. Erro: {e}")
|
| 39 |
+
self.model = None
|
| 40 |
+
else:
|
| 41 |
+
logger.warning("Variável de ambiente GEMINI_API_KEY não encontrada. GeminiManager está desabilitado.")
|
| 42 |
+
|
| 43 |
+
def _check_model(self):
|
| 44 |
+
"""Verificação interna para garantir que o modelo está pronto para uso."""
|
| 45 |
+
if not self.model:
|
| 46 |
+
raise RuntimeError("O modelo Gemini não está inicializado. Verifique a chave da API (GEMINI_API_KEY).")
|
| 47 |
+
|
| 48 |
+
def process_turn(
|
| 49 |
+
self,
|
| 50 |
+
prompt_text: str,
|
| 51 |
+
image_list: Optional[List[Image.Image]] = None,
|
| 52 |
+
use_memory: bool = False,
|
| 53 |
+
restart_session: bool = False
|
| 54 |
+
) -> str:
|
| 55 |
+
"""
|
| 56 |
+
Ponto de entrada principal. Processa uma requisição textual ou multimodal,
|
| 57 |
+
com gerenciamento de memória opcional.
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
prompt_text (str): O prompt textual a ser enviado.
|
| 61 |
+
image_list (Optional[List[Image.Image]]): Lista de imagens a serem incluídas na requisição.
|
| 62 |
+
use_memory (bool): Se True, utiliza ou cria uma sessão de chat com memória (stateful).
|
| 63 |
+
Se False, faz uma chamada única (stateless).
|
| 64 |
+
restart_session (bool): Se True, força o descarte da sessão de memória atual
|
| 65 |
+
e o início de uma nova na próxima chamada com `use_memory=True`.
|
| 66 |
+
"""
|
| 67 |
+
self._check_model()
|
| 68 |
+
|
| 69 |
+
# --- Lógica do Gatilho de Reinicialização ---
|
| 70 |
+
if restart_session and self.chat_session is not None:
|
| 71 |
+
logger.info("Gatilho de reinicialização acionado. Descartando sessão de chat anterior.")
|
| 72 |
+
self.chat_session = None
|
| 73 |
+
# Se o objetivo era apenas reiniciar, podemos retornar sem fazer uma chamada.
|
| 74 |
+
if not prompt_text:
|
| 75 |
+
return ""
|
| 76 |
+
|
| 77 |
+
# Constrói o conteúdo a ser enviado
|
| 78 |
+
prompt_parts = []
|
| 79 |
+
if prompt_text:
|
| 80 |
+
prompt_parts.append(prompt_text)
|
| 81 |
+
if image_list:
|
| 82 |
+
prompt_parts.extend(image_list)
|
| 83 |
+
|
| 84 |
+
# Se não houver nada a ser enviado, retorna uma string vazia.
|
| 85 |
+
if not prompt_parts:
|
| 86 |
+
return ""
|
| 87 |
+
|
| 88 |
+
try:
|
| 89 |
+
# --- Lógica de Gerenciamento de Memória ---
|
| 90 |
+
if use_memory:
|
| 91 |
+
if self.chat_session is None:
|
| 92 |
+
logger.info("Iniciando nova sessão de chat com memória (stateful).")
|
| 93 |
+
self.chat_session = self.model.start_chat(history=[])
|
| 94 |
+
|
| 95 |
+
logger.info("Enviando mensagem para a sessão de chat com memória...")
|
| 96 |
+
response = self.chat_session.send_message(prompt_parts)
|
| 97 |
+
else:
|
| 98 |
+
# Modo stateless (sem memória)
|
| 99 |
+
logger.info("Enviando requisição sem memória (stateless)...")
|
| 100 |
+
response = self.model.generate_content(prompt_parts)
|
| 101 |
+
|
| 102 |
+
# Extrai e retorna o texto da resposta
|
| 103 |
+
response_text = response.text
|
| 104 |
+
logger.debug(f"Gemini respondeu com texto bruto: {response_text[:500]}...")
|
| 105 |
+
return response_text
|
| 106 |
+
|
| 107 |
+
except Exception as e:
|
| 108 |
+
logger.error(f"GEMINI_MANAGER: Falha na chamada da API: {e}", exc_info=True)
|
| 109 |
+
# Em caso de erro na comunicação, é seguro zerar a sessão para evitar um estado corrompido.
|
| 110 |
+
self.chat_session = None
|
| 111 |
+
raise RuntimeError(f"Erro na comunicação com a API do Gemini: {e}")
|
| 112 |
+
|
| 113 |
+
# --- Instância Singleton ---
|
| 114 |
+
# Esta instância única será importada por outros componentes, como o Neura_Link.
|
| 115 |
+
gemini_manager_singleton = GeminiManager()
|
aduc_framework/managers/latent_enhancer_manager.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# latent_enhancer_specialist.py
|
| 2 |
+
# AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 3 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Contato:
|
| 6 |
+
# Carlos Rodrigues dos Santos
|
| 7 | |
| 8 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 9 |
+
#
|
| 10 |
+
# Repositórios e Projetos Relacionados:
|
| 11 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 12 |
+
#
|
| 13 |
+
#
|
| 14 |
+
# PENDING PATENT NOTICE: Please see NOTICE.md.
|
| 15 |
+
#
|
| 16 |
+
# Version 1.0.1
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
import logging
|
| 20 |
+
import time
|
| 21 |
+
from diffusers import LTXLatentUpsamplePipeline
|
| 22 |
+
from ..managers.ltx_manager import ltx_manager_singleton
|
| 23 |
+
|
| 24 |
+
logger = logging.getLogger(__name__)
|
| 25 |
+
|
| 26 |
+
class LatentEnhancerSpecialist:
|
| 27 |
+
"""
|
| 28 |
+
Especialista responsável por melhorar a qualidade de tensores latentes,
|
| 29 |
+
incluindo upscaling espacial e refinamento por denoise.
|
| 30 |
+
"""
|
| 31 |
+
def __init__(self):
|
| 32 |
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 33 |
+
self.pipe_upsample = None
|
| 34 |
+
self.base_vae = None # VAE para o upscaler
|
| 35 |
+
|
| 36 |
+
def _lazy_init_upscaler(self):
|
| 37 |
+
"""Inicializa a pipeline de upscaling apenas quando for usada."""
|
| 38 |
+
if self.pipe_upsample is not None:
|
| 39 |
+
return
|
| 40 |
+
try:
|
| 41 |
+
from diffusers.models.autoencoders import AutoencoderKLLTXVideo
|
| 42 |
+
self.base_vae = AutoencoderKLLTXVideo.from_pretrained(
|
| 43 |
+
"linoyts/LTX-Video-spatial-upscaler-0.9.8",
|
| 44 |
+
subfolder="vae",
|
| 45 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
| 46 |
+
).to(self.device)
|
| 47 |
+
|
| 48 |
+
self.pipe_upsample = LTXLatentUpsamplePipeline.from_pretrained(
|
| 49 |
+
"linoyts/LTX-Video-spatial-upscaler-0.9.8",
|
| 50 |
+
vae=self.base_vae,
|
| 51 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
| 52 |
+
).to(self.device)
|
| 53 |
+
logger.info("[Enhancer] Pipeline de Upscale carregada com sucesso.")
|
| 54 |
+
except Exception as e:
|
| 55 |
+
logger.error(f"[Enhancer] Falha ao carregar pipeline de Upscale: {e}")
|
| 56 |
+
self.pipe_upsample = None
|
| 57 |
+
|
| 58 |
+
@torch.no_grad()
|
| 59 |
+
def upscale(self, latents: torch.Tensor) -> torch.Tensor:
|
| 60 |
+
"""Aplica o upscaling 2x nos tensores latentes fornecidos."""
|
| 61 |
+
self._lazy_init_upscaler()
|
| 62 |
+
if self.pipe_upsample is None:
|
| 63 |
+
logger.warning("[Enhancer] Pipeline de Upscale indisponível. Retornando latentes originais.")
|
| 64 |
+
return latents
|
| 65 |
+
try:
|
| 66 |
+
logger.info(f"[Enhancer] Recebido shape {latents.shape} para Upscale.")
|
| 67 |
+
result = self.pipe_upsample(latents=latents, output_type="latent")
|
| 68 |
+
output_tensor = result.frames
|
| 69 |
+
logger.info(f"[Enhancer] Upscale concluído. Novo shape: {output_tensor.shape}")
|
| 70 |
+
return output_tensor
|
| 71 |
+
except Exception as e:
|
| 72 |
+
logger.error(f"[Enhancer] Erro durante upscale: {e}", exc_info=True)
|
| 73 |
+
return latents
|
| 74 |
+
|
| 75 |
+
@torch.no_grad()
|
| 76 |
+
def refine(self, latents: torch.Tensor, fps: int = 24, **kwargs) -> torch.Tensor:
|
| 77 |
+
"""
|
| 78 |
+
Invoca o LTX Pool Manager para refinar um tensor latente existente.
|
| 79 |
+
"""
|
| 80 |
+
logger.info(f"[Enhancer] Refinando tensor latente com shape {latents.shape}.")
|
| 81 |
+
|
| 82 |
+
main_pipeline_vae = ltx_manager_singleton.workers[0].pipeline.vae
|
| 83 |
+
video_scale_factor = getattr(main_pipeline_vae.config, 'temporal_scale_factor', 8)
|
| 84 |
+
|
| 85 |
+
_, _, num_latent_frames, _, _ = latents.shape
|
| 86 |
+
|
| 87 |
+
# --- [CORREÇÃO FINAL E CRÍTICA] ---
|
| 88 |
+
# A pipeline de refinamento (vid2vid) espera o número de frames de pixels que CORRESPONDE
|
| 89 |
+
# ao latente existente, SEM a lógica do +1 que ela aplicará internamente.
|
| 90 |
+
pixel_frames = (num_latent_frames - 1) * video_scale_factor
|
| 91 |
+
|
| 92 |
+
final_ltx_params = {
|
| 93 |
+
"video_total_frames": pixel_frames,
|
| 94 |
+
"video_fps": fps,
|
| 95 |
+
"current_fragment_index": int(time.time()),
|
| 96 |
+
**kwargs
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
refined_latents_tensor, _ = ltx_manager_singleton.refine_latents(latents, **final_ltx_params)
|
| 100 |
+
|
| 101 |
+
if refined_latents_tensor is None:
|
| 102 |
+
logger.warning("[Enhancer] O refinamento falhou. Retornando tensor original não refinado.")
|
| 103 |
+
return latents
|
| 104 |
+
|
| 105 |
+
logger.info(f"[Enhancer] Retornando tensor latente refinado com shape: {refined_latents_tensor.shape}")
|
| 106 |
+
return refined_latents_tensor
|
| 107 |
+
|
| 108 |
+
# --- Singleton Global ---
|
| 109 |
+
latent_enhancer_specialist_singleton = LatentEnhancerSpecialist()
|
aduc_framework/managers/llama_multimodal_manager.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/managers/llama_multimodal_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 1.3.0 (Comprehensive Logging & Stable Attention)
|
| 6 |
+
#
|
| 7 |
+
# Manager especialista em conversas multimodais. Esta versão adiciona
|
| 8 |
+
# logging detalhado em todos os pontos críticos para garantir que não haja
|
| 9 |
+
# falhas silenciosas e para fornecer visibilidade total do fluxo de dados.
|
| 10 |
+
# Opera de forma stateless, recebendo prompts completos a cada chamada.
|
| 11 |
+
|
| 12 |
+
import yaml
|
| 13 |
+
import os
|
| 14 |
+
import logging
|
| 15 |
+
import torch
|
| 16 |
+
from PIL import Image
|
| 17 |
+
from typing import List, Dict, Any, Optional
|
| 18 |
+
import json
|
| 19 |
+
|
| 20 |
+
from transformers import MllamaForConditionalGeneration, MllamaProcessor
|
| 21 |
+
from huggingface_hub import HfFolder
|
| 22 |
+
|
| 23 |
+
from ..tools.hardware_manager import hardware_manager
|
| 24 |
+
|
| 25 |
+
# Logger específico para este manager
|
| 26 |
+
logger = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
class LlamaMultiModalManager:
|
| 29 |
+
"""
|
| 30 |
+
Gerencia uma única instância do Llama 3.2 Vision, com logging robusto
|
| 31 |
+
para rastrear todo o ciclo de vida de uma requisição. Opera de forma stateless.
|
| 32 |
+
"""
|
| 33 |
+
def __init__(self, config: dict):
|
| 34 |
+
logger.debug("LLAMA_MANAGER: Iniciando __init__.")
|
| 35 |
+
self.hf_token = os.getenv("HF_TOKEN") or HfFolder.get_token()
|
| 36 |
+
if not self.hf_token:
|
| 37 |
+
raise ValueError("Token da Hugging Face não encontrado. Faça login via `huggingface-cli login` ou defina a variável de ambiente HF_TOKEN.")
|
| 38 |
+
|
| 39 |
+
self.model_id = config['model_id']
|
| 40 |
+
self.max_new_tokens = config.get('max_new_tokens', 8096)
|
| 41 |
+
self.temperature = config.get('temperature', 0.9)
|
| 42 |
+
self.top_p = config.get('top_p', 0.9)
|
| 43 |
+
self.max_image_size = (480, 480)
|
| 44 |
+
|
| 45 |
+
logger.info(f"LLAMA_MANAGER: Carregando processador do modelo: {self.model_id}...")
|
| 46 |
+
self.processor = MllamaProcessor.from_pretrained(self.model_id, token=self.hf_token)
|
| 47 |
+
logger.info("LLAMA_MANAGER: Processador carregado.")
|
| 48 |
+
|
| 49 |
+
logger.info(f"LLAMA_MANAGER: Carregando modelo base: {self.model_id}...")
|
| 50 |
+
# A implementação de atenção padrão é usada para máxima compatibilidade.
|
| 51 |
+
self.model = MllamaForConditionalGeneration.from_pretrained(
|
| 52 |
+
self.model_id,
|
| 53 |
+
torch_dtype=torch.bfloat16,
|
| 54 |
+
device_map="auto",
|
| 55 |
+
token=self.hf_token
|
| 56 |
+
)
|
| 57 |
+
logger.info("LLAMA_MANAGER: Modelo carregado e mapeado para o dispositivo.")
|
| 58 |
+
logger.debug("LLAMA_MANAGER: __init__ concluído.")
|
| 59 |
+
|
| 60 |
+
def _preprocess_image(self, image: Image.Image) -> Image.Image:
|
| 61 |
+
"""Garante que a imagem esteja no formato RGB e dentro do tamanho máximo."""
|
| 62 |
+
logger.debug(f"Pré-processando imagem. Tamanho original: {image.size}, Modo: {image.mode}")
|
| 63 |
+
img = image.convert("RGB")
|
| 64 |
+
if img.width > self.max_image_size[0] or img.height > self.max_image_size[1]:
|
| 65 |
+
original_size = img.size
|
| 66 |
+
img.thumbnail(self.max_image_size, Image.Resampling.LANCZOS)
|
| 67 |
+
logger.debug(f"Imagem redimensionada de {original_size} para {img.size}.")
|
| 68 |
+
return img
|
| 69 |
+
|
| 70 |
+
@torch.inference_mode()
|
| 71 |
+
def process_turn(self, prompt_text: str, image_list: Optional[List[Image.Image]] = None) -> str:
|
| 72 |
+
"""
|
| 73 |
+
Ponto de entrada para processar uma requisição. Lida com a orquestração
|
| 74 |
+
interna e o tratamento de exceções.
|
| 75 |
+
"""
|
| 76 |
+
logger.info(f"LLAMA_MANAGER: Recebido novo turno. Comprimento do prompt: {len(prompt_text)}, Imagens: {len(image_list) if image_list else 0}.")
|
| 77 |
+
image_list = image_list or []
|
| 78 |
+
|
| 79 |
+
try:
|
| 80 |
+
# Prepara a imagem (se houver)
|
| 81 |
+
processed_image = self._preprocess_image(image_list[0]) if image_list else None
|
| 82 |
+
|
| 83 |
+
# Gera a resposta
|
| 84 |
+
assistant_response_text = self._generate_response(prompt_text, processed_image)
|
| 85 |
+
|
| 86 |
+
logger.info("LLAMA_MANAGER: Turno processado com sucesso.")
|
| 87 |
+
return assistant_response_text
|
| 88 |
+
|
| 89 |
+
except Exception as e:
|
| 90 |
+
logger.error(f"LLAMA_MANAGER: ERRO BRUTO DURANTE O PROCESSAMENTO DO TURNO: {e}", exc_info=True)
|
| 91 |
+
raise e
|
| 92 |
+
|
| 93 |
+
def _generate_response(self, prompt_str: str, image: Optional[Image.Image] = None) -> str:
|
| 94 |
+
"""
|
| 95 |
+
Função de inferência interna: processa, gera e decodifica.
|
| 96 |
+
"""
|
| 97 |
+
# 1. Processamento da Entrada
|
| 98 |
+
logger.debug("---> LLAMA_MANAGER: Etapa de Processamento da Entrada Iniciada.")
|
| 99 |
+
logger.debug(f"Texto do prompt recebido (primeiros 500 chars):\n---\n{prompt_str[:500]}\n---")
|
| 100 |
+
|
| 101 |
+
inputs = self.processor(
|
| 102 |
+
text=prompt_str,
|
| 103 |
+
images=[image] if image else None,
|
| 104 |
+
return_tensors="pt"
|
| 105 |
+
).to(self.model.device)
|
| 106 |
+
logger.debug(f"Entrada processada e movida para o dispositivo: {self.model.device}. Shape dos input_ids: {inputs['input_ids'].shape}")
|
| 107 |
+
|
| 108 |
+
# 2. Geração do Modelo
|
| 109 |
+
logger.debug("---> LLAMA_MANAGER: Etapa de Geração do Modelo Iniciada.")
|
| 110 |
+
generate_ids = self.model.generate(
|
| 111 |
+
**inputs,
|
| 112 |
+
max_new_tokens=self.max_new_tokens,
|
| 113 |
+
do_sample=True,
|
| 114 |
+
temperature=self.temperature,
|
| 115 |
+
top_p=self.top_p,
|
| 116 |
+
)
|
| 117 |
+
logger.debug(f"Geração concluída. Shape dos IDs de saída: {generate_ids.shape}")
|
| 118 |
+
|
| 119 |
+
# 3. Decodificação da Saída
|
| 120 |
+
logger.debug("---> LLAMA_MANAGER: Etapa de Decodificação da Saída Iniciada.")
|
| 121 |
+
input_ids_len = inputs['input_ids'].shape[1]
|
| 122 |
+
output_ids = generate_ids[:, input_ids_len:]
|
| 123 |
+
|
| 124 |
+
response_text_raw = self.processor.batch_decode(
|
| 125 |
+
output_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
|
| 126 |
+
)[0]
|
| 127 |
+
|
| 128 |
+
logger.debug(f"<--- LLAMA_MANAGER: Resposta bruta decodificada (antes da limpeza):\n---\n{response_text_raw}\n---")
|
| 129 |
+
|
| 130 |
+
return response_text_raw.strip()
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
# --- Placeholder e Instanciação Singleton ---
|
| 134 |
+
class LlamaMultiModalPlaceholder:
|
| 135 |
+
def __init__(self, reason: str = "Motivo desconhecido"):
|
| 136 |
+
logger.error(f"LlamaMultiModalManager não inicializado. Razão: {reason}. Placeholder em uso.")
|
| 137 |
+
self.reason = reason
|
| 138 |
+
def process_turn(self, *args, **kwargs):
|
| 139 |
+
return json.dumps({"error": f"Especialista Llama MultiModal indisponível. Razão: {self.reason}"})
|
| 140 |
+
|
| 141 |
+
try:
|
| 142 |
+
with open("config.yaml", 'r') as f:
|
| 143 |
+
config = yaml.safe_load(f)
|
| 144 |
+
llama_config = config['specialists'].get('llama_multimodal')
|
| 145 |
+
if llama_config and llama_config.get('gpus_required', 0) > 0:
|
| 146 |
+
hardware_manager.allocate_gpus('LlamaMultiModal', llama_config['gpus_required'])
|
| 147 |
+
llama_multimodal_manager_singleton = LlamaMultiModalManager(config=llama_config)
|
| 148 |
+
logger.info("Especialista Llama MultiModal (Stateless) pronto.")
|
| 149 |
+
else:
|
| 150 |
+
llama_multimodal_manager_singleton = LlamaMultiModalPlaceholder("Não habilitado ou sem gpus_required na config.yaml")
|
| 151 |
+
except Exception as e:
|
| 152 |
+
logger.critical(f"Falha CRÍTICA ao inicializar o LlamaMultiModalManager: {e}", exc_info=True)
|
| 153 |
+
llama_multimodal_manager_singleton = LlamaMultiModalPlaceholder(reason=str(e))
|
aduc_framework/managers/ltx_manager.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/managers/ltx_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 2.5.0 (Suporte a múltiplos LoRAs e inicialização robusta)
|
| 6 |
+
#
|
| 7 |
+
# Este manager é responsável por controlar a pipeline LTX-Video. Ele gerencia
|
| 8 |
+
# um pool de workers para otimizar o uso de múltiplas GPUs, lida com a inicialização
|
| 9 |
+
# e o setup de dependências complexas (clonando o repo LTX de forma segura), e expõe
|
| 10 |
+
# uma interface de alto nível para a geração de vídeo, agora com suporte a uma lista
|
| 11 |
+
# de LoRAs para composição avançada de estilos.
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
import gc
|
| 15 |
+
import os
|
| 16 |
+
import sys
|
| 17 |
+
import yaml
|
| 18 |
+
import logging
|
| 19 |
+
import huggingface_hub
|
| 20 |
+
import time
|
| 21 |
+
import threading
|
| 22 |
+
import subprocess
|
| 23 |
+
import shutil
|
| 24 |
+
from pathlib import Path
|
| 25 |
+
from typing import Optional, List, Tuple, Union
|
| 26 |
+
|
| 27 |
+
# --- Imports Relativos Corrigidos ---
|
| 28 |
+
from ..types import LatentConditioningItem
|
| 29 |
+
from ..tools.optimization import optimize_ltx_worker, can_optimize_fp8
|
| 30 |
+
from ..tools.hardware_manager import hardware_manager
|
| 31 |
+
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
+
|
| 34 |
+
# --- Gerenciamento de Dependências e Placeholders ---
|
| 35 |
+
DEPS_DIR = Path("./deps")
|
| 36 |
+
LTX_VIDEO_REPO_DIR = DEPS_DIR / "LTX-Video"
|
| 37 |
+
LTX_VIDEO_REPO_URL = "https://github.com/Lightricks/LTX-Video.git"
|
| 38 |
+
|
| 39 |
+
# Placeholders para módulos importados tardiamente (lazy-loaded)
|
| 40 |
+
create_ltx_video_pipeline = None
|
| 41 |
+
calculate_padding = None
|
| 42 |
+
LTXVideoPipeline = None
|
| 43 |
+
ConditioningItem = None
|
| 44 |
+
LTXMultiScalePipeline = None
|
| 45 |
+
vae_encode = None
|
| 46 |
+
latent_to_pixel_coords = None
|
| 47 |
+
randn_tensor = None
|
| 48 |
+
|
| 49 |
+
class LtxPoolManager:
|
| 50 |
+
"""
|
| 51 |
+
Gerencia um pool de LtxWorkers e expõe a pipeline de aprimoramento de prompt.
|
| 52 |
+
"""
|
| 53 |
+
def __init__(self, device_ids: List[str], ltx_config_file_name: str):
|
| 54 |
+
logger.info(f"LTX POOL MANAGER: Criando workers para os dispositivos: {device_ids}")
|
| 55 |
+
self._ltx_modules_loaded = False
|
| 56 |
+
self._setup_dependencies()
|
| 57 |
+
self._lazy_load_ltx_modules()
|
| 58 |
+
|
| 59 |
+
self.ltx_config_file = LTX_VIDEO_REPO_DIR / "configs" / ltx_config_file_name
|
| 60 |
+
|
| 61 |
+
self.workers = [LtxWorker(dev_id, self.ltx_config_file) for dev_id in device_ids]
|
| 62 |
+
self.current_worker_index = 0
|
| 63 |
+
self.lock = threading.Lock()
|
| 64 |
+
|
| 65 |
+
self.prompt_enhancement_pipeline = self.workers[0].pipeline if self.workers else None
|
| 66 |
+
if self.prompt_enhancement_pipeline:
|
| 67 |
+
logger.info("LTX POOL MANAGER: Pipeline de aprimoramento de prompt exposta para outros especialistas.")
|
| 68 |
+
|
| 69 |
+
self._apply_ltx_pipeline_patches()
|
| 70 |
+
|
| 71 |
+
if all(w.device.type == 'cuda' for w in self.workers):
|
| 72 |
+
logger.info("LTX POOL MANAGER: MODO HOT START ATIVADO. Pré-aquecendo todas as GPUs...")
|
| 73 |
+
for worker in self.workers:
|
| 74 |
+
worker.to_gpu()
|
| 75 |
+
logger.info("LTX POOL MANAGER: Todas as GPUs estão prontas.")
|
| 76 |
+
else:
|
| 77 |
+
logger.info("LTX POOL MANAGER: Operando em modo CPU ou misto. Pré-aquecimento de GPU pulado.")
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _setup_dependencies(self):
|
| 81 |
+
"""
|
| 82 |
+
Verifica a integridade do repositório LTX-Video e o clona usando um token de
|
| 83 |
+
autenticação (se disponível), adicionando-o ao sys.path.
|
| 84 |
+
"""
|
| 85 |
+
check_file = LTX_VIDEO_REPO_DIR / "README.md"
|
| 86 |
+
|
| 87 |
+
if not check_file.exists():
|
| 88 |
+
logger.warning(f"Repositório LTX-Video parece estar incompleto ou ausente. Tentando um clone limpo...")
|
| 89 |
+
|
| 90 |
+
if LTX_VIDEO_REPO_DIR.exists():
|
| 91 |
+
try:
|
| 92 |
+
shutil.rmtree(LTX_VIDEO_REPO_DIR)
|
| 93 |
+
logger.info(f"Diretório corrompido '{LTX_VIDEO_REPO_DIR}' removido.")
|
| 94 |
+
except OSError as e:
|
| 95 |
+
logger.error(f"Não foi possível remover o diretório corrompido: {e}")
|
| 96 |
+
raise RuntimeError("Falha ao limpar dependência corrompida.")
|
| 97 |
+
|
| 98 |
+
# --- INÍCIO DA LÓGICA DE AUTENTICAÇÃO ---
|
| 99 |
+
# Procura pelo token no ambiente (configurado via 'Secrets' no HF Spaces)
|
| 100 |
+
auth_token = os.getenv("HF_TOKEN")
|
| 101 |
+
repo_url = LTX_VIDEO_REPO_URL # URL padrão sem autenticação
|
| 102 |
+
|
| 103 |
+
if auth_token:
|
| 104 |
+
logger.info("Token de autenticação (HF_TOKEN) encontrado. Usando URL autenticada para o clone.")
|
| 105 |
+
# Monta a URL no formato https://<token>@github.com/...
|
| 106 |
+
repo_url = LTX_VIDEO_REPO_URL.replace("https://", f"https://{auth_token}@")
|
| 107 |
+
else:
|
| 108 |
+
logger.warning("Nenhum token de autenticação (HF_TOKEN) encontrado. O clone pode falhar se o repositório exigir login para LFS.")
|
| 109 |
+
# --- FIM DA LÓGICA DE AUTENTICAÇÃO ---
|
| 110 |
+
|
| 111 |
+
logger.info(f"Clonando repositório LTX-Video...")
|
| 112 |
+
try:
|
| 113 |
+
DEPS_DIR.mkdir(exist_ok=True)
|
| 114 |
+
# Usa a `repo_url` que pode ou não conter o token
|
| 115 |
+
subprocess.run(
|
| 116 |
+
["git", "clone", "--depth", "1", repo_url, str(LTX_VIDEO_REPO_DIR), "--quiet"],
|
| 117 |
+
check=True, capture_output=True, text=True
|
| 118 |
+
)
|
| 119 |
+
logger.info("Repositório LTX-Video clonado com sucesso.")
|
| 120 |
+
except subprocess.CalledProcessError as e:
|
| 121 |
+
logger.error(f"Falha CRÍTICA ao clonar o repositório LTX-Video. Git stderr: {e.stderr}")
|
| 122 |
+
raise RuntimeError(f"Não foi possível clonar a dependência LTX-Video. Causa provável: {e.stderr}")
|
| 123 |
+
else:
|
| 124 |
+
logger.info("Repositório LTX-Video local encontrado e parece completo.")
|
| 125 |
+
|
| 126 |
+
resolved_path = str(LTX_VIDEO_REPO_DIR.resolve())
|
| 127 |
+
if resolved_path not in sys.path:
|
| 128 |
+
sys.path.insert(0, resolved_path)
|
| 129 |
+
logger.info(f"Adicionado '{resolved_path}' ao sys.path.")
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def _lazy_load_ltx_modules(self):
|
| 133 |
+
"""Importa dinamicamente os módulos do LTX-Video após garantir que o repositório existe."""
|
| 134 |
+
if self._ltx_modules_loaded:
|
| 135 |
+
return
|
| 136 |
+
|
| 137 |
+
global create_ltx_video_pipeline, calculate_padding, LTXVideoPipeline, ConditioningItem, LTXMultiScalePipeline
|
| 138 |
+
global vae_encode, latent_to_pixel_coords, randn_tensor
|
| 139 |
+
|
| 140 |
+
from .ltx_pipeline_utils import create_ltx_video_pipeline, calculate_padding
|
| 141 |
+
from ltx_video.pipelines.pipeline_ltx_video import LTXVideoPipeline, ConditioningItem, LTXMultiScalePipeline
|
| 142 |
+
from ltx_video.models.autoencoders.vae_encode import vae_encode, latent_to_pixel_coords
|
| 143 |
+
from diffusers.utils.torch_utils import randn_tensor
|
| 144 |
+
|
| 145 |
+
self._ltx_modules_loaded = True
|
| 146 |
+
logger.info("Módulos do LTX-Video foram carregados dinamicamente.")
|
| 147 |
+
|
| 148 |
+
def _apply_ltx_pipeline_patches(self):
|
| 149 |
+
"""Aplica patches em tempo de execução na pipeline LTX para compatibilidade com ADUC-SDR."""
|
| 150 |
+
logger.info("LTX POOL MANAGER: Aplicando patches ADUC-SDR na pipeline LTX...")
|
| 151 |
+
for worker in self.workers:
|
| 152 |
+
worker.pipeline.prepare_conditioning = _aduc_prepare_conditioning_patch.__get__(worker.pipeline, LTXVideoPipeline)
|
| 153 |
+
logger.info("LTX POOL MANAGER: Todas as instâncias da pipeline foram corrigidas com sucesso.")
|
| 154 |
+
|
| 155 |
+
def _get_next_worker(self) -> 'LtxWorker':
|
| 156 |
+
with self.lock:
|
| 157 |
+
worker = self.workers[self.current_worker_index]
|
| 158 |
+
self.current_worker_index = (self.current_worker_index + 1) % len(self.workers)
|
| 159 |
+
return worker
|
| 160 |
+
|
| 161 |
+
def _prepare_pipeline_params(self, worker: 'LtxWorker', **kwargs) -> dict:
|
| 162 |
+
pipeline_params = {
|
| 163 |
+
"height": kwargs['height'], "width": kwargs['width'], "num_frames": kwargs['video_total_frames'],
|
| 164 |
+
"frame_rate": kwargs.get('video_fps', 24),
|
| 165 |
+
"generator": torch.Generator(device=worker.device).manual_seed(int(time.time()) + kwargs.get('current_fragment_index', 0)),
|
| 166 |
+
"is_video": True, "vae_per_channel_normalize": True,
|
| 167 |
+
"prompt": kwargs.get('motion_prompt', ""), "negative_prompt": kwargs.get('negative_prompt', "blurry, distorted, static, bad quality"),
|
| 168 |
+
"guidance_scale": kwargs.get('guidance_scale', 1.0), "stg_scale": kwargs.get('stg_scale', 0.0),
|
| 169 |
+
"rescaling_scale": kwargs.get('rescaling_scale', 0.15), "num_inference_steps": kwargs.get('num_inference_steps', 20),
|
| 170 |
+
"output_type": "latent"
|
| 171 |
+
}
|
| 172 |
+
if 'latents' in kwargs:
|
| 173 |
+
pipeline_params["latents"] = kwargs['latents'].to(worker.device, dtype=worker.pipeline.transformer.dtype)
|
| 174 |
+
if 'strength' in kwargs:
|
| 175 |
+
pipeline_params["strength"] = kwargs['strength']
|
| 176 |
+
|
| 177 |
+
if 'conditioning_items_data' in kwargs:
|
| 178 |
+
final_conditioning_items = []
|
| 179 |
+
for item in kwargs['conditioning_items_data']:
|
| 180 |
+
item.latent_tensor = item.latent_tensor.to(worker.device)
|
| 181 |
+
final_conditioning_items.append(item)
|
| 182 |
+
pipeline_params["conditioning_items"] = final_conditioning_items
|
| 183 |
+
|
| 184 |
+
if worker.is_distilled:
|
| 185 |
+
fixed_timesteps = worker.config.get("first_pass", {}).get("timesteps")
|
| 186 |
+
if fixed_timesteps:
|
| 187 |
+
pipeline_params["timesteps"] = fixed_timesteps
|
| 188 |
+
pipeline_params["num_inference_steps"] = len(fixed_timesteps)
|
| 189 |
+
|
| 190 |
+
callback = kwargs.get('callback')
|
| 191 |
+
if callback:
|
| 192 |
+
pipeline_params["callback_on_step_end"] = callback
|
| 193 |
+
pipeline_params["callback_on_step_end_tensor_inputs"] = ["latents"]
|
| 194 |
+
|
| 195 |
+
return pipeline_params
|
| 196 |
+
|
| 197 |
+
def generate_latent_fragment(self, **kwargs) -> Tuple[torch.Tensor, tuple]:
|
| 198 |
+
worker_to_use = self._get_next_worker()
|
| 199 |
+
try:
|
| 200 |
+
height, width = kwargs['height'], kwargs['width']
|
| 201 |
+
padded_h, padded_w = ((height - 1) // 32 + 1) * 32, ((width - 1) // 32 + 1) * 32
|
| 202 |
+
padding_vals = calculate_padding(height, width, padded_h, padded_w)
|
| 203 |
+
kwargs['height'], kwargs['width'] = padded_h, padded_w
|
| 204 |
+
|
| 205 |
+
pipeline_params = self._prepare_pipeline_params(worker_to_use, **kwargs)
|
| 206 |
+
|
| 207 |
+
logger.info(f"Iniciando GERAÇÃO em {worker_to_use.device} com shape {padded_w}x{padded_h}")
|
| 208 |
+
|
| 209 |
+
if isinstance(worker_to_use.pipeline, LTXMultiScalePipeline):
|
| 210 |
+
result = worker_to_use.pipeline.video_pipeline(**pipeline_params).images
|
| 211 |
+
else:
|
| 212 |
+
result = worker_to_use.generate_video_fragment_internal(**pipeline_params)
|
| 213 |
+
return result, padding_vals
|
| 214 |
+
except Exception as e:
|
| 215 |
+
logger.error(f"LTX POOL MANAGER: Erro durante a geração em {worker_to_use.device}: {e}", exc_info=True)
|
| 216 |
+
raise e
|
| 217 |
+
finally:
|
| 218 |
+
if worker_to_use and worker_to_use.device.type == 'cuda':
|
| 219 |
+
with torch.cuda.device(worker_to_use.device):
|
| 220 |
+
gc.collect()
|
| 221 |
+
torch.cuda.empty_cache()
|
| 222 |
+
|
| 223 |
+
def refine_latents(self, latents_to_refine: torch.Tensor, **kwargs) -> Tuple[torch.Tensor, tuple]:
|
| 224 |
+
pass
|
| 225 |
+
|
| 226 |
+
class LtxWorker:
|
| 227 |
+
"""Representa uma única instância da pipeline LTX-Video em um dispositivo específico."""
|
| 228 |
+
def __init__(self, device_id, ltx_config_file):
|
| 229 |
+
self.cpu_device = torch.device('cpu')
|
| 230 |
+
self.device = torch.device(device_id if torch.cuda.is_available() else 'cpu')
|
| 231 |
+
logger.info(f"LTX Worker ({self.device}): Inicializando com config '{ltx_config_file}'...")
|
| 232 |
+
|
| 233 |
+
with open(ltx_config_file, "r") as file:
|
| 234 |
+
self.config = yaml.safe_load(file)
|
| 235 |
+
|
| 236 |
+
with open("config.yaml", 'r') as f:
|
| 237 |
+
global_config = yaml.safe_load(f)
|
| 238 |
+
lora_config = global_config.get('specialists', {}).get('ltx', {}).get('lora', {})
|
| 239 |
+
|
| 240 |
+
self.is_distilled = "distilled" in self.config.get("checkpoint_path", "")
|
| 241 |
+
models_dir = LTX_VIDEO_REPO_DIR / "models_downloaded"
|
| 242 |
+
|
| 243 |
+
logger.info(f"LTX Worker ({self.device}): Preparando para carregar modelo...")
|
| 244 |
+
model_filename = self.config["checkpoint_path"]
|
| 245 |
+
model_path = huggingface_hub.hf_hub_download(
|
| 246 |
+
repo_id="Lightricks/LTX-Video", filename=model_filename,
|
| 247 |
+
local_dir=str(models_dir), local_dir_use_symlinks=False
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
self.pipeline = create_ltx_video_pipeline(
|
| 251 |
+
ckpt_path=model_path,
|
| 252 |
+
precision=self.config["precision"],
|
| 253 |
+
text_encoder_model_name_or_path=self.config["text_encoder_model_name_or_path"],
|
| 254 |
+
sampler=self.config["sampler"],
|
| 255 |
+
device='cpu'
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
if lora_config and isinstance(lora_config, list):
|
| 259 |
+
logger.info(f"LTX Worker ({self.device}): Encontrados {len(lora_config)} LoRAs para carregar.")
|
| 260 |
+
|
| 261 |
+
for lora_item in lora_config:
|
| 262 |
+
lora_model_id = lora_item.get("model_id")
|
| 263 |
+
lora_weight = lora_item.get("weight", 0.7)
|
| 264 |
+
|
| 265 |
+
if not lora_model_id:
|
| 266 |
+
logger.warning(f"LTX Worker ({self.device}): Item de LoRA na lista sem 'model_id'. Pulando.")
|
| 267 |
+
continue
|
| 268 |
+
|
| 269 |
+
logger.info(f"LTX Worker ({self.device}): Carregando e aplicando LoRA '{lora_model_id}' com peso {lora_weight}...")
|
| 270 |
+
try:
|
| 271 |
+
self.pipeline.load_lora_weights(lora_model_id)
|
| 272 |
+
self.pipeline.fuse_lora(lora_scale=lora_weight)
|
| 273 |
+
logger.info(f"LTX Worker ({self.device}): LoRA '{lora_model_id}' fundido ao modelo com sucesso.")
|
| 274 |
+
except Exception as e:
|
| 275 |
+
logger.error(f"LTX Worker ({self.device}): Falha ao carregar ou fundir o LoRA '{lora_model_id}'. Erro: {e}", exc_info=True)
|
| 276 |
+
|
| 277 |
+
logger.info(f"LTX Worker ({self.device}): Modelo pronto na CPU. É um modelo distilled? {self.is_distilled}")
|
| 278 |
+
|
| 279 |
+
def to_gpu(self):
|
| 280 |
+
if self.device.type == 'cpu': return
|
| 281 |
+
logger.info(f"LTX Worker: Movendo pipeline para a GPU {self.device}...")
|
| 282 |
+
self.pipeline.to(self.device)
|
| 283 |
+
if self.device.type == 'cuda' and can_optimize_fp8():
|
| 284 |
+
logger.info(f"LTX Worker ({self.device}): GPU com suporte a FP8 detectada. Otimizando...")
|
| 285 |
+
optimize_ltx_worker(self)
|
| 286 |
+
logger.info(f"LTX Worker ({self.device}): Otimização completa.")
|
| 287 |
+
|
| 288 |
+
def to_cpu(self):
|
| 289 |
+
if self.device.type == 'cpu': return
|
| 290 |
+
logger.info(f"LTX Worker: Descarregando pipeline da GPU {self.device}...")
|
| 291 |
+
self.pipeline.to('cpu')
|
| 292 |
+
gc.collect()
|
| 293 |
+
if torch.cuda.is_available(): torch.cuda.empty_cache()
|
| 294 |
+
|
| 295 |
+
def generate_video_fragment_internal(self, **kwargs):
|
| 296 |
+
return self.pipeline(**kwargs).images
|
| 297 |
+
|
| 298 |
+
def _aduc_prepare_conditioning_patch(
|
| 299 |
+
self: "LTXVideoPipeline",
|
| 300 |
+
conditioning_items: Optional[List[Union["ConditioningItem", "LatentConditioningItem"]]],
|
| 301 |
+
init_latents: torch.Tensor,
|
| 302 |
+
num_frames: int,
|
| 303 |
+
height: int,
|
| 304 |
+
width: int,
|
| 305 |
+
vae_per_channel_normalize: bool = False,
|
| 306 |
+
generator=None,
|
| 307 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, int]:
|
| 308 |
+
if not conditioning_items:
|
| 309 |
+
init_latents, init_latent_coords = self.patchifier.patchify(latents=init_latents)
|
| 310 |
+
init_pixel_coords = latent_to_pixel_coords(init_latent_coords, self.vae, causal_fix=self.transformer.config.causal_temporal_positioning)
|
| 311 |
+
return init_latents, init_pixel_coords, None, 0
|
| 312 |
+
|
| 313 |
+
init_conditioning_mask = torch.zeros_like(init_latents[:, 0, ...], dtype=torch.float32, device=init_latents.device)
|
| 314 |
+
extra_conditioning_latents, extra_conditioning_pixel_coords, extra_conditioning_mask = [], [], []
|
| 315 |
+
extra_conditioning_num_latents = 0
|
| 316 |
+
|
| 317 |
+
for item in conditioning_items:
|
| 318 |
+
if not isinstance(item, LatentConditioningItem):
|
| 319 |
+
logger.warning("Patch ADUC: Item de condicionamento não é um LatentConditioningItem e será ignorado.")
|
| 320 |
+
continue
|
| 321 |
+
|
| 322 |
+
media_item_latents = item.latent_tensor.to(dtype=init_latents.dtype, device=init_latents.device)
|
| 323 |
+
media_frame_number, strength = item.media_frame_number, item.conditioning_strength
|
| 324 |
+
|
| 325 |
+
if media_frame_number == 0:
|
| 326 |
+
f_l, h_l, w_l = media_item_latents.shape[-3:]
|
| 327 |
+
init_latents[..., :f_l, :h_l, :w_l] = torch.lerp(init_latents[..., :f_l, :h_l, :w_l], media_item_latents, strength)
|
| 328 |
+
init_conditioning_mask[..., :f_l, :h_l, :w_l] = strength
|
| 329 |
+
else:
|
| 330 |
+
noise = randn_tensor(media_item_latents.shape, generator=generator, device=media_item_latents.device, dtype=media_item_latents.dtype)
|
| 331 |
+
media_item_latents = torch.lerp(noise, media_item_latents, strength)
|
| 332 |
+
patched_latents, latent_coords = self.patchifier.patchify(latents=media_item_latents)
|
| 333 |
+
pixel_coords = latent_to_pixel_coords(latent_coords, self.vae, causal_fix=self.transformer.config.causal_temporal_positioning)
|
| 334 |
+
pixel_coords[:, 0] += media_frame_number
|
| 335 |
+
extra_conditioning_num_latents += patched_latents.shape[1]
|
| 336 |
+
new_mask = torch.full(patched_latents.shape[:2], strength, dtype=torch.float32, device=init_latents.device)
|
| 337 |
+
extra_conditioning_latents.append(patched_latents)
|
| 338 |
+
extra_conditioning_pixel_coords.append(pixel_coords)
|
| 339 |
+
extra_conditioning_mask.append(new_mask)
|
| 340 |
+
|
| 341 |
+
init_latents, init_latent_coords = self.patchifier.patchify(latents=init_latents)
|
| 342 |
+
init_pixel_coords = latent_to_pixel_coords(init_latent_coords, self.vae, causal_fix=self.transformer.config.causal_temporal_positioning)
|
| 343 |
+
init_conditioning_mask, _ = self.patchifier.patchify(latents=init_conditioning_mask.unsqueeze(1))
|
| 344 |
+
init_conditioning_mask = init_conditioning_mask.squeeze(-1)
|
| 345 |
+
|
| 346 |
+
if extra_conditioning_latents:
|
| 347 |
+
init_latents = torch.cat([*extra_conditioning_latents, init_latents], dim=1)
|
| 348 |
+
init_pixel_coords = torch.cat([*extra_conditioning_pixel_coords, init_pixel_coords], dim=2)
|
| 349 |
+
init_conditioning_mask = torch.cat([*extra_conditioning_mask, init_conditioning_mask], dim=1)
|
| 350 |
+
|
| 351 |
+
return init_latents, init_pixel_coords, init_conditioning_mask, extra_conditioning_num_latents
|
| 352 |
+
|
| 353 |
+
# --- Instanciação Singleton ---
|
| 354 |
+
with open("config.yaml", 'r') as f:
|
| 355 |
+
config = yaml.safe_load(f)
|
| 356 |
+
ltx_gpus_required = config['specialists']['ltx']['gpus_required']
|
| 357 |
+
ltx_device_ids = hardware_manager.allocate_gpus('LTX', ltx_gpus_required)
|
| 358 |
+
ltx_config_filename = config['specialists']['ltx']['config_file']
|
| 359 |
+
ltx_manager_singleton = LtxPoolManager(device_ids=ltx_device_ids, ltx_config_file_name=ltx_config_filename)
|
| 360 |
+
logger.info("Especialista de Vídeo (LTX) pronto.")
|
aduc_framework/managers/ltx_pipeline_utils.py
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import os
|
| 3 |
+
import random
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from diffusers.utils import logging
|
| 7 |
+
from typing import Optional, List, Union
|
| 8 |
+
import yaml
|
| 9 |
+
|
| 10 |
+
import imageio
|
| 11 |
+
import json
|
| 12 |
+
import numpy as np
|
| 13 |
+
import torch
|
| 14 |
+
import cv2
|
| 15 |
+
from safetensors import safe_open
|
| 16 |
+
from PIL import Image
|
| 17 |
+
from transformers import (
|
| 18 |
+
T5EncoderModel,
|
| 19 |
+
T5Tokenizer,
|
| 20 |
+
AutoModelForCausalLM,
|
| 21 |
+
AutoProcessor,
|
| 22 |
+
AutoTokenizer,
|
| 23 |
+
)
|
| 24 |
+
from huggingface_hub import hf_hub_download
|
| 25 |
+
|
| 26 |
+
from ltx_video.models.autoencoders.causal_video_autoencoder import (
|
| 27 |
+
CausalVideoAutoencoder,
|
| 28 |
+
)
|
| 29 |
+
from ltx_video.models.transformers.symmetric_patchifier import SymmetricPatchifier
|
| 30 |
+
from ltx_video.models.transformers.transformer3d import Transformer3DModel
|
| 31 |
+
from ltx_video.pipelines.pipeline_ltx_video import (
|
| 32 |
+
ConditioningItem,
|
| 33 |
+
LTXVideoPipeline,
|
| 34 |
+
LTXMultiScalePipeline,
|
| 35 |
+
)
|
| 36 |
+
from ltx_video.schedulers.rf import RectifiedFlowScheduler
|
| 37 |
+
from ltx_video.utils.skip_layer_strategy import SkipLayerStrategy
|
| 38 |
+
from ltx_video.models.autoencoders.latent_upsampler import LatentUpsampler
|
| 39 |
+
import ltx_video.pipelines.crf_compressor as crf_compressor
|
| 40 |
+
|
| 41 |
+
MAX_HEIGHT = 720
|
| 42 |
+
MAX_WIDTH = 1280
|
| 43 |
+
MAX_NUM_FRAMES = 257
|
| 44 |
+
|
| 45 |
+
logger = logging.get_logger("LTX-Video")
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def get_total_gpu_memory():
|
| 49 |
+
if torch.cuda.is_available():
|
| 50 |
+
total_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
|
| 51 |
+
return total_memory
|
| 52 |
+
return 44
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def get_device():
|
| 56 |
+
if torch.cuda.is_available():
|
| 57 |
+
return "cuda"
|
| 58 |
+
elif torch.backends.mps.is_available():
|
| 59 |
+
return "mps"
|
| 60 |
+
return "cuda"
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def load_image_to_tensor_with_resize_and_crop(
|
| 64 |
+
image_input: Union[str, Image.Image],
|
| 65 |
+
target_height: int = 512,
|
| 66 |
+
target_width: int = 768,
|
| 67 |
+
just_crop: bool = False,
|
| 68 |
+
) -> torch.Tensor:
|
| 69 |
+
"""Load and process an image into a tensor.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
image_input: Either a file path (str) or a PIL Image object
|
| 73 |
+
target_height: Desired height of output tensor
|
| 74 |
+
target_width: Desired width of output tensor
|
| 75 |
+
just_crop: If True, only crop the image to the target size without resizing
|
| 76 |
+
"""
|
| 77 |
+
if isinstance(image_input, str):
|
| 78 |
+
image = Image.open(image_input).convert("RGB")
|
| 79 |
+
elif isinstance(image_input, Image.Image):
|
| 80 |
+
image = image_input
|
| 81 |
+
else:
|
| 82 |
+
raise ValueError("image_input must be either a file path or a PIL Image object")
|
| 83 |
+
|
| 84 |
+
input_width, input_height = image.size
|
| 85 |
+
aspect_ratio_target = target_width / target_height
|
| 86 |
+
aspect_ratio_frame = input_width / input_height
|
| 87 |
+
if aspect_ratio_frame > aspect_ratio_target:
|
| 88 |
+
new_width = int(input_height * aspect_ratio_target)
|
| 89 |
+
new_height = input_height
|
| 90 |
+
x_start = (input_width - new_width) // 2
|
| 91 |
+
y_start = 0
|
| 92 |
+
else:
|
| 93 |
+
new_width = input_width
|
| 94 |
+
new_height = int(input_width / aspect_ratio_target)
|
| 95 |
+
x_start = 0
|
| 96 |
+
y_start = (input_height - new_height) // 2
|
| 97 |
+
|
| 98 |
+
image = image.crop((x_start, y_start, x_start + new_width, y_start + new_height))
|
| 99 |
+
if not just_crop:
|
| 100 |
+
image = image.resize((target_width, target_height))
|
| 101 |
+
|
| 102 |
+
image = np.array(image)
|
| 103 |
+
image = cv2.GaussianBlur(image, (3, 3), 0)
|
| 104 |
+
frame_tensor = torch.from_numpy(image).float()
|
| 105 |
+
frame_tensor = crf_compressor.compress(frame_tensor / 255.0) * 255.0
|
| 106 |
+
frame_tensor = frame_tensor.permute(2, 0, 1)
|
| 107 |
+
frame_tensor = (frame_tensor / 127.5) - 1.0
|
| 108 |
+
# Create 5D tensor: (batch_size=1, channels=3, num_frames=1, height, width)
|
| 109 |
+
return frame_tensor.unsqueeze(0).unsqueeze(2)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def calculate_padding(
|
| 113 |
+
source_height: int, source_width: int, target_height: int, target_width: int
|
| 114 |
+
) -> tuple[int, int, int, int]:
|
| 115 |
+
|
| 116 |
+
# Calculate total padding needed
|
| 117 |
+
pad_height = target_height - source_height
|
| 118 |
+
pad_width = target_width - source_width
|
| 119 |
+
|
| 120 |
+
# Calculate padding for each side
|
| 121 |
+
pad_top = pad_height // 2
|
| 122 |
+
pad_bottom = pad_height - pad_top # Handles odd padding
|
| 123 |
+
pad_left = pad_width // 2
|
| 124 |
+
pad_right = pad_width - pad_left # Handles odd padding
|
| 125 |
+
|
| 126 |
+
# Return padded tensor
|
| 127 |
+
# Padding format is (left, right, top, bottom)
|
| 128 |
+
padding = (pad_left, pad_right, pad_top, pad_bottom)
|
| 129 |
+
return padding
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def convert_prompt_to_filename(text: str, max_len: int = 20) -> str:
|
| 133 |
+
# Remove non-letters and convert to lowercase
|
| 134 |
+
clean_text = "".join(
|
| 135 |
+
char.lower() for char in text if char.isalpha() or char.isspace()
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# Split into words
|
| 139 |
+
words = clean_text.split()
|
| 140 |
+
|
| 141 |
+
# Build result string keeping track of length
|
| 142 |
+
result = []
|
| 143 |
+
current_length = 0
|
| 144 |
+
|
| 145 |
+
for word in words:
|
| 146 |
+
# Add word length plus 1 for underscore (except for first word)
|
| 147 |
+
new_length = current_length + len(word)
|
| 148 |
+
|
| 149 |
+
if new_length <= max_len:
|
| 150 |
+
result.append(word)
|
| 151 |
+
current_length += len(word)
|
| 152 |
+
else:
|
| 153 |
+
break
|
| 154 |
+
|
| 155 |
+
return "-".join(result)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# Generate output video name
|
| 159 |
+
def get_unique_filename(
|
| 160 |
+
base: str,
|
| 161 |
+
ext: str,
|
| 162 |
+
prompt: str,
|
| 163 |
+
seed: int,
|
| 164 |
+
resolution: tuple[int, int, int],
|
| 165 |
+
dir: Path,
|
| 166 |
+
endswith=None,
|
| 167 |
+
index_range=1000,
|
| 168 |
+
) -> Path:
|
| 169 |
+
base_filename = f"{base}_{convert_prompt_to_filename(prompt, max_len=30)}_{seed}_{resolution[0]}x{resolution[1]}x{resolution[2]}"
|
| 170 |
+
for i in range(index_range):
|
| 171 |
+
filename = dir / f"{base_filename}_{i}{endswith if endswith else ''}{ext}"
|
| 172 |
+
if not os.path.exists(filename):
|
| 173 |
+
return filename
|
| 174 |
+
raise FileExistsError(
|
| 175 |
+
f"Could not find a unique filename after {index_range} attempts."
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def seed_everething(seed: int):
|
| 180 |
+
random.seed(seed)
|
| 181 |
+
np.random.seed(seed)
|
| 182 |
+
torch.manual_seed(seed)
|
| 183 |
+
if torch.cuda.is_available():
|
| 184 |
+
torch.cuda.manual_seed(seed)
|
| 185 |
+
if torch.backends.mps.is_available():
|
| 186 |
+
torch.mps.manual_seed(seed)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def main():
|
| 190 |
+
parser = argparse.ArgumentParser(
|
| 191 |
+
description="Load models from separate directories and run the pipeline."
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
# Directories
|
| 195 |
+
parser.add_argument(
|
| 196 |
+
"--output_path",
|
| 197 |
+
type=str,
|
| 198 |
+
default=None,
|
| 199 |
+
help="Path to the folder to save output video, if None will save in outputs/ directory.",
|
| 200 |
+
)
|
| 201 |
+
parser.add_argument("--seed", type=int, default="171198")
|
| 202 |
+
|
| 203 |
+
# Pipeline parameters
|
| 204 |
+
parser.add_argument(
|
| 205 |
+
"--num_images_per_prompt",
|
| 206 |
+
type=int,
|
| 207 |
+
default=1,
|
| 208 |
+
help="Number of images per prompt",
|
| 209 |
+
)
|
| 210 |
+
parser.add_argument(
|
| 211 |
+
"--image_cond_noise_scale",
|
| 212 |
+
type=float,
|
| 213 |
+
default=0.15,
|
| 214 |
+
help="Amount of noise to add to the conditioned image",
|
| 215 |
+
)
|
| 216 |
+
parser.add_argument(
|
| 217 |
+
"--height",
|
| 218 |
+
type=int,
|
| 219 |
+
default=704,
|
| 220 |
+
help="Height of the output video frames. Optional if an input image provided.",
|
| 221 |
+
)
|
| 222 |
+
parser.add_argument(
|
| 223 |
+
"--width",
|
| 224 |
+
type=int,
|
| 225 |
+
default=1216,
|
| 226 |
+
help="Width of the output video frames. If None will infer from input image.",
|
| 227 |
+
)
|
| 228 |
+
parser.add_argument(
|
| 229 |
+
"--num_frames",
|
| 230 |
+
type=int,
|
| 231 |
+
default=121,
|
| 232 |
+
help="Number of frames to generate in the output video",
|
| 233 |
+
)
|
| 234 |
+
parser.add_argument(
|
| 235 |
+
"--frame_rate", type=int, default=30, help="Frame rate for the output video"
|
| 236 |
+
)
|
| 237 |
+
parser.add_argument(
|
| 238 |
+
"--device",
|
| 239 |
+
default=None,
|
| 240 |
+
help="Device to run inference on. If not specified, will automatically detect and use CUDA or MPS if available, else CPU.",
|
| 241 |
+
)
|
| 242 |
+
parser.add_argument(
|
| 243 |
+
"--pipeline_config",
|
| 244 |
+
type=str,
|
| 245 |
+
default="configs/ltxv-13b-0.9.7-dev.yaml",
|
| 246 |
+
help="The path to the config file for the pipeline, which contains the parameters for the pipeline",
|
| 247 |
+
)
|
| 248 |
+
|
| 249 |
+
# Prompts
|
| 250 |
+
parser.add_argument(
|
| 251 |
+
"--prompt",
|
| 252 |
+
type=str,
|
| 253 |
+
help="Text prompt to guide generation",
|
| 254 |
+
)
|
| 255 |
+
parser.add_argument(
|
| 256 |
+
"--negative_prompt",
|
| 257 |
+
type=str,
|
| 258 |
+
default="worst quality, inconsistent motion, blurry, jittery, distorted",
|
| 259 |
+
help="Negative prompt for undesired features",
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
parser.add_argument(
|
| 263 |
+
"--offload_to_cpu",
|
| 264 |
+
action="store_true",
|
| 265 |
+
help="Offloading unnecessary computations to CPU.",
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
# video-to-video arguments:
|
| 269 |
+
parser.add_argument(
|
| 270 |
+
"--input_media_path",
|
| 271 |
+
type=str,
|
| 272 |
+
default=None,
|
| 273 |
+
help="Path to the input video (or imaage) to be modified using the video-to-video pipeline",
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
# Conditioning arguments
|
| 277 |
+
parser.add_argument(
|
| 278 |
+
"--conditioning_media_paths",
|
| 279 |
+
type=str,
|
| 280 |
+
nargs="*",
|
| 281 |
+
help="List of paths to conditioning media (images or videos). Each path will be used as a conditioning item.",
|
| 282 |
+
)
|
| 283 |
+
parser.add_argument(
|
| 284 |
+
"--conditioning_strengths",
|
| 285 |
+
type=float,
|
| 286 |
+
nargs="*",
|
| 287 |
+
help="List of conditioning strengths (between 0 and 1) for each conditioning item. Must match the number of conditioning items.",
|
| 288 |
+
)
|
| 289 |
+
parser.add_argument(
|
| 290 |
+
"--conditioning_start_frames",
|
| 291 |
+
type=int,
|
| 292 |
+
nargs="*",
|
| 293 |
+
help="List of frame indices where each conditioning item should be applied. Must match the number of conditioning items.",
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
args = parser.parse_args()
|
| 297 |
+
logger.warning(f"Running generation with arguments: {args}")
|
| 298 |
+
infer(**vars(args))
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def create_ltx_video_pipeline(
|
| 302 |
+
ckpt_path: str,
|
| 303 |
+
precision: str,
|
| 304 |
+
text_encoder_model_name_or_path: str,
|
| 305 |
+
sampler: Optional[str] = None,
|
| 306 |
+
device: Optional[str] = None,
|
| 307 |
+
enhance_prompt: bool = False,
|
| 308 |
+
prompt_enhancer_image_caption_model_name_or_path: Optional[str] = None,
|
| 309 |
+
prompt_enhancer_llm_model_name_or_path: Optional[str] = None,
|
| 310 |
+
) -> LTXVideoPipeline:
|
| 311 |
+
ckpt_path = Path(ckpt_path)
|
| 312 |
+
assert os.path.exists(
|
| 313 |
+
ckpt_path
|
| 314 |
+
), f"Ckpt path provided (--ckpt_path) {ckpt_path} does not exist"
|
| 315 |
+
|
| 316 |
+
with safe_open(ckpt_path, framework="pt") as f:
|
| 317 |
+
metadata = f.metadata()
|
| 318 |
+
config_str = metadata.get("config")
|
| 319 |
+
configs = json.loads(config_str)
|
| 320 |
+
allowed_inference_steps = configs.get("allowed_inference_steps", None)
|
| 321 |
+
|
| 322 |
+
vae = CausalVideoAutoencoder.from_pretrained(ckpt_path)
|
| 323 |
+
transformer = Transformer3DModel.from_pretrained(ckpt_path)
|
| 324 |
+
|
| 325 |
+
# Use constructor if sampler is specified, otherwise use from_pretrained
|
| 326 |
+
if sampler == "from_checkpoint" or not sampler:
|
| 327 |
+
scheduler = RectifiedFlowScheduler.from_pretrained(ckpt_path)
|
| 328 |
+
else:
|
| 329 |
+
scheduler = RectifiedFlowScheduler(
|
| 330 |
+
sampler=("Uniform" if sampler.lower() == "uniform" else "LinearQuadratic")
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
text_encoder = T5EncoderModel.from_pretrained(
|
| 334 |
+
text_encoder_model_name_or_path, subfolder="text_encoder"
|
| 335 |
+
)
|
| 336 |
+
patchifier = SymmetricPatchifier(patch_size=1)
|
| 337 |
+
tokenizer = T5Tokenizer.from_pretrained(
|
| 338 |
+
text_encoder_model_name_or_path, subfolder="tokenizer"
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
transformer = transformer.to(device)
|
| 342 |
+
vae = vae.to(device)
|
| 343 |
+
text_encoder = text_encoder.to(device)
|
| 344 |
+
|
| 345 |
+
if enhance_prompt:
|
| 346 |
+
prompt_enhancer_image_caption_model = AutoModelForCausalLM.from_pretrained(
|
| 347 |
+
prompt_enhancer_image_caption_model_name_or_path, trust_remote_code=True
|
| 348 |
+
)
|
| 349 |
+
prompt_enhancer_image_caption_processor = AutoProcessor.from_pretrained(
|
| 350 |
+
prompt_enhancer_image_caption_model_name_or_path, trust_remote_code=True
|
| 351 |
+
)
|
| 352 |
+
prompt_enhancer_llm_model = AutoModelForCausalLM.from_pretrained(
|
| 353 |
+
prompt_enhancer_llm_model_name_or_path,
|
| 354 |
+
torch_dtype="bfloat16",
|
| 355 |
+
)
|
| 356 |
+
prompt_enhancer_llm_tokenizer = AutoTokenizer.from_pretrained(
|
| 357 |
+
prompt_enhancer_llm_model_name_or_path,
|
| 358 |
+
)
|
| 359 |
+
else:
|
| 360 |
+
prompt_enhancer_image_caption_model = None
|
| 361 |
+
prompt_enhancer_image_caption_processor = None
|
| 362 |
+
prompt_enhancer_llm_model = None
|
| 363 |
+
prompt_enhancer_llm_tokenizer = None
|
| 364 |
+
|
| 365 |
+
vae = vae.to(torch.bfloat16)
|
| 366 |
+
if precision == "bfloat16" and transformer.dtype != torch.bfloat16:
|
| 367 |
+
transformer = transformer.to(torch.bfloat16)
|
| 368 |
+
text_encoder = text_encoder.to(torch.bfloat16)
|
| 369 |
+
|
| 370 |
+
# Use submodels for the pipeline
|
| 371 |
+
submodel_dict = {
|
| 372 |
+
"transformer": transformer,
|
| 373 |
+
"patchifier": patchifier,
|
| 374 |
+
"text_encoder": text_encoder,
|
| 375 |
+
"tokenizer": tokenizer,
|
| 376 |
+
"scheduler": scheduler,
|
| 377 |
+
"vae": vae,
|
| 378 |
+
"prompt_enhancer_image_caption_model": prompt_enhancer_image_caption_model,
|
| 379 |
+
"prompt_enhancer_image_caption_processor": prompt_enhancer_image_caption_processor,
|
| 380 |
+
"prompt_enhancer_llm_model": prompt_enhancer_llm_model,
|
| 381 |
+
"prompt_enhancer_llm_tokenizer": prompt_enhancer_llm_tokenizer,
|
| 382 |
+
"allowed_inference_steps": allowed_inference_steps,
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
pipeline = LTXVideoPipeline(**submodel_dict)
|
| 386 |
+
pipeline = pipeline.to(device)
|
| 387 |
+
return pipeline
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
def create_latent_upsampler(latent_upsampler_model_path: str, device: str):
|
| 391 |
+
latent_upsampler = LatentUpsampler.from_pretrained(latent_upsampler_model_path)
|
| 392 |
+
latent_upsampler.to(device)
|
| 393 |
+
latent_upsampler.eval()
|
| 394 |
+
return latent_upsampler
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def infer(
|
| 398 |
+
output_path: Optional[str],
|
| 399 |
+
seed: int,
|
| 400 |
+
pipeline_config: str,
|
| 401 |
+
image_cond_noise_scale: float,
|
| 402 |
+
height: Optional[int],
|
| 403 |
+
width: Optional[int],
|
| 404 |
+
num_frames: int,
|
| 405 |
+
frame_rate: int,
|
| 406 |
+
prompt: str,
|
| 407 |
+
negative_prompt: str,
|
| 408 |
+
offload_to_cpu: bool,
|
| 409 |
+
input_media_path: Optional[str] = None,
|
| 410 |
+
conditioning_media_paths: Optional[List[str]] = None,
|
| 411 |
+
conditioning_strengths: Optional[List[float]] = None,
|
| 412 |
+
conditioning_start_frames: Optional[List[int]] = None,
|
| 413 |
+
device: Optional[str] = None,
|
| 414 |
+
**kwargs,
|
| 415 |
+
):
|
| 416 |
+
# check if pipeline_config is a file
|
| 417 |
+
if not os.path.isfile(pipeline_config):
|
| 418 |
+
raise ValueError(f"Pipeline config file {pipeline_config} does not exist")
|
| 419 |
+
with open(pipeline_config, "r") as f:
|
| 420 |
+
pipeline_config = yaml.safe_load(f)
|
| 421 |
+
|
| 422 |
+
models_dir = "MODEL_DIR"
|
| 423 |
+
|
| 424 |
+
ltxv_model_name_or_path = pipeline_config["checkpoint_path"]
|
| 425 |
+
if not os.path.isfile(ltxv_model_name_or_path):
|
| 426 |
+
ltxv_model_path = hf_hub_download(
|
| 427 |
+
repo_id="Lightricks/LTX-Video",
|
| 428 |
+
filename=ltxv_model_name_or_path,
|
| 429 |
+
local_dir=models_dir,
|
| 430 |
+
repo_type="model",
|
| 431 |
+
)
|
| 432 |
+
else:
|
| 433 |
+
ltxv_model_path = ltxv_model_name_or_path
|
| 434 |
+
|
| 435 |
+
spatial_upscaler_model_name_or_path = pipeline_config.get(
|
| 436 |
+
"spatial_upscaler_model_path"
|
| 437 |
+
)
|
| 438 |
+
if spatial_upscaler_model_name_or_path and not os.path.isfile(
|
| 439 |
+
spatial_upscaler_model_name_or_path
|
| 440 |
+
):
|
| 441 |
+
spatial_upscaler_model_path = hf_hub_download(
|
| 442 |
+
repo_id="Lightricks/LTX-Video",
|
| 443 |
+
filename=spatial_upscaler_model_name_or_path,
|
| 444 |
+
local_dir=models_dir,
|
| 445 |
+
repo_type="model",
|
| 446 |
+
)
|
| 447 |
+
else:
|
| 448 |
+
spatial_upscaler_model_path = spatial_upscaler_model_name_or_path
|
| 449 |
+
|
| 450 |
+
if kwargs.get("input_image_path", None):
|
| 451 |
+
logger.warning(
|
| 452 |
+
"Please use conditioning_media_paths instead of input_image_path."
|
| 453 |
+
)
|
| 454 |
+
assert not conditioning_media_paths and not conditioning_start_frames
|
| 455 |
+
conditioning_media_paths = [kwargs["input_image_path"]]
|
| 456 |
+
conditioning_start_frames = [0]
|
| 457 |
+
|
| 458 |
+
# Validate conditioning arguments
|
| 459 |
+
if conditioning_media_paths:
|
| 460 |
+
# Use default strengths of 1.0
|
| 461 |
+
if not conditioning_strengths:
|
| 462 |
+
conditioning_strengths = [1.0] * len(conditioning_media_paths)
|
| 463 |
+
if not conditioning_start_frames:
|
| 464 |
+
raise ValueError(
|
| 465 |
+
"If `conditioning_media_paths` is provided, "
|
| 466 |
+
"`conditioning_start_frames` must also be provided"
|
| 467 |
+
)
|
| 468 |
+
if len(conditioning_media_paths) != len(conditioning_strengths) or len(
|
| 469 |
+
conditioning_media_paths
|
| 470 |
+
) != len(conditioning_start_frames):
|
| 471 |
+
raise ValueError(
|
| 472 |
+
"`conditioning_media_paths`, `conditioning_strengths`, "
|
| 473 |
+
"and `conditioning_start_frames` must have the same length"
|
| 474 |
+
)
|
| 475 |
+
if any(s < 0 or s > 1 for s in conditioning_strengths):
|
| 476 |
+
raise ValueError("All conditioning strengths must be between 0 and 1")
|
| 477 |
+
if any(f < 0 or f >= num_frames for f in conditioning_start_frames):
|
| 478 |
+
raise ValueError(
|
| 479 |
+
f"All conditioning start frames must be between 0 and {num_frames-1}"
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
seed_everething(seed)
|
| 483 |
+
if offload_to_cpu and not torch.cuda.is_available():
|
| 484 |
+
logger.warning(
|
| 485 |
+
"offload_to_cpu is set to True, but offloading will not occur since the model is already running on CPU."
|
| 486 |
+
)
|
| 487 |
+
offload_to_cpu = False
|
| 488 |
+
else:
|
| 489 |
+
offload_to_cpu = offload_to_cpu and get_total_gpu_memory() < 30
|
| 490 |
+
|
| 491 |
+
output_dir = (
|
| 492 |
+
Path(output_path)
|
| 493 |
+
if output_path
|
| 494 |
+
else Path(f"outputs/{datetime.today().strftime('%Y-%m-%d')}")
|
| 495 |
+
)
|
| 496 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
| 497 |
+
|
| 498 |
+
# Adjust dimensions to be divisible by 32 and num_frames to be (N * 8 + 1)
|
| 499 |
+
height_padded = ((height - 1) // 32 + 1) * 32
|
| 500 |
+
width_padded = ((width - 1) // 32 + 1) * 32
|
| 501 |
+
num_frames_padded = ((num_frames - 2) // 8 + 1) * 8 + 1
|
| 502 |
+
|
| 503 |
+
padding = calculate_padding(height, width, height_padded, width_padded)
|
| 504 |
+
|
| 505 |
+
logger.warning(
|
| 506 |
+
f"Padded dimensions: {height_padded}x{width_padded}x{num_frames_padded}"
|
| 507 |
+
)
|
| 508 |
+
|
| 509 |
+
prompt_enhancement_words_threshold = pipeline_config[
|
| 510 |
+
"prompt_enhancement_words_threshold"
|
| 511 |
+
]
|
| 512 |
+
|
| 513 |
+
prompt_word_count = len(prompt.split())
|
| 514 |
+
enhance_prompt = (
|
| 515 |
+
prompt_enhancement_words_threshold > 0
|
| 516 |
+
and prompt_word_count < prompt_enhancement_words_threshold
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
if prompt_enhancement_words_threshold > 0 and not enhance_prompt:
|
| 520 |
+
logger.info(
|
| 521 |
+
f"Prompt has {prompt_word_count} words, which exceeds the threshold of {prompt_enhancement_words_threshold}. Prompt enhancement disabled."
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
precision = pipeline_config["precision"]
|
| 525 |
+
text_encoder_model_name_or_path = pipeline_config["text_encoder_model_name_or_path"]
|
| 526 |
+
sampler = pipeline_config["sampler"]
|
| 527 |
+
prompt_enhancer_image_caption_model_name_or_path = pipeline_config[
|
| 528 |
+
"prompt_enhancer_image_caption_model_name_or_path"
|
| 529 |
+
]
|
| 530 |
+
prompt_enhancer_llm_model_name_or_path = pipeline_config[
|
| 531 |
+
"prompt_enhancer_llm_model_name_or_path"
|
| 532 |
+
]
|
| 533 |
+
|
| 534 |
+
pipeline = create_ltx_video_pipeline(
|
| 535 |
+
ckpt_path=ltxv_model_path,
|
| 536 |
+
precision=precision,
|
| 537 |
+
text_encoder_model_name_or_path=text_encoder_model_name_or_path,
|
| 538 |
+
sampler=sampler,
|
| 539 |
+
device=kwargs.get("device", get_device()),
|
| 540 |
+
enhance_prompt=enhance_prompt,
|
| 541 |
+
prompt_enhancer_image_caption_model_name_or_path=prompt_enhancer_image_caption_model_name_or_path,
|
| 542 |
+
prompt_enhancer_llm_model_name_or_path=prompt_enhancer_llm_model_name_or_path,
|
| 543 |
+
)
|
| 544 |
+
|
| 545 |
+
if pipeline_config.get("pipeline_type", None) == "multi-scale":
|
| 546 |
+
if not spatial_upscaler_model_path:
|
| 547 |
+
raise ValueError(
|
| 548 |
+
"spatial upscaler model path is missing from pipeline config file and is required for multi-scale rendering"
|
| 549 |
+
)
|
| 550 |
+
latent_upsampler = create_latent_upsampler(
|
| 551 |
+
spatial_upscaler_model_path, pipeline.device
|
| 552 |
+
)
|
| 553 |
+
pipeline = LTXMultiScalePipeline(pipeline, latent_upsampler=latent_upsampler)
|
| 554 |
+
|
| 555 |
+
media_item = None
|
| 556 |
+
if input_media_path:
|
| 557 |
+
media_item = load_media_file(
|
| 558 |
+
media_path=input_media_path,
|
| 559 |
+
height=height,
|
| 560 |
+
width=width,
|
| 561 |
+
max_frames=num_frames_padded,
|
| 562 |
+
padding=padding,
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
conditioning_items = (
|
| 566 |
+
prepare_conditioning(
|
| 567 |
+
conditioning_media_paths=conditioning_media_paths,
|
| 568 |
+
conditioning_strengths=conditioning_strengths,
|
| 569 |
+
conditioning_start_frames=conditioning_start_frames,
|
| 570 |
+
height=height,
|
| 571 |
+
width=width,
|
| 572 |
+
num_frames=num_frames,
|
| 573 |
+
padding=padding,
|
| 574 |
+
pipeline=pipeline,
|
| 575 |
+
)
|
| 576 |
+
if conditioning_media_paths
|
| 577 |
+
else None
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
stg_mode = pipeline_config.get("stg_mode", "attention_values")
|
| 581 |
+
del pipeline_config["stg_mode"]
|
| 582 |
+
if stg_mode.lower() == "stg_av" or stg_mode.lower() == "attention_values":
|
| 583 |
+
skip_layer_strategy = SkipLayerStrategy.AttentionValues
|
| 584 |
+
elif stg_mode.lower() == "stg_as" or stg_mode.lower() == "attention_skip":
|
| 585 |
+
skip_layer_strategy = SkipLayerStrategy.AttentionSkip
|
| 586 |
+
elif stg_mode.lower() == "stg_r" or stg_mode.lower() == "residual":
|
| 587 |
+
skip_layer_strategy = SkipLayerStrategy.Residual
|
| 588 |
+
elif stg_mode.lower() == "stg_t" or stg_mode.lower() == "transformer_block":
|
| 589 |
+
skip_layer_strategy = SkipLayerStrategy.TransformerBlock
|
| 590 |
+
else:
|
| 591 |
+
raise ValueError(f"Invalid spatiotemporal guidance mode: {stg_mode}")
|
| 592 |
+
|
| 593 |
+
# Prepare input for the pipeline
|
| 594 |
+
sample = {
|
| 595 |
+
"prompt": prompt,
|
| 596 |
+
"prompt_attention_mask": None,
|
| 597 |
+
"negative_prompt": negative_prompt,
|
| 598 |
+
"negative_prompt_attention_mask": None,
|
| 599 |
+
}
|
| 600 |
+
|
| 601 |
+
device = device or get_device()
|
| 602 |
+
generator = torch.Generator(device=device).manual_seed(seed)
|
| 603 |
+
|
| 604 |
+
images = pipeline(
|
| 605 |
+
**pipeline_config,
|
| 606 |
+
skip_layer_strategy=skip_layer_strategy,
|
| 607 |
+
generator=generator,
|
| 608 |
+
output_type="pt",
|
| 609 |
+
callback_on_step_end=None,
|
| 610 |
+
height=height_padded,
|
| 611 |
+
width=width_padded,
|
| 612 |
+
num_frames=num_frames_padded,
|
| 613 |
+
frame_rate=frame_rate,
|
| 614 |
+
**sample,
|
| 615 |
+
media_items=media_item,
|
| 616 |
+
conditioning_items=conditioning_items,
|
| 617 |
+
is_video=True,
|
| 618 |
+
vae_per_channel_normalize=True,
|
| 619 |
+
image_cond_noise_scale=image_cond_noise_scale,
|
| 620 |
+
mixed_precision=(precision == "mixed_precision"),
|
| 621 |
+
offload_to_cpu=offload_to_cpu,
|
| 622 |
+
device=device,
|
| 623 |
+
enhance_prompt=enhance_prompt,
|
| 624 |
+
).images
|
| 625 |
+
|
| 626 |
+
# Crop the padded images to the desired resolution and number of frames
|
| 627 |
+
(pad_left, pad_right, pad_top, pad_bottom) = padding
|
| 628 |
+
pad_bottom = -pad_bottom
|
| 629 |
+
pad_right = -pad_right
|
| 630 |
+
if pad_bottom == 0:
|
| 631 |
+
pad_bottom = images.shape[3]
|
| 632 |
+
if pad_right == 0:
|
| 633 |
+
pad_right = images.shape[4]
|
| 634 |
+
images = images[:, :, :num_frames, pad_top:pad_bottom, pad_left:pad_right]
|
| 635 |
+
|
| 636 |
+
for i in range(images.shape[0]):
|
| 637 |
+
# Gathering from B, C, F, H, W to C, F, H, W and then permuting to F, H, W, C
|
| 638 |
+
video_np = images[i].permute(1, 2, 3, 0).cpu().float().numpy()
|
| 639 |
+
# Unnormalizing images to [0, 255] range
|
| 640 |
+
video_np = (video_np * 255).astype(np.uint8)
|
| 641 |
+
fps = frame_rate
|
| 642 |
+
height, width = video_np.shape[1:3]
|
| 643 |
+
# In case a single image is generated
|
| 644 |
+
if video_np.shape[0] == 1:
|
| 645 |
+
output_filename = get_unique_filename(
|
| 646 |
+
f"image_output_{i}",
|
| 647 |
+
".png",
|
| 648 |
+
prompt=prompt,
|
| 649 |
+
seed=seed,
|
| 650 |
+
resolution=(height, width, num_frames),
|
| 651 |
+
dir=output_dir,
|
| 652 |
+
)
|
| 653 |
+
imageio.imwrite(output_filename, video_np[0])
|
| 654 |
+
else:
|
| 655 |
+
output_filename = get_unique_filename(
|
| 656 |
+
f"video_output_{i}",
|
| 657 |
+
".mp4",
|
| 658 |
+
prompt=prompt,
|
| 659 |
+
seed=seed,
|
| 660 |
+
resolution=(height, width, num_frames),
|
| 661 |
+
dir=output_dir,
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
# Write video
|
| 665 |
+
with imageio.get_writer(output_filename, fps=fps) as video:
|
| 666 |
+
for frame in video_np:
|
| 667 |
+
video.append_data(frame)
|
| 668 |
+
|
| 669 |
+
logger.warning(f"Output saved to {output_filename}")
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
def prepare_conditioning(
|
| 673 |
+
conditioning_media_paths: List[str],
|
| 674 |
+
conditioning_strengths: List[float],
|
| 675 |
+
conditioning_start_frames: List[int],
|
| 676 |
+
height: int,
|
| 677 |
+
width: int,
|
| 678 |
+
num_frames: int,
|
| 679 |
+
padding: tuple[int, int, int, int],
|
| 680 |
+
pipeline: LTXVideoPipeline,
|
| 681 |
+
) -> Optional[List[ConditioningItem]]:
|
| 682 |
+
"""Prepare conditioning items based on input media paths and their parameters.
|
| 683 |
+
|
| 684 |
+
Args:
|
| 685 |
+
conditioning_media_paths: List of paths to conditioning media (images or videos)
|
| 686 |
+
conditioning_strengths: List of conditioning strengths for each media item
|
| 687 |
+
conditioning_start_frames: List of frame indices where each item should be applied
|
| 688 |
+
height: Height of the output frames
|
| 689 |
+
width: Width of the output frames
|
| 690 |
+
num_frames: Number of frames in the output video
|
| 691 |
+
padding: Padding to apply to the frames
|
| 692 |
+
pipeline: LTXVideoPipeline object used for condition video trimming
|
| 693 |
+
|
| 694 |
+
Returns:
|
| 695 |
+
A list of ConditioningItem objects.
|
| 696 |
+
"""
|
| 697 |
+
conditioning_items = []
|
| 698 |
+
for path, strength, start_frame in zip(
|
| 699 |
+
conditioning_media_paths, conditioning_strengths, conditioning_start_frames
|
| 700 |
+
):
|
| 701 |
+
num_input_frames = orig_num_input_frames = get_media_num_frames(path)
|
| 702 |
+
if hasattr(pipeline, "trim_conditioning_sequence") and callable(
|
| 703 |
+
getattr(pipeline, "trim_conditioning_sequence")
|
| 704 |
+
):
|
| 705 |
+
num_input_frames = pipeline.trim_conditioning_sequence(
|
| 706 |
+
start_frame, orig_num_input_frames, num_frames
|
| 707 |
+
)
|
| 708 |
+
if num_input_frames < orig_num_input_frames:
|
| 709 |
+
logger.warning(
|
| 710 |
+
f"Trimming conditioning video {path} from {orig_num_input_frames} to {num_input_frames} frames."
|
| 711 |
+
)
|
| 712 |
+
|
| 713 |
+
media_tensor = load_media_file(
|
| 714 |
+
media_path=path,
|
| 715 |
+
height=height,
|
| 716 |
+
width=width,
|
| 717 |
+
max_frames=num_input_frames,
|
| 718 |
+
padding=padding,
|
| 719 |
+
just_crop=True,
|
| 720 |
+
)
|
| 721 |
+
conditioning_items.append(ConditioningItem(media_tensor, start_frame, strength))
|
| 722 |
+
return conditioning_items
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
def get_media_num_frames(media_path: str) -> int:
|
| 726 |
+
is_video = any(
|
| 727 |
+
media_path.lower().endswith(ext) for ext in [".mp4", ".avi", ".mov", ".mkv"]
|
| 728 |
+
)
|
| 729 |
+
num_frames = 1
|
| 730 |
+
if is_video:
|
| 731 |
+
reader = imageio.get_reader(media_path)
|
| 732 |
+
num_frames = reader.count_frames()
|
| 733 |
+
reader.close()
|
| 734 |
+
return num_frames
|
| 735 |
+
|
| 736 |
+
|
| 737 |
+
def load_media_file(
|
| 738 |
+
media_path: str,
|
| 739 |
+
height: int,
|
| 740 |
+
width: int,
|
| 741 |
+
max_frames: int,
|
| 742 |
+
padding: tuple[int, int, int, int],
|
| 743 |
+
just_crop: bool = False,
|
| 744 |
+
) -> torch.Tensor:
|
| 745 |
+
is_video = any(
|
| 746 |
+
media_path.lower().endswith(ext) for ext in [".mp4", ".avi", ".mov", ".mkv"]
|
| 747 |
+
)
|
| 748 |
+
if is_video:
|
| 749 |
+
reader = imageio.get_reader(media_path)
|
| 750 |
+
num_input_frames = min(reader.count_frames(), max_frames)
|
| 751 |
+
|
| 752 |
+
# Read and preprocess the relevant frames from the video file.
|
| 753 |
+
frames = []
|
| 754 |
+
for i in range(num_input_frames):
|
| 755 |
+
frame = Image.fromarray(reader.get_data(i))
|
| 756 |
+
frame_tensor = load_image_to_tensor_with_resize_and_crop(
|
| 757 |
+
frame, height, width, just_crop=just_crop
|
| 758 |
+
)
|
| 759 |
+
frame_tensor = torch.nn.functional.pad(frame_tensor, padding)
|
| 760 |
+
frames.append(frame_tensor)
|
| 761 |
+
reader.close()
|
| 762 |
+
|
| 763 |
+
# Stack frames along the temporal dimension
|
| 764 |
+
media_tensor = torch.cat(frames, dim=2)
|
| 765 |
+
else: # Input image
|
| 766 |
+
media_tensor = load_image_to_tensor_with_resize_and_crop(
|
| 767 |
+
media_path, height, width, just_crop=just_crop
|
| 768 |
+
)
|
| 769 |
+
media_tensor = torch.nn.functional.pad(media_tensor, padding)
|
| 770 |
+
return media_tensor
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
if __name__ == "__main__":
|
| 774 |
+
main()
|
aduc_framework/managers/mmaudio_manager.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# managers/mmaudio_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Version: 3.0.0 (GPU Pool Manager)
|
| 6 |
+
#
|
| 7 |
+
# Esta versão refatora o MMAudioManager para um modelo de Pool com Workers,
|
| 8 |
+
# permitindo o uso de múltiplas GPUs dedicadas para a geração de áudio
|
| 9 |
+
# com um sistema de rodízio para gerenciamento eficiente de VRAM.
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import logging
|
| 13 |
+
import subprocess
|
| 14 |
+
import os
|
| 15 |
+
import time
|
| 16 |
+
import yaml
|
| 17 |
+
import gc
|
| 18 |
+
import threading
|
| 19 |
+
from pathlib import Path
|
| 20 |
+
import gradio as gr
|
| 21 |
+
import sys
|
| 22 |
+
|
| 23 |
+
# Imports relativos para o hardware_manager
|
| 24 |
+
from ..tools.hardware_manager import hardware_manager
|
| 25 |
+
|
| 26 |
+
logger = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
# --- Gerenciamento de Dependências ---
|
| 29 |
+
DEPS_DIR = Path("./deps")
|
| 30 |
+
MMAUDIO_REPO_DIR = DEPS_DIR / "MMAudio"
|
| 31 |
+
MMAUDIO_REPO_URL = "https://github.com/hkchengrex/MMAudio.git"
|
| 32 |
+
|
| 33 |
+
# Lazy-loaded imports
|
| 34 |
+
ModelConfig, all_model_cfg, mmaudio_generate, load_video, make_video = None, None, None, None, None
|
| 35 |
+
MMAudio, get_my_mmaudio = None, None
|
| 36 |
+
FeaturesUtils = None
|
| 37 |
+
SequenceConfig = None
|
| 38 |
+
FlowMatching = None
|
| 39 |
+
|
| 40 |
+
class MMAudioWorker:
|
| 41 |
+
"""Representa uma única instância do pipeline MMAudio em um dispositivo."""
|
| 42 |
+
def __init__(self, device_id: str):
|
| 43 |
+
self.device = torch.device(device_id)
|
| 44 |
+
self.cpu_device = torch.device("cpu")
|
| 45 |
+
self.dtype = torch.bfloat16 if 'cuda' in self.device.type else torch.float32
|
| 46 |
+
|
| 47 |
+
self.net: 'MMAudio' = None
|
| 48 |
+
self.feature_utils: 'FeaturesUtils' = None
|
| 49 |
+
self.seq_cfg: 'SequenceConfig' = None
|
| 50 |
+
self.model_config: 'ModelConfig' = None
|
| 51 |
+
|
| 52 |
+
self._check_and_run_global_setup()
|
| 53 |
+
self._lazy_load_mmaudio_modules()
|
| 54 |
+
logger.info(f"MMAudio Worker inicializado para o dispositivo {self.device}.")
|
| 55 |
+
|
| 56 |
+
def _lazy_load_mmaudio_modules(self):
|
| 57 |
+
"""Importa dinamicamente os módulos do MMAudio."""
|
| 58 |
+
global ModelConfig, all_model_cfg, mmaudio_generate, load_video, make_video, MMAudio, get_my_mmaudio, FeaturesUtils, SequenceConfig, FlowMatching
|
| 59 |
+
if MMAudio is not None: return
|
| 60 |
+
|
| 61 |
+
from mmaudio.eval_utils import ModelConfig, all_model_cfg, generate as mmaudio_generate, load_video, make_video
|
| 62 |
+
from mmaudio.model.flow_matching import FlowMatching
|
| 63 |
+
from mmaudio.model.networks import MMAudio, get_my_mmaudio
|
| 64 |
+
from mmaudio.model.utils.features_utils import FeaturesUtils
|
| 65 |
+
from mmaudio.model.sequence_config import SequenceConfig
|
| 66 |
+
logger.info("Módulos do MMAudio foram carregados dinamicamente.")
|
| 67 |
+
|
| 68 |
+
@staticmethod
|
| 69 |
+
def _check_and_run_global_setup():
|
| 70 |
+
"""Executa o setup de clonagem do repositório e download de modelos uma única vez."""
|
| 71 |
+
setup_flag = DEPS_DIR / "mmaudio.setup.complete"
|
| 72 |
+
if setup_flag.exists():
|
| 73 |
+
return True
|
| 74 |
+
|
| 75 |
+
logger.info("--- Iniciando Setup Global do MMAudio (primeira execução) ---")
|
| 76 |
+
if not MMAUDIO_REPO_DIR.exists():
|
| 77 |
+
DEPS_DIR.mkdir(exist_ok=True)
|
| 78 |
+
subprocess.run(["git", "clone", "--depth", "1", MMAUDIO_REPO_URL, str(MMAUDIO_REPO_DIR)], check=True)
|
| 79 |
+
|
| 80 |
+
if str(MMAUDIO_REPO_DIR.resolve()) not in sys.path:
|
| 81 |
+
sys.path.insert(0, str(MMAUDIO_REPO_DIR.resolve()))
|
| 82 |
+
|
| 83 |
+
# Importar após adicionar ao path
|
| 84 |
+
from mmaudio.eval_utils import all_model_cfg as cfg
|
| 85 |
+
|
| 86 |
+
# Ajustar caminhos e baixar modelos
|
| 87 |
+
for cfg_key in cfg:
|
| 88 |
+
config = cfg[cfg_key]
|
| 89 |
+
config.model_path = MMAUDIO_REPO_DIR / config.model_path
|
| 90 |
+
config.vae_path = MMAUDIO_REPO_DIR / config.vae_path
|
| 91 |
+
if config.bigvgan_16k_path:
|
| 92 |
+
config.bigvgan_16k_path = MMAUDIO_REPO_DIR / config.bigvgan_16k_path
|
| 93 |
+
config.synchformer_ckpt = MMAUDIO_REPO_DIR / config.synchformer_ckpt
|
| 94 |
+
config.download_if_needed()
|
| 95 |
+
|
| 96 |
+
setup_flag.touch()
|
| 97 |
+
logger.info("--- Setup Global do MMAudio Concluído ---")
|
| 98 |
+
return True
|
| 99 |
+
|
| 100 |
+
def initialize_models(self):
|
| 101 |
+
"""Carrega os modelos do worker para a CPU e depois para a GPU designada."""
|
| 102 |
+
if self.net is not None: return
|
| 103 |
+
|
| 104 |
+
self.model_config = all_model_cfg['large_44k_v2']
|
| 105 |
+
self.seq_cfg = self.model_config.seq_cfg
|
| 106 |
+
|
| 107 |
+
logger.info(f"Worker {self.device}: Carregando modelo MMAudio para a CPU...")
|
| 108 |
+
self.net = get_my_mmaudio(self.model_config.model_name).eval()
|
| 109 |
+
self.net.load_weights(torch.load(self.model_config.model_path, map_location=self.cpu_device, weights_only=True))
|
| 110 |
+
|
| 111 |
+
self.feature_utils = FeaturesUtils(
|
| 112 |
+
tod_vae_ckpt=self.model_config.vae_path,
|
| 113 |
+
synchformer_ckpt=self.model_config.synchformer_ckpt,
|
| 114 |
+
enable_conditions=True, mode=self.model_config.mode,
|
| 115 |
+
bigvgan_vocoder_ckpt=self.model_config.bigvgan_16k_path,
|
| 116 |
+
need_vae_encoder=False
|
| 117 |
+
).eval()
|
| 118 |
+
|
| 119 |
+
self.net.to(self.device, self.dtype)
|
| 120 |
+
self.feature_utils.to(self.device, self.dtype)
|
| 121 |
+
logger.info(f"Worker {self.device}: Modelos MMAudio prontos na VRAM.")
|
| 122 |
+
|
| 123 |
+
def unload_models(self):
|
| 124 |
+
"""Descarrega os modelos da VRAM, movendo-os para a CPU."""
|
| 125 |
+
if self.net is None: return
|
| 126 |
+
logger.info(f"Worker {self.device}: Descarregando modelos MMAudio da VRAM...")
|
| 127 |
+
self.net.to(self.cpu_device)
|
| 128 |
+
self.feature_utils.to(self.cpu_device)
|
| 129 |
+
del self.net, self.feature_utils, self.seq_cfg, self.model_config
|
| 130 |
+
self.net, self.feature_utils, self.seq_cfg, self.model_config = None, None, None, None
|
| 131 |
+
gc.collect()
|
| 132 |
+
if torch.cuda.is_available(): torch.cuda.empty_cache()
|
| 133 |
+
|
| 134 |
+
def generate_audio_internal(self, video_path: str, prompt: str, duration_seconds: float, output_path: str) -> str:
|
| 135 |
+
"""Lógica de geração de áudio que roda na GPU do worker."""
|
| 136 |
+
negative_prompt = "human voice, speech, talking, singing, narration"
|
| 137 |
+
rng = torch.Generator(device=self.device).manual_seed(int(time.time()))
|
| 138 |
+
fm = FlowMatching(min_sigma=0, inference_mode='euler', num_steps=25)
|
| 139 |
+
|
| 140 |
+
video_info = load_video(Path(video_path), duration_seconds)
|
| 141 |
+
self.seq_cfg.duration = video_info.duration_sec
|
| 142 |
+
self.net.update_seq_lengths(self.seq_cfg.latent_seq_len, self.seq_cfg.clip_seq_len, self.seq_cfg.sync_seq_len)
|
| 143 |
+
|
| 144 |
+
with torch.no_grad():
|
| 145 |
+
audios = mmaudio_generate(
|
| 146 |
+
clip_video=video_info.clip_frames.unsqueeze(0).to(self.device, self.dtype),
|
| 147 |
+
sync_video=video_info.sync_frames.unsqueeze(0).to(self.device, self.dtype),
|
| 148 |
+
text=[prompt], negative_text=[negative_prompt],
|
| 149 |
+
feature_utils=self.feature_utils, net=self.net, fm=fm, rng=rng, cfg_strength=4.5
|
| 150 |
+
)
|
| 151 |
+
audio_waveform = audios.float().cpu()[0]
|
| 152 |
+
|
| 153 |
+
make_video(video_info, Path(output_path), audio_waveform, sampling_rate=self.seq_cfg.sampling_rate)
|
| 154 |
+
return output_path
|
| 155 |
+
|
| 156 |
+
class MMAudioPoolManager:
|
| 157 |
+
def __init__(self, device_ids: list[str], workspace_dir: str):
|
| 158 |
+
logger.info(f"MMAUDIO POOL MANAGER: Criando workers para os dispositivos: {device_ids}")
|
| 159 |
+
self.workspace_dir = workspace_dir
|
| 160 |
+
if not device_ids or 'cpu' in device_ids:
|
| 161 |
+
raise ValueError("MMAudioPoolManager requer GPUs dedicadas.")
|
| 162 |
+
self.workers = [MMAudioWorker(device_id) for device_id in device_ids]
|
| 163 |
+
self.current_worker_index = 0
|
| 164 |
+
self.lock = threading.Lock()
|
| 165 |
+
self.last_cleanup_thread = None
|
| 166 |
+
|
| 167 |
+
def _cleanup_worker_thread(self, worker: MMAudioWorker):
|
| 168 |
+
logger.info(f"MMAUDIO CLEANUP THREAD: Iniciando limpeza de {worker.device} em background...")
|
| 169 |
+
worker.unload_models()
|
| 170 |
+
|
| 171 |
+
def generate_audio_for_video(self, video_path: str, prompt: str, duration_seconds: float, output_path_override: str = None) -> str:
|
| 172 |
+
if duration_seconds < 1:
|
| 173 |
+
logger.warning(f"Vídeo muito curto ({duration_seconds:.2f}s). Pulando geração de áudio.")
|
| 174 |
+
return video_path
|
| 175 |
+
|
| 176 |
+
worker_to_use = None
|
| 177 |
+
try:
|
| 178 |
+
with self.lock:
|
| 179 |
+
if self.last_cleanup_thread and self.last_cleanup_thread.is_alive():
|
| 180 |
+
self.last_cleanup_thread.join()
|
| 181 |
+
|
| 182 |
+
worker_to_use = self.workers[self.current_worker_index]
|
| 183 |
+
previous_worker_index = (self.current_worker_index - 1 + len(self.workers)) % len(self.workers)
|
| 184 |
+
worker_to_cleanup = self.workers[previous_worker_index]
|
| 185 |
+
|
| 186 |
+
cleanup_thread = threading.Thread(target=self._cleanup_worker_thread, args=(worker_to_cleanup,))
|
| 187 |
+
cleanup_thread.start()
|
| 188 |
+
self.last_cleanup_thread = cleanup_thread
|
| 189 |
+
|
| 190 |
+
worker_to_use.initialize_models()
|
| 191 |
+
self.current_worker_index = (self.current_worker_index + 1) % len(self.workers)
|
| 192 |
+
|
| 193 |
+
logger.info(f"MMAUDIO POOL MANAGER: Gerando áudio em {worker_to_use.device}...")
|
| 194 |
+
|
| 195 |
+
output_path = output_path_override or os.path.join(self.workspace_dir, f"{Path(video_path).stem}_with_audio.mp4")
|
| 196 |
+
|
| 197 |
+
return worker_to_use.generate_audio_internal(
|
| 198 |
+
video_path=video_path, prompt=prompt, duration_seconds=duration_seconds, output_path=output_path
|
| 199 |
+
)
|
| 200 |
+
except Exception as e:
|
| 201 |
+
logger.error(f"MMAUDIO POOL MANAGER: Erro durante a geração de áudio: {e}", exc_info=True)
|
| 202 |
+
raise gr.Error(f"Falha na geração de áudio: {e}")
|
| 203 |
+
|
| 204 |
+
# --- Instanciação Singleton ---
|
| 205 |
+
class MMAudioPlaceholder:
|
| 206 |
+
def generate_audio_for_video(self, video_path, *args, **kwargs):
|
| 207 |
+
logger.error("MMAudio não foi inicializado pois nenhuma GPU foi alocada. Pulando etapa de áudio.")
|
| 208 |
+
return video_path
|
| 209 |
+
|
| 210 |
+
try:
|
| 211 |
+
with open("config.yaml", 'r') as f:
|
| 212 |
+
config = yaml.safe_load(f)
|
| 213 |
+
WORKSPACE_DIR = config['application']['workspace_dir']
|
| 214 |
+
|
| 215 |
+
mmaudio_gpus_required = config['specialists'].get('mmaudio', {}).get('gpus_required', 0)
|
| 216 |
+
mmaudio_device_ids = hardware_manager.allocate_gpus('MMAudio', mmaudio_gpus_required)
|
| 217 |
+
|
| 218 |
+
if mmaudio_gpus_required > 0 and 'cpu' not in mmaudio_device_ids:
|
| 219 |
+
mmaudio_manager_singleton = MMAudioPoolManager(device_ids=mmaudio_device_ids, workspace_dir=WORKSPACE_DIR)
|
| 220 |
+
logger.info("Especialista de Áudio (MMAudio Pool) pronto.")
|
| 221 |
+
else:
|
| 222 |
+
mmaudio_manager_singleton = MMAudioPlaceholder()
|
| 223 |
+
logger.warning("MMAudio Pool Manager não foi inicializado. Nenhuma GPU foi requisitada na config.yaml.")
|
| 224 |
+
except Exception as e:
|
| 225 |
+
logger.critical(f"Falha CRÍTICA ao inicializar o MMAudioManager: {e}", exc_info=True)
|
| 226 |
+
mmaudio_manager_singleton = MMAudioPlaceholder()
|
aduc_framework/managers/seedvr_manager.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/managers/seedvr_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 11.0.0 (Otimização com FP16 Checkpoint)
|
| 6 |
+
#
|
| 7 |
+
# Utiliza o checkpoint FP16 otimizado para reduzir significativamente o uso
|
| 8 |
+
# de VRAM, mantendo a arquitetura de "monkey patch" para desativar o
|
| 9 |
+
# paralelismo problemático e garantir a estabilidade em modo de GPU única.
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import os
|
| 13 |
+
import gc
|
| 14 |
+
import yaml
|
| 15 |
+
import logging
|
| 16 |
+
import sys
|
| 17 |
+
import subprocess
|
| 18 |
+
from pathlib import Path
|
| 19 |
+
from urllib.parse import urlparse
|
| 20 |
+
from torch.hub import download_url_to_file
|
| 21 |
+
import mediapy
|
| 22 |
+
from einops import rearrange
|
| 23 |
+
import shutil
|
| 24 |
+
from omegaconf import OmegaConf
|
| 25 |
+
from typing import Generator, Dict, Any
|
| 26 |
+
|
| 27 |
+
from ..tools.hardware_manager import hardware_manager
|
| 28 |
+
|
| 29 |
+
logger = logging.getLogger(__name__)
|
| 30 |
+
|
| 31 |
+
# Define os caminhos base para evitar hardcoding
|
| 32 |
+
APP_ROOT = Path(os.getcwd())
|
| 33 |
+
DEPS_DIR = APP_ROOT / "deps"
|
| 34 |
+
SEEDVR_SPACE_DIR = DEPS_DIR / "SeedVR_Space"
|
| 35 |
+
SEEDVR_SPACE_URL = "https://huggingface.co/spaces/ByteDance-Seed/SeedVR2-3B"
|
| 36 |
+
|
| 37 |
+
def _load_file_from_url(url, model_dir='./', file_name=None):
|
| 38 |
+
"""Helper para baixar arquivos de modelo e checkpoints."""
|
| 39 |
+
os.makedirs(model_dir, exist_ok=True)
|
| 40 |
+
filename = file_name or os.path.basename(urlparse(url).path)
|
| 41 |
+
cached_file = os.path.abspath(os.path.join(model_dir, filename))
|
| 42 |
+
if not os.path.exists(cached_file):
|
| 43 |
+
logger.info(f"Baixando arquivo do SeedVR: {filename}...")
|
| 44 |
+
download_url_to_file(url, cached_file, hash_prefix=None, progress=True)
|
| 45 |
+
return cached_file
|
| 46 |
+
|
| 47 |
+
class SeedVrManager:
|
| 48 |
+
"""Gerencia uma única instância do pipeline SeedVR em uma GPU dedicada e isolada."""
|
| 49 |
+
def __init__(self, device_id: str):
|
| 50 |
+
self.global_device_id = device_id
|
| 51 |
+
self.local_device_name = 'cuda:0' # O que o processo enxergará
|
| 52 |
+
self.gpu_index = self.global_device_id.split(':')[-1]
|
| 53 |
+
|
| 54 |
+
self.runner = None
|
| 55 |
+
self._check_and_run_global_setup()
|
| 56 |
+
logger.info(f"SeedVR Manager (FP16 Optimized) inicializado para operar na GPU {self.global_device_id}.")
|
| 57 |
+
|
| 58 |
+
@staticmethod
|
| 59 |
+
def _check_and_run_global_setup():
|
| 60 |
+
"""Executa o setup de arquivos e aplica o patch para desativar o paralelismo."""
|
| 61 |
+
setup_flag = DEPS_DIR / "seedvr.setup.complete"
|
| 62 |
+
|
| 63 |
+
if str(APP_ROOT) not in sys.path: sys.path.insert(0, str(APP_ROOT))
|
| 64 |
+
|
| 65 |
+
try:
|
| 66 |
+
from common import decorators
|
| 67 |
+
import functools
|
| 68 |
+
def _passthrough_decorator(func):
|
| 69 |
+
@functools.wraps(func)
|
| 70 |
+
def wrapped(*args, **kwargs):
|
| 71 |
+
return func(*args, **kwargs)
|
| 72 |
+
return wrapped
|
| 73 |
+
decorators.master_only = _passthrough_decorator
|
| 74 |
+
logger.info("Monkey patch aplicado com sucesso em 'common.decorators.master_only' para desativar o paralelismo.")
|
| 75 |
+
except Exception as e:
|
| 76 |
+
logger.error(f"Falha ao aplicar o monkey patch para o SeedVR: {e}", exc_info=True)
|
| 77 |
+
|
| 78 |
+
if setup_flag.exists(): return True
|
| 79 |
+
|
| 80 |
+
logger.info("--- Iniciando Setup Global do SeedVR (primeira execução) ---")
|
| 81 |
+
if not SEEDVR_SPACE_DIR.exists():
|
| 82 |
+
DEPS_DIR.mkdir(exist_ok=True, parents=True)
|
| 83 |
+
logger.info(f"Clonando repositório do SeedVR de {SEEDVR_SPACE_URL}...")
|
| 84 |
+
subprocess.run(["git", "clone", "--depth", "1", SEEDVR_SPACE_URL, str(SEEDVR_SPACE_DIR)], check=True)
|
| 85 |
+
|
| 86 |
+
required_dirs = ["projects", "common", "models", "configs_3b", "data"]
|
| 87 |
+
for dirname in required_dirs:
|
| 88 |
+
source, target = SEEDVR_SPACE_DIR / dirname, APP_ROOT / dirname
|
| 89 |
+
if not target.exists():
|
| 90 |
+
logger.info(f"Copiando diretório '{dirname}' do SeedVR para a raiz do projeto...")
|
| 91 |
+
shutil.copytree(source, target)
|
| 92 |
+
|
| 93 |
+
try:
|
| 94 |
+
import apex
|
| 95 |
+
except ImportError:
|
| 96 |
+
logger.info("Dependência 'apex' não encontrada. Instalando a partir do wheel fornecido...")
|
| 97 |
+
apex_url = 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/apex-0.1-cp310-cp310-linux_x86_64.whl'
|
| 98 |
+
apex_wheel_path = _load_file_from_url(url=apex_url, model_dir=str(DEPS_DIR))
|
| 99 |
+
subprocess.run([sys.executable, '-m', 'pip', 'install', apex_wheel_path], check=True)
|
| 100 |
+
|
| 101 |
+
ckpt_dir = APP_ROOT / 'ckpts'
|
| 102 |
+
ckpt_dir.mkdir(exist_ok=True)
|
| 103 |
+
|
| 104 |
+
# >>> ALTERAÇÃO PRINCIPAL: Usando o checkpoint FP16 otimizado <<<
|
| 105 |
+
model_urls = {
|
| 106 |
+
'vae': 'https://huggingface.co/batuhanince/seedvr_3b_fp16/resolve/main/ema_vae_fp16.safetensors',
|
| 107 |
+
'dit_3b': 'https://huggingface.co/batuhanince/seedvr_3b_fp16/resolve/main/seedvr2_ema_3b_fp16.safetensors',
|
| 108 |
+
'pos_emb': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/pos_emb.pt',
|
| 109 |
+
'neg_emb': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/neg_emb.pt'
|
| 110 |
+
}
|
| 111 |
+
for name, url in model_urls.items():
|
| 112 |
+
_load_file_from_url(url=url, model_dir=str(ckpt_dir))
|
| 113 |
+
|
| 114 |
+
setup_flag.touch()
|
| 115 |
+
logger.info("--- Setup Global do SeedVR Concluído ---")
|
| 116 |
+
|
| 117 |
+
def _initialize_runner(self):
|
| 118 |
+
if self.runner is not None: return
|
| 119 |
+
os.environ['CUDA_VISIBLE_DEVICES'] = self.gpu_index
|
| 120 |
+
from projects.video_diffusion_sr.infer import VideoDiffusionInfer
|
| 121 |
+
from common.config import load_config
|
| 122 |
+
|
| 123 |
+
logger.info(f"Manager na GPU {self.global_device_id}: Inicializando runner SeedVR 3B com checkpoint FP16...")
|
| 124 |
+
|
| 125 |
+
config_path = APP_ROOT / 'configs_3b' / 'main.yaml'
|
| 126 |
+
# >>> O caminho agora aponta para o novo arquivo .safetensors <<<
|
| 127 |
+
checkpoint_path = APP_ROOT / 'ckpts' / 'seedvr2_ema_3b_fp16.safetensors'
|
| 128 |
+
|
| 129 |
+
config = load_config(str(config_path))
|
| 130 |
+
self.runner = VideoDiffusionInfer(config)
|
| 131 |
+
OmegaConf.set_readonly(self.runner.config, False)
|
| 132 |
+
|
| 133 |
+
self.runner.configure_dit_model(device=self.local_device_name, checkpoint=str(checkpoint_path))
|
| 134 |
+
self.runner.configure_vae_model()
|
| 135 |
+
|
| 136 |
+
logger.info(f"Manager na GPU {self.global_device_id}: Runner 3B (FP16) pronto na VRAM.")
|
| 137 |
+
|
| 138 |
+
def _unload_runner(self):
|
| 139 |
+
if self.runner is not None:
|
| 140 |
+
del self.runner; self.runner = None
|
| 141 |
+
gc.collect()
|
| 142 |
+
if torch.cuda.is_available(): torch.cuda.empty_cache()
|
| 143 |
+
logger.info(f"Manager na GPU {self.global_device_id}: Runner descarregado da VRAM.")
|
| 144 |
+
if 'CUDA_VISIBLE_DEVICES' in os.environ:
|
| 145 |
+
del os.environ['CUDA_VISIBLE_DEVICES']
|
| 146 |
+
|
| 147 |
+
def process_video(self, input_video_path: str, output_video_path: str, prompt: str, steps: int = 100, seed: int = 666) -> Generator[Dict[str, Any], None, None]:
|
| 148 |
+
try:
|
| 149 |
+
self._initialize_runner()
|
| 150 |
+
yield {"progress": 0.1, "desc": "Runner inicializado. Lendo vídeo..."}
|
| 151 |
+
|
| 152 |
+
device = torch.device(self.local_device_name)
|
| 153 |
+
|
| 154 |
+
from common.seed import set_seed
|
| 155 |
+
from data.image.transforms.divisible_crop import DivisibleCrop
|
| 156 |
+
from data.image.transforms.na_resize import NaResize
|
| 157 |
+
from data.video.transforms.rearrange import Rearrange
|
| 158 |
+
from projects.video_diffusion_sr.color_fix import wavelet_reconstruction
|
| 159 |
+
from torchvision.transforms import Compose, Lambda, Normalize
|
| 160 |
+
from torchvision.io.video import read_video
|
| 161 |
+
|
| 162 |
+
set_seed(seed, same_across_ranks=True)
|
| 163 |
+
self.runner.config.diffusion.timesteps.sampling.steps = steps
|
| 164 |
+
self.runner.configure_diffusion()
|
| 165 |
+
|
| 166 |
+
video_tensor = read_video(input_video_path, output_format="TCHW")[0] / 255.0
|
| 167 |
+
res_h, res_w = video_tensor.shape[-2:]
|
| 168 |
+
video_transform = Compose([
|
| 169 |
+
NaResize(resolution=(res_h * res_w) ** 0.5, mode="area", downsample_only=False),
|
| 170 |
+
Lambda(lambda x: torch.clamp(x, 0.0, 1.0)),
|
| 171 |
+
DivisibleCrop((16, 16)), Normalize(0.5, 0.5), Rearrange("t c h w -> c t h w"),
|
| 172 |
+
])
|
| 173 |
+
cond_latents = [video_transform(video_tensor.to(device))]
|
| 174 |
+
yield {"progress": 0.2, "desc": "Encodificando para o espaço latente..."}
|
| 175 |
+
self.runner.dit.to("cpu"); self.runner.vae.to(device)
|
| 176 |
+
cond_latents = self.runner.vae_encode(cond_latents)
|
| 177 |
+
self.runner.vae.to("cpu"); gc.collect(); torch.cuda.empty_cache(); self.runner.dit.to(device)
|
| 178 |
+
|
| 179 |
+
pos_emb = torch.load(APP_ROOT / 'ckpts' / 'pos_emb.pt').to(device)
|
| 180 |
+
neg_emb = torch.load(APP_ROOT / 'ckpts' / 'neg_emb.pt').to(device)
|
| 181 |
+
text_embeds_dict = {"texts_pos": [pos_emb], "texts_neg": [neg_emb]}
|
| 182 |
+
|
| 183 |
+
noises = [torch.randn_like(latent) for latent in cond_latents]
|
| 184 |
+
conditions = [self.runner.get_condition(noise, latent_blur=latent, task="sr") for noise, latent in zip(noises, cond_latents)]
|
| 185 |
+
|
| 186 |
+
yield {"progress": 0.5, "desc": "Aplicando difusão para super-resolução..."}
|
| 187 |
+
with torch.no_grad(), torch.autocast("cuda", torch.bfloat16, enabled=True):
|
| 188 |
+
video_tensors = self.runner.inference(noises=noises, conditions=conditions, dit_offload=True, **text_embeds_dict)
|
| 189 |
+
|
| 190 |
+
yield {"progress": 0.8, "desc": "Decodificando para o espaço de pixel..."}
|
| 191 |
+
self.runner.dit.to("cpu"); gc.collect(); torch.cuda.empty_cache(); self.runner.vae.to(device)
|
| 192 |
+
samples = self.runner.vae_decode(video_tensors)
|
| 193 |
+
final_sample, input_video_sample = samples[0], cond_latents[0]
|
| 194 |
+
if final_sample.shape[1] < input_video_sample.shape[1]:
|
| 195 |
+
input_video_sample = input_video_sample[:, :final_sample.shape[1]]
|
| 196 |
+
|
| 197 |
+
final_sample = wavelet_reconstruction(rearrange(final_sample, "c t h w -> t c h w"), rearrange(input_video_sample, "c t h w -> t c h w"))
|
| 198 |
+
final_sample = rearrange(final_sample, "t c h w -> t h w c")
|
| 199 |
+
final_sample = final_sample.clip(-1, 1).mul_(0.5).add_(0.5).mul_(255).round()
|
| 200 |
+
final_sample_np = final_sample.to(torch.uint8).cpu().numpy()
|
| 201 |
+
|
| 202 |
+
yield {"progress": 0.9, "desc": "Escrevendo arquivo de vídeo final..."}
|
| 203 |
+
mediapy.write_video(output_video_path, final_sample_np, fps=24)
|
| 204 |
+
yield {"progress": 1.0, "final_path": output_video_path}
|
| 205 |
+
|
| 206 |
+
finally:
|
| 207 |
+
self._unload_runner()
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
# --- Instanciação Singleton ---
|
| 211 |
+
class SeedVrPlaceholder:
|
| 212 |
+
def process_video(self, input_video_path, *args, **kwargs):
|
| 213 |
+
logger.warning("SeedVR está desabilitado (gpus_required: 0). Pulando etapa de masterização HD.")
|
| 214 |
+
yield {"progress": 1.0, "final_path": input_video_path}
|
| 215 |
+
|
| 216 |
+
try:
|
| 217 |
+
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 218 |
+
seedvr_gpus_required = config['specialists'].get('seedvr', {}).get('gpus_required', 0)
|
| 219 |
+
|
| 220 |
+
if seedvr_gpus_required > 0:
|
| 221 |
+
seedvr_device_ids = hardware_manager.allocate_gpus('SeedVR', seedvr_gpus_required)
|
| 222 |
+
if seedvr_device_ids and 'cpu' not in seedvr_device_ids:
|
| 223 |
+
device_to_use = seedvr_device_ids[0]
|
| 224 |
+
seedvr_manager_singleton = SeedVrManager(device_id=device_to_use)
|
| 225 |
+
logger.info(f"Especialista de Masterização HD (SeedVR FP16) pronto para usar a GPU {device_to_use}.")
|
| 226 |
+
else:
|
| 227 |
+
seedvr_manager_singleton = SeedVrPlaceholder()
|
| 228 |
+
else:
|
| 229 |
+
seedvr_manager_singleton = SeedVrPlaceholder()
|
| 230 |
+
except Exception as e:
|
| 231 |
+
logger.critical(f"Falha CRÍTICA ao inicializar o SeedVrManager: {e}", exc_info=True)
|
| 232 |
+
seedvr_manager_singleton = SeedVrPlaceholder()
|
aduc_framework/managers/upscaler_specialist.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# upscaler_specialist.py
|
| 2 |
+
# Copyright (C) 2025 Carlos Rodrigues
|
| 3 |
+
# Especialista ADUC para upscaling espacial de tensores latentes.
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import logging
|
| 7 |
+
from diffusers import LTXLatentUpsamplePipeline
|
| 8 |
+
from ..managers.ltx_manager import ltx_manager_singleton
|
| 9 |
+
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
+
|
| 12 |
+
class UpscalerSpecialist:
|
| 13 |
+
"""
|
| 14 |
+
Especialista responsável por aumentar a resolução espacial de tensores latentes
|
| 15 |
+
usando o LTX Video Spatial Upscaler.
|
| 16 |
+
"""
|
| 17 |
+
def __init__(self):
|
| 18 |
+
# Força uso de CUDA se disponível
|
| 19 |
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 20 |
+
self.base_vae = None
|
| 21 |
+
self.pipe_upsample = None
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _lazy_init(self):
|
| 25 |
+
try:
|
| 26 |
+
# Tenta usar o VAE do ltx_manager
|
| 27 |
+
if ltx_manager_singleton.workers:
|
| 28 |
+
candidate_vae = ltx_manager_singleton.workers[0].pipeline.vae
|
| 29 |
+
if candidate_vae.__class__.__name__ == "AutoencoderKLLTXVideo":
|
| 30 |
+
self.base_vae = candidate_vae
|
| 31 |
+
logger.info("[Upscaler] Usando VAE do ltx_manager (AutoencoderKLLTXVideo).")
|
| 32 |
+
else:
|
| 33 |
+
logger.warning(f"[Upscaler] VAE incompatível: {type(candidate_vae)}. "
|
| 34 |
+
"Carregando AutoencoderKLLTXVideo manualmente...")
|
| 35 |
+
from diffusers.models.autoencoders import AutoencoderKLLTXVideo
|
| 36 |
+
self.base_vae = AutoencoderKLLTXVideo.from_pretrained(
|
| 37 |
+
"linoyts/LTX-Video-spatial-upscaler-0.9.8",
|
| 38 |
+
subfolder="vae",
|
| 39 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
| 40 |
+
).to(self.device)
|
| 41 |
+
else:
|
| 42 |
+
logger.warning("[Upscaler] Nenhum worker disponível, carregando VAE manualmente...")
|
| 43 |
+
from diffusers.models.autoencoders import AutoencoderKLLTXVideo
|
| 44 |
+
self.base_vae = AutoencoderKLLTXVideo.from_pretrained(
|
| 45 |
+
"linoyts/LTX-Video-spatial-upscaler-0.9.8",
|
| 46 |
+
subfolder="vae",
|
| 47 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
| 48 |
+
).to(self.device)
|
| 49 |
+
|
| 50 |
+
# Carregar pipeline
|
| 51 |
+
self.pipe_upsample = LTXLatentUpsamplePipeline.from_pretrained(
|
| 52 |
+
"linoyts/LTX-Video-spatial-upscaler-0.9.8",
|
| 53 |
+
vae=self.base_vae,
|
| 54 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
| 55 |
+
).to(self.device)
|
| 56 |
+
|
| 57 |
+
logger.info("[Upscaler] Pipeline carregado com sucesso.")
|
| 58 |
+
|
| 59 |
+
except Exception as e:
|
| 60 |
+
logger.error(f"[Upscaler] Falha ao carregar pipeline: {e}")
|
| 61 |
+
self.pipe_upsample = None
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@torch.no_grad()
|
| 66 |
+
def upscale(self, latents: torch.Tensor) -> torch.Tensor:
|
| 67 |
+
"""Aplica o upscaling 2x nos tensores latentes fornecidos."""
|
| 68 |
+
self._lazy_init()
|
| 69 |
+
if self.pipe_upsample is None:
|
| 70 |
+
logger.warning("[Upscaler] Pipeline indisponível. Retornando latentes originais.")
|
| 71 |
+
return latents
|
| 72 |
+
|
| 73 |
+
try:
|
| 74 |
+
logger.info(f"[Upscaler] Recebido shape {latents.shape}. Executando upscale em {self.device}...")
|
| 75 |
+
|
| 76 |
+
# [CORREÇÃO FINAL] Conforme a documentação oficial, o resultado está em .frames
|
| 77 |
+
result = self.pipe_upsample(latents=latents, output_type="latent")
|
| 78 |
+
output_tensor = result.frames
|
| 79 |
+
|
| 80 |
+
logger.info(f"[Upscaler] Upscale concluído. Novo shape: {output_tensor.shape}")
|
| 81 |
+
return output_tensor
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
logger.error(f"[Upscaler] Erro durante upscale: {e}", exc_info=True)
|
| 85 |
+
return latents
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
# ---------------------------
|
| 89 |
+
# Singleton global
|
| 90 |
+
# ---------------------------
|
| 91 |
+
upscaler_specialist_singleton = UpscalerSpecialist()
|
aduc_framework/managers/vae_manager.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/managers/vae_manager.py
|
| 2 |
+
#
|
| 3 |
+
# Versão 2.1.0 (Correção de Timestep no Decode)
|
| 4 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 5 |
+
#
|
| 6 |
+
# - Corrige um `AssertionError` na função `decode` ao não passar o argumento
|
| 7 |
+
# `timestep` esperado pelo decodificador do VAE.
|
| 8 |
+
# - Adiciona um `timestep` padrão (0.05) para a decodificação, garantindo
|
| 9 |
+
# uma reconstrução de imagem limpa e estável.
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import logging
|
| 13 |
+
import gc
|
| 14 |
+
import yaml
|
| 15 |
+
from typing import List
|
| 16 |
+
from PIL import Image
|
| 17 |
+
import numpy as np
|
| 18 |
+
|
| 19 |
+
from ltx_video.models.autoencoders.vae_encode import vae_encode, vae_decode
|
| 20 |
+
from ..tools.hardware_manager import hardware_manager
|
| 21 |
+
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
class VaeManager:
|
| 25 |
+
"""
|
| 26 |
+
Especialista VAE "Hot" e Persistente.
|
| 27 |
+
Carrega o modelo VAE em uma GPU dedicada uma única vez e o mantém lá,
|
| 28 |
+
pronto para processar requisições de encode/decode com latência mínima.
|
| 29 |
+
"""
|
| 30 |
+
def __init__(self):
|
| 31 |
+
with open("config.yaml", 'r') as f:
|
| 32 |
+
config = yaml.safe_load(f)
|
| 33 |
+
gpus_required = config['specialists'].get('vae', {}).get('gpus_required', 0)
|
| 34 |
+
|
| 35 |
+
if gpus_required > 0 and torch.cuda.is_available():
|
| 36 |
+
device_id = hardware_manager.allocate_gpus('VAE_Manager', gpus_required)[0]
|
| 37 |
+
self.device = torch.device(device_id)
|
| 38 |
+
logger.info(f"VaeManager: GPU dedicada '{device_id}' alocada.")
|
| 39 |
+
else:
|
| 40 |
+
self.device = torch.device('cpu')
|
| 41 |
+
logger.warning("VaeManager: Nenhuma GPU dedicada foi alocada no config.yaml. Operando em modo CPU.")
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
from ..managers.ltx_manager import ltx_manager_singleton
|
| 45 |
+
self.vae = ltx_manager_singleton.workers[0].pipeline.vae
|
| 46 |
+
except ImportError as e:
|
| 47 |
+
logger.critical("Falha ao importar ltx_manager_singleton. Garanta que VaeManager seja importado DEPOIS de LtxManager.", exc_info=True)
|
| 48 |
+
raise e
|
| 49 |
+
|
| 50 |
+
self.vae.to(self.device)
|
| 51 |
+
self.vae.eval()
|
| 52 |
+
self.dtype = self.vae.dtype
|
| 53 |
+
logger.info(f"VaeManager inicializado. Modelo VAE está 'quente' e pronto na {self.device} com dtype {self.dtype}.")
|
| 54 |
+
|
| 55 |
+
def _preprocess_pil_image(self, pil_image: Image.Image, target_resolution: tuple) -> torch.Tensor:
|
| 56 |
+
from PIL import ImageOps
|
| 57 |
+
img = pil_image.convert("RGB")
|
| 58 |
+
processed_img = ImageOps.fit(img, target_resolution, Image.Resampling.LANCZOS)
|
| 59 |
+
image_np = np.array(processed_img).astype(np.float32) / 255.0
|
| 60 |
+
tensor = torch.from_numpy(image_np).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
|
| 61 |
+
return (tensor * 2.0) - 1.0
|
| 62 |
+
|
| 63 |
+
@torch.no_grad()
|
| 64 |
+
def encode_batch(self, pil_images: List[Image.Image], target_resolution: tuple) -> List[torch.Tensor]:
|
| 65 |
+
if not pil_images:
|
| 66 |
+
return []
|
| 67 |
+
|
| 68 |
+
latents_list = []
|
| 69 |
+
for img in pil_images:
|
| 70 |
+
pixel_tensor = self._preprocess_pil_image(img, target_resolution)
|
| 71 |
+
pixel_tensor_gpu = pixel_tensor.to(self.device, dtype=self.dtype)
|
| 72 |
+
latents = vae_encode(pixel_tensor_gpu, self.vae, vae_per_channel_normalize=True)
|
| 73 |
+
latents_list.append(latents.cpu())
|
| 74 |
+
return latents_list
|
| 75 |
+
|
| 76 |
+
@torch.no_grad()
|
| 77 |
+
def decode(self, latent_tensor: torch.Tensor, decode_timestep: float = 0.05) -> torch.Tensor:
|
| 78 |
+
"""Decodifica um tensor latente para o espaço de pixels."""
|
| 79 |
+
latent_tensor_gpu = latent_tensor.to(self.device, dtype=self.dtype)
|
| 80 |
+
|
| 81 |
+
# --- CORREÇÃO APLICADA AQUI ---
|
| 82 |
+
# O modelo espera um tensor de timestep, um para cada item no batch.
|
| 83 |
+
num_items_in_batch = latent_tensor_gpu.shape[0]
|
| 84 |
+
timestep_tensor = torch.tensor([decode_timestep] * num_items_in_batch, device=self.device, dtype=self.dtype)
|
| 85 |
+
|
| 86 |
+
pixels = vae_decode(
|
| 87 |
+
latent_tensor_gpu,
|
| 88 |
+
self.vae,
|
| 89 |
+
is_video=True,
|
| 90 |
+
timestep=timestep_tensor, # Passando o tensor de timestep
|
| 91 |
+
vae_per_channel_normalize=True
|
| 92 |
+
)
|
| 93 |
+
# --- FIM DA CORREÇÃO ---
|
| 94 |
+
|
| 95 |
+
return pixels.cpu()
|
| 96 |
+
|
| 97 |
+
# --- Instância Singleton ---
|
| 98 |
+
vae_manager_singleton = VaeManager()
|
aduc_framework/prompts/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Euia-AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR para geração de vídeo coerente.
|
| 2 |
+
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 3 |
+
#
|
| 4 |
+
# Contato:
|
| 5 |
+
# Carlos Rodrigues dos Santos
|
| 6 | |
| 7 |
+
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 8 |
+
#
|
| 9 |
+
# Repositórios e Projetos Relacionados:
|
| 10 |
+
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 11 |
+
# Hugging Face (Ltx-SuperTime-60Secondos): https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/
|
| 12 |
+
# Hugging Face (Novinho): https://huggingface.co/spaces/Carlexxx/Novinho/
|
| 13 |
+
#
|
| 14 |
+
# This program is free software: you can redistribute it and/or modify
|
| 15 |
+
# it under the terms of the GNU Affero General Public License as published by
|
| 16 |
+
# the Free Software Foundation, either version 3 of the License, or
|
| 17 |
+
# (at your option) any later version.
|
| 18 |
+
#
|
| 19 |
+
# This program is distributed in the hope that it will be useful,
|
| 20 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 21 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 22 |
+
# GNU Affero General Public License for more details.
|
| 23 |
+
#
|
| 24 |
+
# You should have received a copy of the GNU Affero General Public License
|
| 25 |
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
aduc_framework/prompts/NOTICE.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTICE
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2025 Carlos Rodrigues dos Santos. All rights reserved.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Aviso de Propriedade Intelectual e Licenciamento
|
| 8 |
+
|
| 9 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 10 |
+
|
| 11 |
+
O método e o sistema de orquestração de prompts denominados **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste documento e implementados neste software, estão atualmente em processo de patenteamento.
|
| 12 |
+
|
| 13 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, incluindo, mas não se limitando a:
|
| 14 |
+
|
| 15 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 16 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 17 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 18 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 19 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 20 |
+
|
| 21 |
+
### **Reconhecimento e Implicações (EM PORTUGUÊS):**
|
| 22 |
+
|
| 23 |
+
Ao acessar ou utilizar este software e a arquitetura ADUC aqui implementada, você reconhece:
|
| 24 |
+
|
| 25 |
+
1. A natureza inovadora e a importância da arquitetura ADUC no campo da orquestração de prompts para IA.
|
| 26 |
+
2. Que a essência desta arquitetura, ou suas implementações derivadas, podem estar sujeitas a direitos de propriedade intelectual, incluindo patentes.
|
| 27 |
+
3. Que o uso comercial, a reprodução da lógica central da ADUC em sistemas independentes, ou a exploração direta da invenção sem o devido licenciamento podem infringir os direitos de patente pendente.
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
### **Patent Pending (IN ENGLISH):**
|
| 32 |
+
|
| 33 |
+
The method and system for prompt orchestration named **ADUC (Automated Discovery and Orchestration of Complex tasks)**, as described herein and implemented in this software, are currently in the process of being patented.
|
| 34 |
+
|
| 35 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 36 |
+
|
| 37 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 38 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 39 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 40 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 41 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 42 |
+
|
| 43 |
+
### **Acknowledgement and Implications (IN ENGLISH):**
|
| 44 |
+
|
| 45 |
+
By accessing or using this software and the ADUC architecture implemented herein, you acknowledge:
|
| 46 |
+
|
| 47 |
+
1. The innovative nature and significance of the ADUC architecture in the field of AI prompt orchestration.
|
| 48 |
+
2. That the essence of this architecture, or its derivative implementations, may be subject to intellectual property rights, including patents.
|
| 49 |
+
3. That commercial use, reproduction of ADUC's core logic in independent systems, or direct exploitation of the invention without proper licensing may infringe upon pending patent rights.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
## Licença AGPLv3
|
| 54 |
+
|
| 55 |
+
This program is free software: you can redistribute it and/or modify
|
| 56 |
+
it under the terms of the GNU Affero General Public License as published by
|
| 57 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 58 |
+
(at your option) any later version.
|
| 59 |
+
|
| 60 |
+
This program is distributed in the hope that it will be useful,
|
| 61 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 62 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 63 |
+
GNU Affero General Public License for more details.
|
| 64 |
+
|
| 65 |
+
You should have received a copy of the GNU Affero General Public License
|
| 66 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
**Contato para Consultas:**
|
| 71 |
+
|
| 72 |
+
Para mais informações sobre a arquitetura ADUC, o status do patenteamento, ou para discutir licenciamento para usos comerciais ou não conformes com a AGPLv3, por favor, entre em contato:
|
| 73 |
+
|
| 74 |
+
Carlos Rodrigues dos Santos
|
| 75 | |
| 76 |
+
Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
aduc_framework/prompts/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Euia-AducSdr
|
| 3 |
+
emoji: 🎥
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
app_file: app.py
|
| 8 |
+
pinned: true
|
| 9 |
+
license: agpl-3.0
|
| 10 |
+
short_description: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
### 🇧🇷 Português
|
| 15 |
+
|
| 16 |
+
Uma implementação aberta e funcional da arquitetura ADUC-SDR (Arquitetura de Unificação Compositiva - Escala Dinâmica e Resiliente), projetada para a geração de vídeo coerente de longa duração. Este projeto materializa os princípios de fragmentação, navegação geométrica e um mecanismo de "eco causal 4bits memoria" para garantir a continuidade física e narrativa em sequências de vídeo geradas por múltiplos modelos de IA.
|
| 17 |
+
|
| 18 |
+
**Licença:** Este projeto é licenciado sob os termos da **GNU Affero General Public License v3.0**. Isto significa que se você usar este software (ou qualquer trabalho derivado) para fornecer um serviço através de uma rede, você é **obrigado a disponibilizar o código-fonte completo** da sua versão para os usuários desse serviço.
|
| 19 |
+
|
| 20 |
+
- **Copyright (C) 4 de Agosto de 2025, Carlos Rodrigues dos Santos**
|
| 21 |
+
- Uma cópia completa da licença pode ser encontrada no arquivo [LICENSE](LICENSE).
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
### 🇬🇧 English
|
| 26 |
+
|
| 27 |
+
An open and functional implementation of the ADUC-SDR (Architecture for Compositive Unification - Dynamic and Resilient Scaling) architecture, designed for long-form coherent video generation. This project materializes the principles of fragmentation, geometric navigation, and a "causal echo 4bits memori" mechanism to ensure physical and narrative continuity in video sequences generated by multiple AI models.
|
| 28 |
+
|
| 29 |
+
**License:** This project is licensed under the terms of the **GNU Affero General Public License v3.0**. This means that if you use this software (or any derivative work) to provide a service over a network, you are **required to make the complete source code** of your version available to the users of that service.
|
| 30 |
+
|
| 31 |
+
- **Copyright (C) August 4, 2025, Carlos Rodrigues dos Santos**
|
| 32 |
+
- A full copy of the license can be found in the [LICENSE](LICENSE) file.
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
## **Aviso de Propriedade Intelectual e Patenteamento**
|
| 37 |
+
|
| 38 |
+
### **Processo de Patenteamento em Andamento (EM PORTUGUÊS):**
|
| 39 |
+
|
| 40 |
+
A arquitetura e o método **ADUC (Automated Discovery and Orchestration of Complex tasks)**, conforme descritos neste projeto e nas reivindicações associadas, estão **atualmente em processo de patenteamento**.
|
| 41 |
+
|
| 42 |
+
O titular dos direitos, Carlos Rodrigues dos Santos, está buscando proteção legal para as inovações chave da arquitetura ADUC, que incluem, mas não se limitam a:
|
| 43 |
+
|
| 44 |
+
* Fragmentação e escalonamento de solicitações que excedem limites de contexto de modelos de IA.
|
| 45 |
+
* Distribuição inteligente de sub-tarefas para especialistas heterogêneos.
|
| 46 |
+
* Gerenciamento de estado persistido com avaliação iterativa e realimentação para o planejamento de próximas etapas.
|
| 47 |
+
* Planejamento e roteamento sensível a custo, latência e requisitos de qualidade.
|
| 48 |
+
* O uso de "tokens universais" para comunicação agnóstica a modelos.
|
| 49 |
+
|
| 50 |
+
Ao utilizar este software e a arquitetura ADUC aqui implementada, você reconhece a natureza inovadora desta arquitetura e que a **reprodução ou exploração da lógica central da ADUC em sistemas independentes pode infringir direitos de patente pendente.**
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
### **Patent Pending (IN ENGLISH):**
|
| 55 |
+
|
| 56 |
+
The **ADUC (Automated Discovery and Orchestration of Complex tasks)** architecture and method, as described in this project and its associated claims, are **currently in the process of being patented.**
|
| 57 |
+
|
| 58 |
+
The rights holder, Carlos Rodrigues dos Santos, is seeking legal protection for the key innovations of the ADUC architecture, including, but not limited to:
|
| 59 |
+
|
| 60 |
+
* Fragmentation and scaling of requests exceeding AI model context limits.
|
| 61 |
+
* Intelligent distribution of sub-tasks to heterogeneous specialists.
|
| 62 |
+
* Persistent state management with iterative evaluation and feedback for planning subsequent steps.
|
| 63 |
+
* Cost, latency, and quality-aware planning and routing.
|
| 64 |
+
* The use of "universal tokens" for model-agnostic communication.
|
| 65 |
+
|
| 66 |
+
By using this software and the ADUC architecture implemented herein, you acknowledge the innovative nature of this architecture and that **the reproduction or exploitation of ADUC's core logic in independent systems may infringe upon pending patent rights.**
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
### Detalhes Técnicos e Reivindicações da ADUC
|
| 71 |
+
|
| 72 |
+
#### 🇧🇷 Definição Curta (para Tese e Patente)
|
| 73 |
+
|
| 74 |
+
**ADUC** é um *framework pré-input* e *intermediário* de **gerenciamento de prompts** que:
|
| 75 |
+
|
| 76 |
+
1. **fragmenta** solicitações acima do limite de contexto de qualquer modelo,
|
| 77 |
+
2. **escala linearmente** (processo sequencial com memória persistida),
|
| 78 |
+
3. **distribui** sub-tarefas a **especialistas** (modelos/ferramentas heterogêneos), e
|
| 79 |
+
4. **realimenta** a próxima etapa com avaliação do que foi feito/esperado (LLM diretor).
|
| 80 |
+
|
| 81 |
+
Não é um modelo; é uma **camada orquestradora** plugável antes do input de modelos existentes (texto, imagem, áudio, vídeo), usando *tokens universais* e a tecnologia atual.
|
| 82 |
+
|
| 83 |
+
#### 🇬🇧 Short Definition (for Thesis and Patent)
|
| 84 |
+
|
| 85 |
+
**ADUC** is a *pre-input* and *intermediate* **prompt management framework** that:
|
| 86 |
+
|
| 87 |
+
1. **fragments** requests exceeding any model's context limit,
|
| 88 |
+
2. **scales linearly** (sequential process with persisted memory),
|
| 89 |
+
3. **distributes** sub-tasks to **specialists** (heterogeneous models/tools), and
|
| 90 |
+
4. **feeds back** to the next step with an evaluation of what was done/expected (director LLM).
|
| 91 |
+
|
| 92 |
+
It is not a model; it is a pluggable **orchestration layer** before the input of existing models (text, image, audio, video), using *universal tokens* and current technology.
|
| 93 |
+
|
| 94 |
+
---
|
| 95 |
+
|
| 96 |
+
#### 🇧🇷 Elementos Essenciais (Telegráfico)
|
| 97 |
+
|
| 98 |
+
* **Agnóstico a modelos:** opera com qualquer LLM/difusor/API.
|
| 99 |
+
* **Pré-input manager:** recebe pedido do usuário, **divide** em blocos ≤ limite de tokens, **prioriza**, **agenda** e **roteia**.
|
| 100 |
+
* **Memória persistida:** resultados/latentes/“eco” viram **estado compartilhado** para o próximo bloco (nada é ignorado).
|
| 101 |
+
* **Especialistas:** *routers* decidem quem faz o quê (ex.: “descrição → LLM-A”, “keyframe → Img-B”, “vídeo → Vid-C”).
|
| 102 |
+
* **Controle de qualidade:** LLM diretor compara *o que fez* × *o que deveria* × *o que falta* e **regenera objetivos** do próximo fragmento.
|
| 103 |
+
* **Custo/latência-aware:** planeja pela **VRAM/tempo/custo**, não tenta “abraçar tudo de uma vez”.
|
| 104 |
+
|
| 105 |
+
#### 🇬🇧 Essential Elements (Telegraphic)
|
| 106 |
+
|
| 107 |
+
* **Model-agnostic:** operates with any LLM/diffuser/API.
|
| 108 |
+
* **Pre-input manager:** receives user request, **divides** into blocks ≤ token limit, **prioritizes**, **schedules**, and **routes**.
|
| 109 |
+
* **Persisted memory:** results/latents/“echo” become **shared state** for the next block (nothing is ignored).
|
| 110 |
+
* **Specialists:** *routers* decide who does what (e.g., “description → LLM-A”, “keyframe → Img-B”, “video → Vid-C”).
|
| 111 |
+
* **Quality control:** director LLM compares *what was done* × *what should be done* × *what is missing* and **regenerates objectives** for the next fragment.
|
| 112 |
+
* **Cost/latency-aware:** plans by **VRAM/time/cost**, does not try to “embrace everything at once”.
|
| 113 |
+
|
| 114 |
+
---
|
| 115 |
+
|
| 116 |
+
#### 🇧🇷 Reivindicações Independentes (Método e Sistema)
|
| 117 |
+
|
| 118 |
+
**Reivindicação Independente (Método) — Versão Enxuta:**
|
| 119 |
+
|
| 120 |
+
1. **Método** de **orquestração de prompts** para execução de tarefas acima do limite de contexto de modelos de IA, compreendendo:
|
| 121 |
+
(a) **receber** uma solicitação que excede um limite de tokens;
|
| 122 |
+
(b) **analisar** a solicitação por um **LLM diretor** e **fragmentá-la** em sub-tarefas ≤ limite;
|
| 123 |
+
(c) **selecionar** especialistas de execução para cada sub-tarefa com base em capacidades declaradas;
|
| 124 |
+
(d) **gerar** prompts específicos por sub-tarefa em **tokens universais**, incluindo referências ao **estado persistido** de execuções anteriores;
|
| 125 |
+
(e) **executar sequencialmente** as sub-tarefas e **persistir** suas saídas como memória (incluindo latentes/eco/artefatos);
|
| 126 |
+
(f) **avaliar** automaticamente a saída versus metas declaradas e **regenerar objetivos** do próximo fragmento;
|
| 127 |
+
(g) **iterar** (b)–(f) até que os critérios de completude sejam atendidos, produzindo o resultado agregado;
|
| 128 |
+
em que o framework **escala linearmente** no tempo e armazenamento físico, **independente** da janela de contexto dos modelos subjacentes.
|
| 129 |
+
|
| 130 |
+
**Reivindicação Independente (Sistema):**
|
| 131 |
+
|
| 132 |
+
2. **Sistema** de orquestração de prompts, compreendendo: um **planejador LLM diretor**; um **roteador de especialistas**; um **banco de estado persistido** (incl. memória cinética para vídeo); um **gerador de prompts universais**; e um **módulo de avaliação/realimentação**, acoplados por uma **API pré-input** a modelos heterogêneos.
|
| 133 |
+
|
| 134 |
+
#### 🇬🇧 Independent Claims (Method and System)
|
| 135 |
+
|
| 136 |
+
**Independent Claim (Method) — Concise Version:**
|
| 137 |
+
|
| 138 |
+
1. A **method** for **prompt orchestration** for executing tasks exceeding AI model context limits, comprising:
|
| 139 |
+
(a) **receiving** a request that exceeds a token limit;
|
| 140 |
+
(b) **analyzing** the request by a **director LLM** and **fragmenting it** into sub-tasks ≤ the limit;
|
| 141 |
+
(c) **selecting** execution specialists for each sub-task based on declared capabilities;
|
| 142 |
+
(d) **generating** specific prompts per sub-task in **universal tokens**, including references to the **persisted state** of previous executions;
|
| 143 |
+
(e) **sequentially executing** the sub-tasks and **persisting** their outputs as memory (including latents/echo/artifacts);
|
| 144 |
+
(f) **automatically evaluating** the output against declared goals and **regenerating objectives** for the next fragment;
|
| 145 |
+
(g) **iterating** (b)–(f) until completion criteria are met, producing the aggregated result;
|
| 146 |
+
wherein the framework **scales linearly** in time and physical storage, **independent** of the context window of the underlying models.
|
| 147 |
+
|
| 148 |
+
**Independent Claim (System):**
|
| 149 |
+
|
| 150 |
+
2. A prompt orchestration **system**, comprising: a **director LLM planner**; a **specialist router**; a **persisted state bank** (incl. kinetic memory for video); a **universal prompt generator**; and an **evaluation/feedback module**, coupled via a **pre-input API** to heterogeneous models.
|
| 151 |
+
|
| 152 |
+
---
|
| 153 |
+
|
| 154 |
+
#### 🇧🇷 Dependentes Úteis
|
| 155 |
+
|
| 156 |
+
* (3) Onde o roteamento considera **custo/latência/VRAM** e metas de qualidade.
|
| 157 |
+
* (4) Onde o banco de estado inclui **eco cinético** para vídeo (últimos *n* frames/latentes/fluxo).
|
| 158 |
+
* (5) Onde a avaliação usa métricas específicas por domínio (Lflow, consistência semântica, etc.).
|
| 159 |
+
* (6) Onde *tokens universais* padronizam instruções entre especialistas.
|
| 160 |
+
* (7) Onde a orquestração decide **cut vs continuous** e **corte regenerativo** (Déjà-Vu) ao editar vídeo.
|
| 161 |
+
* (8) Onde o sistema **nunca descarta** conteúdo excedente: **reagenda** em novos fragmentos.
|
| 162 |
+
|
| 163 |
+
#### 🇬🇧 Useful Dependents
|
| 164 |
+
|
| 165 |
+
* (3) Wherein routing considers **cost/latency/VRAM** and quality goals.
|
| 166 |
+
* (4) Wherein the state bank includes **kinetic echo** for video (last *n* frames/latents/flow).
|
| 167 |
+
* (5) Wherein evaluation uses domain-specific metrics (Lflow, semantic consistency, etc.).
|
| 168 |
+
* (6) Wherein *universal tokens* standardize instructions between specialists.
|
| 169 |
+
* (7) Wherein orchestration decides **cut vs continuous** and **regenerative cut** (Déjà-Vu) when editing video.
|
| 170 |
+
* (8) Wherein the system **never discards** excess content: it **reschedules** it in new fragments.
|
| 171 |
+
|
| 172 |
+
---
|
| 173 |
+
|
| 174 |
+
#### 🇧🇷 Como isso conversa com SDR (Vídeo)
|
| 175 |
+
|
| 176 |
+
* **Eco Cinético**: é um **tipo de estado persistido** consumido pelo próximo passo.
|
| 177 |
+
* **Déjà-Vu (Corte Regenerativo)**: é **uma política de orquestração** aplicada quando há edição; ADUC decide, monta os prompts certos e chama o especialista de vídeo.
|
| 178 |
+
* **Cut vs Continuous**: decisão do **diretor** com base em estado + metas; ADUC roteia e garante a sobreposição/remoção final.
|
| 179 |
+
|
| 180 |
+
#### 🇬🇧 How this Converses with SDR (Video)
|
| 181 |
+
|
| 182 |
+
* **Kinetic Echo**: is a **type of persisted state** consumed by the next step.
|
| 183 |
+
* **Déjà-Vu (Regenerative Cut)**: is an **orchestration policy** applied during editing; ADUC decides, crafts the right prompts, and calls the video specialist.
|
| 184 |
+
* **Cut vs Continuous**: decision made by the **director** based on state + goals; ADUC routes and ensures the final overlap/removal.
|
| 185 |
+
|
| 186 |
+
---
|
| 187 |
+
|
| 188 |
+
#### 🇧🇷 Mensagem Clara ao Usuário (Experiência)
|
| 189 |
+
|
| 190 |
+
> “Seu pedido excede o limite X do modelo Y. Em vez de truncar silenciosamente, o **ADUC** dividirá e **entregará 100%** do conteúdo por etapas coordenadas.”
|
| 191 |
+
|
| 192 |
+
Isso é diferencial prático e jurídico: **não-obviedade** por transformar limite de contexto em **pipeline controlado**, com **persistência de estado** e **avaliação iterativa**.
|
| 193 |
+
|
| 194 |
+
#### 🇬🇧 Clear User Message (Experience)
|
| 195 |
+
|
| 196 |
+
> "Your request exceeds model Y's limit X. Instead of silently truncating, **ADUC** will divide and **deliver 100%** of the content through coordinated steps."
|
| 197 |
+
|
| 198 |
+
This is a practical and legal differentiator: **non-obviousness** by transforming context limits into a **controlled pipeline**, with **state persistence** and **iterative evaluation**.
|
| 199 |
+
|
| 200 |
+
---
|
| 201 |
+
|
| 202 |
+
### Contact / Contato / Contacto
|
| 203 |
+
|
| 204 |
+
- **Author / Autor:** Carlos Rodrigues dos Santos
|
| 205 |
+
- **Email:** [email protected]
|
| 206 |
+
- **GitHub:** [https://github.com/carlex22/Aduc-sdr](https://github.com/carlex22/Aduc-sdr)
|
| 207 |
+
- **Hugging Face Spaces:**
|
| 208 |
+
- [Ltx-SuperTime-60Secondos](https://huggingface.co/spaces/Carlexx/Ltx-SuperTime-60Secondos/)
|
| 209 |
+
- [Novinho](https://huggingface.co/spaces/Carlexxx/Novinho/)
|
| 210 |
+
|
| 211 |
+
---
|
aduc_framework/prompts/TASK_01_CREATE_NARRATIVE_SYNTHESIS.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE:
|
| 2 |
+
Você é um Roteirista-Analista. Sua função é sintetizar um conceito textual e referências visuais em uma única história coesa e contínua.
|
| 3 |
+
|
| 4 |
+
# GOAL:
|
| 5 |
+
Escrever uma narrativa fluida baseada na "Ideia Principal" e inspirada pelas "Imagens de Referência". A história deve ser dividida em exatamente {num_scenes} parágrafos e focar em descrever ações e eventos visuais.
|
| 6 |
+
|
| 7 |
+
# CRITICAL RULES:
|
| 8 |
+
1. **SÍNTESE VISUAL:** A história DEVE ser uma fusão do que está escrito na "Ideia Principal" e do que é visível nas "Imagens de Referência".
|
| 9 |
+
2. **AÇÃO, NÃO ABSTRAÇÃO:** Descreva eventos e movimentos concretos. O que a câmera veria? Evite sentimentos internos ou metáforas.
|
| 10 |
+
3. **NARRATIVA CONTÍNUA:** O texto deve fluir como uma única história do começo ao fim.
|
| 11 |
+
|
| 12 |
+
# CONTEXT:
|
| 13 |
+
- **Ideia Principal:** {global_prompt}
|
| 14 |
+
- **Imagens de Referência:** [As imagens serão fornecidas aqui]
|
| 15 |
+
- **Número de Parágrafos a Criar:** {num_scenes}
|
| 16 |
+
|
| 17 |
+
# OUTPUT_RULES:
|
| 18 |
+
- A resposta deve conter APENAS o texto bruto da história contínua. Não use JSON. Não adicione títulos ou notas.
|
aduc_framework/prompts/anticipatory_keyframe_prompt.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE: AI Art Director
|
| 2 |
+
|
| 3 |
+
# TASK:
|
| 4 |
+
# Analyze the context and generate a JSON object to direct the creation of the next keyframe.
|
| 5 |
+
# Your goal is to visually connect the CURRENT scene to the FUTURE scene.
|
| 6 |
+
|
| 7 |
+
# CONTEXT:
|
| 8 |
+
# - History: {historico_prompt}
|
| 9 |
+
# - Current Scene: "{cena_atual}"
|
| 10 |
+
# - Future Scene: "{cena_futura}"
|
| 11 |
+
# - Available Reference IDs: {available_ref_images}
|
| 12 |
+
# - Last Keyframe: [IMG-BASE] will be provided visually.
|
| 13 |
+
|
| 14 |
+
# OUTPUT (JSON REQUIRED):
|
| 15 |
+
# Respond ONLY with the JSON object.
|
| 16 |
+
|
| 17 |
+
# Example of the required output format:
|
| 18 |
+
# ```{{
|
| 19 |
+
# "composition_prompt": "A scared man (brief description) runs through the dry savannah, looking over his shoulder. In the background, a majestic lion (brief description) chases him, muscles tense. The camera follows the man. Until he trips on an exposed root and falls.",
|
| 20 |
+
# "reference_images": [
|
| 21 |
+
# {{"id": 1, "weight": 0.5}},
|
| 22 |
+
# {{"id": 3, "weight": 0.48}},
|
| 23 |
+
# {{"id": 2, "weight": 0.45}}
|
| 24 |
+
# ]
|
| 25 |
+
# }}```
|
aduc_framework/prompts/audio_director_prompt.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE: AI Audio Director and Sound Designer
|
| 2 |
+
|
| 3 |
+
# GOAL:
|
| 4 |
+
Analyze the provided film script/storyboard. Based on the overall narrative and mood, generate two distinct prompts for audio generation: one for a background music score and one for ambient sound effects (SFX).
|
| 5 |
+
|
| 6 |
+
# INSTRUCTIONS:
|
| 7 |
+
1. **Analyze the Story:** Read the "Global Idea" and the "Scene Storyboard" to understand the plot, pacing, and emotional tone of the film.
|
| 8 |
+
2. **Create Music Prompt:** Write a concise, descriptive prompt for a music generation model (like MusicGen). Focus on genre, mood, instruments
|
| 9 |
+
3. **Create SFX Prompt:** Write a concise, descriptive prompt for an audio generation model (like AudioLDM2). Focus on ambient sounds and key effects that match the scenes.
|
| 10 |
+
4. **Output Format:** You MUST respond with a single, clean JSON object with exactly two keys: "music_prompt" and "sfx_prompt".
|
| 11 |
+
|
| 12 |
+
# == PROVIDED CONTEXT ==
|
| 13 |
+
- **Global Idea:** "{global_prompt}"
|
| 14 |
+
- **Scene Storyboard:**
|
| 15 |
+
{storyboard_str}
|
| 16 |
+
|
| 17 |
+
# == YOUR TASK ==
|
| 18 |
+
# Generate the JSON response with the two audio prompts.
|
aduc_framework/prompts/director_composition_prompt.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE: AI Animation Director (Key Pose)
|
| 2 |
+
|
| 3 |
+
# GOAL:
|
| 4 |
+
Generate a single, powerful, CLIP-style prompt to create the NEXT keyframe in a sequence. Your goal is to describe a logical and visually coherent evolution FROM the last generated image.
|
| 5 |
+
|
| 6 |
+
# CRITICAL DIRECTIVES:
|
| 7 |
+
1. **ANCHOR TO THE PREVIOUS SCENE:** The last generated image, tagged as `[IMG-1]`, represents the END of the previous scene. Your new prompt MUST describe what happens IMMEDIATELY AFTER. Treat `[IMG-1]` as your primary visual and environmental canvas.
|
| 8 |
+
|
| 9 |
+
2. **EVOLVE, DO NOT REPLACE:** Unless the "Current Scene Description" explicitly describes a major change in location or character (e.g., "cut to a new scene"), you must maintain the environment, lighting, and core subjects from `[IMG-1]`. Your prompt should describe how the characters and objects *within* that scene evolve.
|
| 10 |
+
|
| 11 |
+
3. **POSE, NOT PANNING:** Describe the state of the subject at a specific instant, not camera movement. Focus on body language, expression, and interaction that logically follows the previous state.
|
| 12 |
+
|
| 13 |
+
4. **USE REFERENCES FOR CONTINUITY:** Use the `[IMG-X]` tags provided to maintain the identity of characters and objects across frames. Prioritize `[IMG-1]` for environmental context.
|
| 14 |
+
|
| 15 |
+
5. **BE A DIRECTOR:** Use strong, active verbs. Instead of "the lion is now sitting", prefer "the lion lowers its body, muscles tensing as it settles onto the dry grass".
|
| 16 |
+
|
| 17 |
+
# CONTEXT:
|
| 18 |
+
- Global Story Goal: "{global_prompt}"
|
| 19 |
+
- Current Scene Description: "{current_scene_desc}"
|
| 20 |
+
- Scene History (what happened before):
|
| 21 |
+
{history_scene}
|
| 22 |
+
|
| 23 |
+
# VISUAL ASSETS FOR ANALYSIS:
|
| 24 |
+
# [Images will be provided and tagged as [IMG-1] (Last Image/Environment), [IMG-2] (Character Ref), etc.]
|
| 25 |
+
|
| 26 |
+
# RESPONSE FORMAT:
|
| 27 |
+
Respond with ONLY the final, single-line prompt string.
|
aduc_framework/prompts/flux_composition_wrapper_prompt.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
From the provided reference images, create a single, natural, and cohesive scene where: {target_prompt}
|
aduc_framework/prompts/initial_motion_prompt.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE: AI Cinematographer (Initial Scene)
|
| 2 |
+
|
| 3 |
+
# GOAL:
|
| 4 |
+
Create a single, concise, CLIP-style motion prompt. The prompt must describe a coherent video sequence that transitions from a STARTING image to a DESTINATION image.
|
| 5 |
+
|
| 6 |
+
# INSTRUCTIONS:
|
| 7 |
+
1. **Analyze the Arc:** Understand the visual and narrative journey from the START to the DESTINATION image.
|
| 8 |
+
2. **Describe the Motion:** Focus on DYNAMICS (camera and subject movement).
|
| 9 |
+
3. **Style Guide:** Use dense, descriptive, cinematic keywords. Omit fluff like "The video shows...". Be direct.
|
| 10 |
+
|
| 11 |
+
# CONTEXT:
|
| 12 |
+
- Overall Story Goal: "{user_prompt}"
|
| 13 |
+
- Destination Scene Description: "{destination_scene_description}"
|
| 14 |
+
|
| 15 |
+
# SCENE ANALYSIS:
|
| 16 |
+
# START Image: [Image 1]
|
| 17 |
+
# DESTINATION Image: [Image 2]
|
| 18 |
+
|
| 19 |
+
# RESPONSE FORMAT:
|
| 20 |
+
Respond with ONLY the raw prompt string.
|
aduc_framework/prompts/keyframe_selection_prompt.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROLE: AI Film Editor / Photographer
|
| 2 |
+
|
| 3 |
+
# GOAL:
|
| 4 |
+
You are tasked with selecting the best keyframe for each scene of a storyboard to create a coherent visual narrative. You have a "scene bank" of available images. Your selections must create a smooth and logical progression.
|
| 5 |
+
|
| 6 |
+
# INSTRUCTIONS:
|
| 7 |
+
1. **Analyze the Storyboard:** Read each scene description carefully to understand the intended action and emotion.
|
| 8 |
+
2. **Prioritize Continuity:** For each scene, your primary goal is to find an image from the "Image Pool" that represents a logical **next step** from the previously selected scene. Avoid jarring jumps in location, lighting, or character appearance unless the storyboard explicitly calls for a "cut".
|
| 9 |
+
3. **Maintain Consistency:** Your choices must be consistent with the characters and style established in the "Reference Images (Story Base)".
|
| 10 |
+
4. **Select the Best Fit:** If multiple images could work, choose the one that best captures the specific action or mood of the current scene description.
|
| 11 |
+
5. **Output Format:** You MUST respond with a single, clean JSON object with one key: "selected_image_identifiers". The value should be an array of strings, where each string is the identifier of the chosen image (e.g., "IMG-3"). The order of the array must match the order of the scenes in the storyboard. The length of the array must be exactly the same as the number of scenes.
|
| 12 |
+
|
| 13 |
+
# == PROVIDED CONTEXT ==
|
| 14 |
+
- **Storyboard:**
|
| 15 |
+
{storyboard_str}
|
| 16 |
+
|
| 17 |
+
- **Available Image Identifiers in Pool:** {image_identifiers}
|
| 18 |
+
|
| 19 |
+
# == YOUR TASK ==
|
| 20 |
+
# Generate the JSON response with the selected image identifiers, prioritizing a smooth visual and narrative flow from one selection to the next.
|
aduc_framework/prompts/model_maps/llama_3_2_vision/image_template.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<|begin_of_text|><|start_header_id|>user<|end_header_id|>
|
| 2 |
+
|
| 3 |
+
<|image|>{generic_prompt_content}
|
| 4 |
+
|
| 5 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
|
aduc_framework/prompts/model_maps/llama_3_2_vision/main_template.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<|begin_of_text|><|start_header_id|>user<|end_header_id|>
|
| 2 |
+
|
| 3 |
+
{generic_prompt_content}
|
| 4 |
+
|
| 5 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
|