Carlexxx commited on
Commit
fb56537
·
1 Parent(s): 783650e

feat: ✨ aBINC 2.2

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +16 -0
  2. Dockerfile +480 -0
  3. LICENSE +25 -0
  4. NOTICE.md +76 -0
  5. README.md +208 -6
  6. aduc_framework/__init__.py +57 -0
  7. aduc_framework/aduc_sdr.py +137 -0
  8. aduc_framework/director.py +98 -0
  9. aduc_framework/engineers/LICENSE +23 -0
  10. aduc_framework/engineers/NOTICE.md +76 -0
  11. aduc_framework/engineers/README.md +211 -0
  12. aduc_framework/engineers/__init__.py +35 -0
  13. aduc_framework/engineers/composer.py +156 -0
  14. aduc_framework/engineers/composer_2D.py +160 -0
  15. aduc_framework/engineers/deformes3D.py +152 -0
  16. aduc_framework/engineers/deformes4D.py +234 -0
  17. aduc_framework/engineers/neura_link.py +128 -0
  18. aduc_framework/engineers/planner_2d.py +91 -0
  19. aduc_framework/engineers/planner_3d.py +103 -0
  20. aduc_framework/engineers/planner_4d.py +176 -0
  21. aduc_framework/engineers/planner_5D.py +143 -0
  22. aduc_framework/engineers/prompt_engine.py +110 -0
  23. aduc_framework/managers/LICENSE +25 -0
  24. aduc_framework/managers/LICENSE.txt +201 -0
  25. aduc_framework/managers/NOTICE.md +60 -0
  26. aduc_framework/managers/README.md +156 -0
  27. aduc_framework/managers/__init__.py +25 -0
  28. aduc_framework/managers/config.yaml +24 -0
  29. aduc_framework/managers/flux_kontext_manager.py +165 -0
  30. aduc_framework/managers/gemini_manager.py +115 -0
  31. aduc_framework/managers/latent_enhancer_manager.py +109 -0
  32. aduc_framework/managers/llama_multimodal_manager.py +153 -0
  33. aduc_framework/managers/ltx_manager.py +360 -0
  34. aduc_framework/managers/ltx_pipeline_utils.py +774 -0
  35. aduc_framework/managers/mmaudio_manager.py +226 -0
  36. aduc_framework/managers/seedvr_manager.py +232 -0
  37. aduc_framework/managers/upscaler_specialist.py +91 -0
  38. aduc_framework/managers/vae_manager.py +98 -0
  39. aduc_framework/prompts/LICENSE +25 -0
  40. aduc_framework/prompts/NOTICE.md +76 -0
  41. aduc_framework/prompts/README.md +211 -0
  42. aduc_framework/prompts/TASK_01_CREATE_NARRATIVE_SYNTHESIS.txt +18 -0
  43. aduc_framework/prompts/anticipatory_keyframe_prompt.txt +25 -0
  44. aduc_framework/prompts/audio_director_prompt.txt +18 -0
  45. aduc_framework/prompts/director_composition_prompt.txt +27 -0
  46. aduc_framework/prompts/flux_composition_wrapper_prompt.txt +1 -0
  47. aduc_framework/prompts/initial_motion_prompt.txt +20 -0
  48. aduc_framework/prompts/keyframe_selection_prompt.txt +20 -0
  49. aduc_framework/prompts/model_maps/llama_3_2_vision/image_template.txt +5 -0
  50. 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: Aduc-sdr-2 5
3
- emoji: 💻
4
- colorFrom: yellow
5
  colorTo: purple
6
- sdk: docker
7
- pinned: false
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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|>