carlex3321 commited on
Commit
0834a4b
·
verified ·
1 Parent(s): b11811b

Upload 6 files

Browse files
Files changed (4) hide show
  1. Dockerfile +12 -0
  2. app_vince.py +110 -40
  3. builder.sh +74 -54
  4. start.sh +63 -31
Dockerfile CHANGED
@@ -130,6 +130,18 @@ RUN useradd -m -u 1000 -s /bin/bash appuser && \
130
 
131
  USER appuser
132
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  RUN df -h
135
  # Mostra uso de disco por volume (procure /app ou o filesystem raiz).
 
130
 
131
  USER appuser
132
 
133
+ # Declara volume persistente para HF Spaces
134
+ VOLUME /data
135
+
136
+ # Env vars para caches em /data
137
+ ENV HF_HOME=/data/.cache/huggingface
138
+ ENV TORCH_HOME=/data/.cache/torch
139
+ ENV HF_DATASETS_CACHE=/data/.cache/datasets
140
+ ENV TRANSFORMERS_CACHE=/data/.cache/transformers
141
+ ENV DIFFUSERS_CACHE=/data/.cache/diffusers
142
+
143
+
144
+
145
 
146
  RUN df -h
147
  # Mostra uso de disco por volume (procure /app ou o filesystem raiz).
app_vince.py CHANGED
@@ -3,11 +3,24 @@ import os
3
  from pathlib import Path
4
  from typing import List, Tuple, Optional
5
  import gradio as gr
 
 
6
  from services.vincie import VincieService
7
 
 
8
  svc = VincieService()
 
 
9
 
10
- DEFAULT_NEGATIVE_PROMPT = "Worst quality, Normal quality, Low quality, Low res, Blurry, Jpeg artifacts, Grainy, text, logo, watermark, banner, extra digits, signature, subtitling, Bad anatomy, Bad proportions, Deformed, Disconnected limbs, Disfigured, Extra arms, Extra limbs, Extra hands, Fused fingers, Gross proportions, Long neck, Malformed limbs, Mutated, Mutated hands, Mutated limbs, Missing arms, Missing fingers, Poorly drawn hands, Poorly drawn face, Nsfw, Uncensored, Cleavage, Nude, Nipples, Overexposed, Plain background, Grainy, Underexposed, Deformed structures"
 
 
 
 
 
 
 
 
11
 
12
  def setup_auto() -> str:
13
  try:
@@ -21,9 +34,18 @@ def setup_auto() -> str:
21
 
22
  def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
23
  img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
24
- images = sorted([p for pat in img_globs for p in out_dir.rglob(pat)], key=lambda p: p.stat().st_mtime)
 
 
 
 
 
 
25
  image_paths = [str(p) for p in images[-max_images:]]
26
- videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
 
 
 
27
  video_path = str(videos[-1]) if videos else None
28
  return image_paths, video_path
29
 
@@ -34,7 +56,18 @@ def ui_multi_turn(input_image, turns_text, negative_prompt, seed, steps, cfg_sca
34
  return [], None, "Por favor, forneça as instruções de edição (uma por linha)."
35
  turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
36
  try:
37
- out_dir = svc.multi_turn_edit(input_image, turns, negative_prompt=negative_prompt, seed=int(seed), steps=int(steps), cfg_scale=float(cfg_scale), resolution=int(resolution), use_vae_slicing=use_vae_slicing, num_gpus=int(num_gpus), batch_size=int(batch_size))
 
 
 
 
 
 
 
 
 
 
 
38
  imgs, vid = _list_media(Path(out_dir))
39
  return imgs, vid, f"Saídas salvas em: {out_dir}"
40
  except Exception as e:
@@ -48,7 +81,19 @@ def ui_text_to_video(input_image, prompt, negative_prompt, seed, steps, cfg_scal
48
  if not prompt or not prompt.strip():
49
  return None, "Por favor, forneça um prompt para o vídeo."
50
  try:
51
- out_dir = svc.text_to_video(input_image, prompt, negative_prompt=negative_prompt, seed=int(seed), steps=int(steps), cfg_scale=float(cfg_scale), resolution=int(resolution), fps=int(fps), use_vae_slicing=use_vae_slicing, num_gpus=int(num_gpus), batch_size=int(batch_size))
 
 
 
 
 
 
 
 
 
 
 
 
52
  _, vid = _list_media(Path(out_dir))
53
  return vid, f"Vídeo salvo em: {out_dir}"
54
  except Exception as e:
@@ -57,11 +102,15 @@ def ui_text_to_video(input_image, prompt, negative_prompt, seed, steps, cfg_scal
57
  return None, f"Erro na geração: {e}"
58
 
59
  def ui_multi_concept(files, descs_text, final_prompt):
60
- if not files: return [], None, "Por favor, faça o upload das imagens de conceito."
61
- if not descs_text: return [], None, "Por favor, forneça as descrições (uma por linha)."
62
- if not final_prompt: return [], None, "Por favor, forneça um prompt final."
 
 
 
63
  descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
64
- if len(descs) != len(files): return [], None, f"O número de descrições ({len(descs)}) deve ser igual ao de imagens ({len(files)})."
 
65
  try:
66
  out_dir = svc.multi_concept_compose(files, descs, final_prompt)
67
  imgs, vid = _list_media(Path(out_dir))
@@ -73,62 +122,83 @@ def ui_multi_concept(files, descs_text, final_prompt):
73
 
74
  with gr.Blocks(title="VINCIE Service", theme=gr.themes.Soft()) as demo:
75
  gr.Markdown("# VINCIE Service — Geração Distribuída com Controles Avançados")
76
- gr.Markdown("- **Interface por:** Carlex ([email protected] | GitHub: carlex22)")
 
77
  with gr.Row():
78
  setup_out = gr.Textbox(label="Status da Configuração", interactive=False)
 
79
  with gr.Tab("Edição Multi-Turno"):
80
  with gr.Row():
81
  img_mt = gr.Image(type="filepath", label="Imagem de Entrada")
82
  with gr.Column():
83
- turns_mt = gr.Textbox(lines=5, label="Instruções de Edição (uma por linha)", placeholder="Ex: adicione um chapéu azul\nagora, mude o fundo para uma praia")
84
- with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
85
- with gr.Row():
86
- num_gpus_mt = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
87
- batch_size_mt = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
88
- resolution_mt = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512, info="Maior resolução exige mais VRAM e tempo.")
89
- use_vae_slicing_mt = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
90
- neg_prompt_mt = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
91
- seed_mt = gr.Number(label="Seed (Semente)", value=1, precision=0)
92
- steps_mt = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50, info="Menos passos = mais rápido.")
93
- cfg_mt = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5, info="Quão forte o modelo segue o prompt.")
94
- run_mt = gr.Button("Executar Edição Multi-Turno", variant="primary")
 
 
 
 
95
  gallery_mt = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
96
  video_mt = gr.Video(label="Vídeo da Sequência (se disponível)")
97
  status_mt = gr.Textbox(label="Status da Saída", interactive=False)
98
- run_mt.click(ui_multi_turn, inputs=[img_mt, turns_mt, neg_prompt_mt, seed_mt, steps_mt, cfg_mt, resolution_mt, use_vae_slicing_mt, num_gpus_mt, batch_size_mt], outputs=[gallery_mt, video_mt, status_mt])
 
 
 
 
 
99
  with gr.Tab("Texto-para-Vídeo"):
100
  with gr.Row():
101
  img_vid = gr.Image(type="filepath", label="Frame Inicial")
102
  with gr.Column():
103
  prompt_vid = gr.Textbox(lines=2, label="Prompt do Vídeo", placeholder="Ex: um gato andando pela sala")
104
- with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
105
- with gr.Row():
106
- num_gpus_vid = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
107
- batch_size_vid = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
108
- resolution_vid = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512)
109
- fps_vid = gr.Slider(label="Frames por Segundo (FPS)", minimum=1, maximum=24, step=1, value=2)
110
- use_vae_slicing_vid = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
111
- neg_prompt_vid = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
112
- seed_vid = gr.Number(label="Seed (Semente)", value=1, precision=0)
113
- steps_vid = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50)
114
- cfg_vid = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5)
115
- run_vid = gr.Button("Gerar Vídeo", variant="primary")
116
  video_vid = gr.Video(label="Vídeo Gerado")
117
  status_vid = gr.Textbox(label="Status da Saída", interactive=False)
118
- run_vid.click(ui_text_to_video, inputs=[img_vid, prompt_vid, neg_prompt_vid, seed_vid, steps_vid, cfg_vid, resolution_vid, fps_vid, use_vae_slicing_vid, num_gpus_vid, batch_size_vid], outputs=[video_vid, status_vid])
 
 
 
 
 
119
  with gr.Tab("Composição Multi-Conceito"):
120
  gr.Markdown("Nota: A composição multi-conceito está atualmente configurada para rodar em uma única GPU para garantir estabilidade.")
121
  with gr.Row():
122
  with gr.Column(scale=1):
123
  files_mc = gr.File(file_count="multiple", type="filepath", label="1. Imagens de Conceito")
124
  with gr.Column(scale=2):
125
- descs_mc = gr.Textbox(lines=5, label="2. Descrições (uma por linha, na mesma ordem)", placeholder="Ex: : uma foto de um pai\n: uma foto de uma mãe...")
126
- final_prompt_mc = gr.Textbox(lines=3, label="3. Prompt Final de Composição", placeholder="Ex: Baseado em , ..., uma família sorrindo em um retrato...")
127
- run_mc = gr.Button("Executar Composição", variant="primary")
128
  gallery_mc = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
129
  video_mc = gr.Video(label="Vídeo da Sequência (se disponível)")
130
  status_mc = gr.Textbox(label="Status da Saída", interactive=False)
131
- run_mc.click(ui_multi_concept, inputs=[files_mc, descs_mc, final_prompt_mc], outputs=[gallery_mc, video_mc, status_mc])
 
 
 
 
 
132
  demo.load(fn=setup_auto, outputs=setup_out)
133
 
134
  if __name__ == "__main__":
 
3
  from pathlib import Path
4
  from typing import List, Tuple, Optional
5
  import gradio as gr
6
+
7
+ # Importa o serviço
8
  from services.vincie import VincieService
9
 
10
+ # Instancia e normaliza caminhos (defensivo contra imports alternativos)
11
  svc = VincieService()
12
+ svc.repo_dir = Path(svc.repo_dir)
13
+ svc.ckpt_dir = Path(svc.ckpt_dir)
14
 
15
+ DEFAULT_NEGATIVE_PROMPT = (
16
+ "Worst quality, Normal quality, Low quality, Low res, Blurry, Jpeg artifacts, Grainy, "
17
+ "text, logo, watermark, banner, extra digits, signature, subtitling, Bad anatomy, "
18
+ "Bad proportions, Deformed, Disconnected limbs, Disfigured, Extra arms, Extra limbs, "
19
+ "Extra hands, Fused fingers, Gross proportions, Long neck, Malformed limbs, Mutated, "
20
+ "Mutated hands, Mutated limbs, Missing arms, Missing fingers, Poorly drawn hands, "
21
+ "Poorly drawn face, Nsfw, Uncensored, Cleavage, Nude, Nipples, Overexposed, "
22
+ "Plain background, Grainy, Underexposed, Deformed structures"
23
+ )
24
 
25
  def setup_auto() -> str:
26
  try:
 
34
 
35
  def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
36
  img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
37
+ try:
38
+ images = sorted(
39
+ [p for pat in img_globs for p in out_dir.rglob(pat)],
40
+ key=lambda p: p.stat().st_mtime
41
+ )
42
+ except FileNotFoundError:
43
+ images = []
44
  image_paths = [str(p) for p in images[-max_images:]]
45
+ try:
46
+ videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
47
+ except FileNotFoundError:
48
+ videos = []
49
  video_path = str(videos[-1]) if videos else None
50
  return image_paths, video_path
51
 
 
56
  return [], None, "Por favor, forneça as instruções de edição (uma por linha)."
57
  turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
58
  try:
59
+ out_dir = svc.multi_turn_edit(
60
+ input_image,
61
+ turns,
62
+ negative_prompt=negative_prompt,
63
+ seed=int(seed),
64
+ steps=int(steps),
65
+ cfg_scale=float(cfg_scale),
66
+ resolution=int(resolution),
67
+ use_vae_slicing=use_vae_slicing,
68
+ num_gpus=int(num_gpus),
69
+ batch_size=int(batch_size),
70
+ )
71
  imgs, vid = _list_media(Path(out_dir))
72
  return imgs, vid, f"Saídas salvas em: {out_dir}"
73
  except Exception as e:
 
81
  if not prompt or not prompt.strip():
82
  return None, "Por favor, forneça um prompt para o vídeo."
83
  try:
84
+ out_dir = svc.text_to_video(
85
+ input_image,
86
+ prompt,
87
+ negative_prompt=negative_prompt,
88
+ seed=int(seed),
89
+ steps=int(steps),
90
+ cfg_scale=float(cfg_scale),
91
+ resolution=int(resolution),
92
+ fps=int(fps),
93
+ use_vae_slicing=use_vae_slicing,
94
+ num_gpus=int(num_gpus),
95
+ batch_size=int(batch_size),
96
+ )
97
  _, vid = _list_media(Path(out_dir))
98
  return vid, f"Vídeo salvo em: {out_dir}"
99
  except Exception as e:
 
102
  return None, f"Erro na geração: {e}"
103
 
104
  def ui_multi_concept(files, descs_text, final_prompt):
105
+ if not files:
106
+ return [], None, "Por favor, faça o upload das imagens de conceito."
107
+ if not descs_text:
108
+ return [], None, "Por favor, forneça as descrições (uma por linha)."
109
+ if not final_prompt:
110
+ return [], None, "Por favor, forneça um prompt final."
111
  descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
112
+ if len(descs) != len(files):
113
+ return [], None, f"O número de descrições ({len(descs)}) deve ser igual ao de imagens ({len(files)})."
114
  try:
115
  out_dir = svc.multi_concept_compose(files, descs, final_prompt)
116
  imgs, vid = _list_media(Path(out_dir))
 
122
 
123
  with gr.Blocks(title="VINCIE Service", theme=gr.themes.Soft()) as demo:
124
  gr.Markdown("# VINCIE Service — Geração Distribuída com Controles Avançados")
125
+ gr.Markdown("- Interface por: Carlex ([email protected] | GitHub: carlex22)")
126
+
127
  with gr.Row():
128
  setup_out = gr.Textbox(label="Status da Configuração", interactive=False)
129
+
130
  with gr.Tab("Edição Multi-Turno"):
131
  with gr.Row():
132
  img_mt = gr.Image(type="filepath", label="Imagem de Entrada")
133
  with gr.Column():
134
+ turns_mt = gr.Textbox(
135
+ lines=5,
136
+ label="Instruções de Edição (uma por linha)",
137
+ placeholder="Ex: adicione um chapéu azul\nagora, mude o fundo para uma praia"
138
+ )
139
+ with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
140
+ with gr.Row():
141
+ num_gpus_mt = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
142
+ batch_size_mt = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
143
+ resolution_mt = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512, info="Maior resolução exige mais VRAM e tempo.")
144
+ use_vae_slicing_mt = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
145
+ neg_prompt_mt = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
146
+ seed_mt = gr.Number(label="Seed (Semente)", value=1, precision=0)
147
+ steps_mt = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50, info="Menos passos = mais rápido.")
148
+ cfg_mt = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5, info="Quão forte o modelo segue o prompt.")
149
+ run_mt = gr.Button("Executar Edição Multi-Turno", variant="primary")
150
  gallery_mt = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
151
  video_mt = gr.Video(label="Vídeo da Sequência (se disponível)")
152
  status_mt = gr.Textbox(label="Status da Saída", interactive=False)
153
+ run_mt.click(
154
+ ui_multi_turn,
155
+ inputs=[img_mt, turns_mt, neg_prompt_mt, seed_mt, steps_mt, cfg_mt, resolution_mt, use_vae_slicing_mt, num_gpus_mt, batch_size_mt],
156
+ outputs=[gallery_mt, video_mt, status_mt]
157
+ )
158
+
159
  with gr.Tab("Texto-para-Vídeo"):
160
  with gr.Row():
161
  img_vid = gr.Image(type="filepath", label="Frame Inicial")
162
  with gr.Column():
163
  prompt_vid = gr.Textbox(lines=2, label="Prompt do Vídeo", placeholder="Ex: um gato andando pela sala")
164
+ with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
165
+ with gr.Row():
166
+ num_gpus_vid = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
167
+ batch_size_vid = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
168
+ resolution_vid = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512)
169
+ fps_vid = gr.Slider(label="Frames por Segundo (FPS)", minimum=1, maximum=24, step=1, value=2)
170
+ use_vae_slicing_vid = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
171
+ neg_prompt_vid = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
172
+ seed_vid = gr.Number(label="Seed (Semente)", value=1, precision=0)
173
+ steps_vid = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50)
174
+ cfg_vid = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5)
175
+ run_vid = gr.Button("Gerar Vídeo", variant="primary")
176
  video_vid = gr.Video(label="Vídeo Gerado")
177
  status_vid = gr.Textbox(label="Status da Saída", interactive=False)
178
+ run_vid.click(
179
+ ui_text_to_video,
180
+ inputs=[img_vid, prompt_vid, neg_prompt_vid, seed_vid, steps_vid, cfg_vid, resolution_vid, fps_vid, use_vae_slicing_vid, num_gpus_vid, batch_size_vid],
181
+ outputs=[video_vid, status_vid]
182
+ )
183
+
184
  with gr.Tab("Composição Multi-Conceito"):
185
  gr.Markdown("Nota: A composição multi-conceito está atualmente configurada para rodar em uma única GPU para garantir estabilidade.")
186
  with gr.Row():
187
  with gr.Column(scale=1):
188
  files_mc = gr.File(file_count="multiple", type="filepath", label="1. Imagens de Conceito")
189
  with gr.Column(scale=2):
190
+ descs_mc = gr.Textbox(lines=5, label="2. Descrições (uma por linha, na mesma ordem)", placeholder="Ex: <ref1>: uma foto de um pai\n<ref2>: uma foto de uma mãe...")
191
+ final_prompt_mc = gr.Textbox(lines=3, label="3. Prompt Final de Composição", placeholder="Ex: Baseado em <ref1>, <ref2>, ..., uma família sorrindo em um retrato...")
192
+ run_mc = gr.Button("Executar Composição", variant="primary")
193
  gallery_mc = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
194
  video_mc = gr.Video(label="Vídeo da Sequência (se disponível)")
195
  status_mc = gr.Textbox(label="Status da Saída", interactive=False)
196
+ run_mc.click(
197
+ ui_multi_concept,
198
+ inputs=[files_mc, descs_mc, final_prompt_mc],
199
+ outputs=[gallery_mc, video_mc, status_mc]
200
+ )
201
+
202
  demo.load(fn=setup_auto, outputs=setup_out)
203
 
204
  if __name__ == "__main__":
builder.sh CHANGED
@@ -1,45 +1,62 @@
1
  #!/usr/bin/env bash
2
  set -euo pipefail
3
 
4
- echo "🚀 Builder (Apex + Q8) — roda em runtime com GPU visível"
5
-
6
- # ===== Config e diretórios =====
7
- export SELF_HF_REPO_ID="${SELF_HF_REPO_ID:-carlex3321/aduc-sdr}" # Model repo no HF com wheels
8
- export HF_HOME="${HF_HOME:-/app/model_cache}"
 
 
 
 
 
 
9
  export HF_HUB_CACHE="${HF_HUB_CACHE:-$HF_HOME/hub}"
10
- export TORCH_HOME="${TORCH_HOME:-$HF_HOME/torch}"
 
 
 
 
 
 
 
 
 
11
  export HF_HUB_ENABLE_HF_TRANSFER="${HF_HUB_ENABLE_HF_TRANSFER:-1}"
12
- export PATH="$HOME/.local/bin:$PATH"
13
 
14
- mkdir -p /app/wheels /app/cuda_cache "$HF_HOME" "$TORCH_HOME" /app/wheels/src
 
15
  chmod -R 777 /app/wheels || true
16
  export CUDA_CACHE_PATH="/app/cuda_cache"
17
 
 
18
  if [ -f "/NGC-DL-CONTAINER-LICENSE" ]; then
19
  cp -f /NGC-DL-CONTAINER-LICENSE /app/wheels/NGC-DL-CONTAINER-LICENSE || true
20
  fi
21
 
22
- # ===== Dependências mínimas =====
23
  python -m pip install -v -U pip build setuptools wheel hatchling hatch-vcs scikit-build-core cmake ninja packaging "huggingface_hub[hf_transfer]" || true
24
 
25
  # ===== Tags de ambiente (Python/CUDA/Torch) =====
26
  PY_TAG="$(python -c 'import sys; print(f"cp{sys.version_info[0]}{sys.version_info[1]}")' 2>/dev/null || echo cp310)"
27
  TORCH_VER="$(python - <<'PY'
28
  try:
29
- import torch, re
30
- v = torch.__version__
31
- print(re.sub(r'\+.*$', '', v))
32
  except Exception:
33
- print("unknown")
34
  PY
35
  )"
36
  CU_TAG="$(python - <<'PY'
37
  try:
38
- import torch
39
- cu = getattr(torch.version, "cuda", None)
40
- print("cu"+cu.replace(".","")) if cu else print("")
41
  except Exception:
42
- print("")
43
  PY
44
  )"
45
  echo "[env] PY_TAG=${PY_TAG} TORCH_VER=${TORCH_VER} CU_TAG=${CU_TAG}"
@@ -48,11 +65,11 @@ echo "[env] PY_TAG=${PY_TAG} TORCH_VER=${TORCH_VER} CU_TAG=${CU_TAG}"
48
  check_apex() {
49
  python - <<'PY'
50
  try:
51
- from apex.normalization import FusedLayerNorm, FusedRMSNorm
52
- import importlib; importlib.import_module("fused_layer_norm_cuda")
53
- ok = True
54
  except Exception:
55
- ok = False
56
  raise SystemExit(0 if ok else 1)
57
  PY
58
  }
@@ -65,10 +82,10 @@ raise SystemExit(0 if spec else 1)
65
  PY
66
  }
67
 
68
- # ===== Download do Hub =====
69
  install_from_hf () {
70
- local PKG="$1" # 'apex' ou 'q8_kernels'
71
- echo "[hub] Verificando wheel de ${PKG} no repositório ${SELF_HF_REPO_ID}"
72
  python - "$PKG" "$PY_TAG" "$CU_TAG" <<'PY' || exit 0
73
  import os, sys
74
  from huggingface_hub import HfApi, hf_hub_download, HfFolder
@@ -76,16 +93,16 @@ from huggingface_hub import HfApi, hf_hub_download, HfFolder
76
  pkg, py_tag, cu_tag = sys.argv[1], sys.argv[2], sys.argv[3]
77
  repo = os.environ.get("SELF_HF_REPO_ID","carlex3321/aduc-sdr")
78
  api = HfApi(token=os.getenv("HF_TOKEN") or HfFolder.get_token())
 
79
  try:
80
- files = api.list_repo_files(repo_id=repo, repo_type="model")
81
  except Exception:
82
- raise SystemExit(0)
83
 
84
  cands = [f for f in files if f.endswith(".whl") and f.rsplit("/",1)[-1].startswith(pkg+"-") and py_tag in f]
85
- pref = [f for f in cands if cu_tag and cu_tag in f] or cands
86
  if not pref:
87
- raise SystemExit(0)
88
-
89
  target = sorted(pref, reverse=True)[0]
90
  print(target)
91
  path = hf_hub_download(repo_id=repo, filename=target, repo_type="model", local_dir="/app/wheels")
@@ -96,7 +113,7 @@ PY
96
  # ===== Builders =====
97
  build_apex () {
98
  local SRC="/app/wheels/src/apex"
99
- echo "[build] Preparando fonte Apex em ${SRC}"
100
  if [ -d "$SRC/.git" ]; then
101
  git -C "$SRC" fetch --all -p || true
102
  git -C "$SRC" reset --hard HEAD || true
@@ -105,16 +122,18 @@ build_apex () {
105
  rm -rf "$SRC"
106
  git clone --depth 1 https://github.com/NVIDIA/apex "$SRC"
107
  fi
 
108
  echo "[build] Compilando Apex -> wheel"
109
  export APEX_CPP_EXT=1 APEX_CUDA_EXT=1 APEX_ALL_CONTRIB_EXT=0
110
- python -m pip wheel --no-build-isolation --no-deps "$SRC" -w /app/wheels || true
 
111
  local W="$(ls -t /app/wheels/apex-*.whl 2>/dev/null | head -n1 || true)"
112
  if [ -n "${W}" ]; then
113
- python -m pip install -U --no-deps "${W}" || true
114
- echo "[build] Apex instalado da wheel recém-compilada: ${W}"
115
  else
116
- echo "[build] Nenhuma wheel Apex gerada; instalando do source (pode falhar)"
117
- python -m pip install --no-build-isolation "$SRC" || true
118
  fi
119
  }
120
 
@@ -126,25 +145,27 @@ build_q8 () {
126
  git clone --filter=blob:none "$Q8_REPO" "$SRC"
127
  git -C "$SRC" checkout "$Q8_COMMIT"
128
  git -C "$SRC" submodule update --init --recursive
 
129
  echo "[build] Compilando Q8 Kernels -> wheel"
130
- python -m pip wheel --no-build-isolation "$SRC" -w /app/wheels || true
 
131
  local W="$(ls -t /app/wheels/q8_kernels-*.whl 2>/dev/null | head -n1 || true)"
132
  if [ -n "${W}" ]; then
133
- python -m pip install -U --no-deps "${W}" || true
134
- echo "[build] Q8 instalado da wheel recém-compilada: ${W}"
135
  else
136
- echo "[build] Nenhuma wheel q8_kernels gerada; instalando do source (pode falhar)"
137
- python -m pip install --no-build-isolation "$SRC" || true
138
  fi
139
  }
140
 
141
  # ===== Pipeline genérico =====
142
  ensure_pkg () {
143
- local PKG="$1" # apex | q8_kernels
144
- local CHECK_FN="$2" # check_apex | check_q8
145
- local BUILD_FN="$3" # build_apex | build_q8
146
-
147
  echo "[flow] === ${PKG} ==="
 
148
  if ${CHECK_FN}; then
149
  echo "[flow] ${PKG}: já instalado (import OK)"
150
  return 0
@@ -155,7 +176,7 @@ ensure_pkg () {
155
  if [ -n "${HF_OUT:-}" ]; then
156
  WHEEL_PATH="$(printf "%s\n" "${HF_OUT}" | tail -n1)"
157
  echo "[hub] Baixado: ${WHEEL_PATH}"
158
- python -m pip install -U --no-build-isolation "${WHEEL_PATH}" || true
159
  if ${CHECK_FN}; then
160
  echo "[flow] ${PKG}: sucesso via Hub (${WHEEL_PATH})"
161
  return 0
@@ -173,7 +194,7 @@ ensure_pkg () {
173
  return 0
174
  fi
175
 
176
- echo "[flow] ${PKG}: falhou após build; registrando logs e seguindo"
177
  return 1
178
  }
179
 
@@ -181,25 +202,24 @@ ensure_pkg () {
181
  ensure_pkg "apex" check_apex build_apex || true
182
  ensure_pkg "q8_kernels" check_q8 build_q8 || true
183
 
184
-
185
  python - <<'PY'
186
  import os
187
  from huggingface_hub import HfApi, HfFolder
188
  repo=os.environ.get("SELF_HF_REPO_ID","carlex3321/aduc-sdr")
189
  token=os.getenv("HF_TOKEN") or HfFolder.get_token()
190
  if not token:
191
- raise SystemExit("HF_TOKEN ausente; upload desabilitado")
192
  api=HfApi(token=token)
193
  api.upload_folder(
194
- folder_path="/app/wheels",
195
- repo_id=repo,
196
- repo_type="model",
197
- allow_patterns=["*.whl","NGC-DL-CONTAINER-LICENSE"],
198
- ignore_patterns=["**/src/**","**/*.log","**/logs/**",".git/**"],
199
  )
200
  print("Upload concluído (wheels + licença).")
201
  PY
202
 
203
-
204
  chmod -R 777 /app/wheels || true
205
  echo "✅ Builder finalizado."
 
1
  #!/usr/bin/env bash
2
  set -euo pipefail
3
 
4
+ echo "🚀 Builder (Apex + Q8) — runtime, GPU visível, cache persistente"
5
+
6
+ # ===== Persistência e caches =====
7
+ # Prioriza /data (HF Spaces) e mantém compatibilidade com /app
8
+ if [ -d /data ]; then
9
+ export HF_HOME="${HF_HOME:-/data/.cache/huggingface}"
10
+ export TORCH_HOME="${TORCH_HOME:-/data/.cache/torch}"
11
+ else
12
+ export HF_HOME="${HF_HOME:-/app/.cache/huggingface}"
13
+ export TORCH_HOME="${TORCH_HOME:-/app/.cache/torch}"
14
+ fi
15
  export HF_HUB_CACHE="${HF_HUB_CACHE:-$HF_HOME/hub}"
16
+ mkdir -p "$HF_HOME" "$HF_HUB_CACHE" "$TORCH_HOME"
17
+
18
+ # Symlink de compatibilidade (se scripts esperarem /app/.cache/huggingface)
19
+ mkdir -p /app/.cache
20
+ ln -sf "$HF_HOME" /app/.cache/huggingface
21
+
22
+ # ===== Repositório de wheels no Hub =====
23
+ export SELF_HF_REPO_ID="${SELF_HF_REPO_ID:-carlex3321/aduc-sdr}"
24
+
25
+ # ===== Aceleração de transferência =====
26
  export HF_HUB_ENABLE_HF_TRANSFER="${HF_HUB_ENABLE_HF_TRANSFER:-1}"
27
+ export HF_HUB_DOWNLOAD_TIMEOUT="${HF_HUB_DOWNLOAD_TIMEOUT:-60}"
28
 
29
+ # ===== Diretórios de trabalho =====
30
+ mkdir -p /app/wheels /app/cuda_cache /app/wheels/src
31
  chmod -R 777 /app/wheels || true
32
  export CUDA_CACHE_PATH="/app/cuda_cache"
33
 
34
+ # Licença (NVIDIA NGC) se presente
35
  if [ -f "/NGC-DL-CONTAINER-LICENSE" ]; then
36
  cp -f /NGC-DL-CONTAINER-LICENSE /app/wheels/NGC-DL-CONTAINER-LICENSE || true
37
  fi
38
 
39
+ # ===== Dependências mínimas de build =====
40
  python -m pip install -v -U pip build setuptools wheel hatchling hatch-vcs scikit-build-core cmake ninja packaging "huggingface_hub[hf_transfer]" || true
41
 
42
  # ===== Tags de ambiente (Python/CUDA/Torch) =====
43
  PY_TAG="$(python -c 'import sys; print(f"cp{sys.version_info[0]}{sys.version_info[1]}")' 2>/dev/null || echo cp310)"
44
  TORCH_VER="$(python - <<'PY'
45
  try:
46
+ import torch, re
47
+ v = torch.__version__
48
+ print(re.sub(r'\+.*$', '', v))
49
  except Exception:
50
+ print("unknown")
51
  PY
52
  )"
53
  CU_TAG="$(python - <<'PY'
54
  try:
55
+ import torch
56
+ cu = getattr(torch.version, "cuda", None)
57
+ print("cu"+cu.replace(".","")) if cu else print("")
58
  except Exception:
59
+ print("")
60
  PY
61
  )"
62
  echo "[env] PY_TAG=${PY_TAG} TORCH_VER=${TORCH_VER} CU_TAG=${CU_TAG}"
 
65
  check_apex() {
66
  python - <<'PY'
67
  try:
68
+ from apex.normalization import FusedLayerNorm, FusedRMSNorm
69
+ import importlib; importlib.import_module("fused_layer_norm_cuda")
70
+ ok = True
71
  except Exception:
72
+ ok = False
73
  raise SystemExit(0 if ok else 1)
74
  PY
75
  }
 
82
  PY
83
  }
84
 
85
+ # ===== Download de wheels do Hub =====
86
  install_from_hf () {
87
+ local PKG="$1" # 'apex' ou 'q8_kernels'
88
+ echo "[hub] Buscando wheel de ${PKG} em ${SELF_HF_REPO_ID} (py=${PY_TAG}, cu=${CU_TAG})"
89
  python - "$PKG" "$PY_TAG" "$CU_TAG" <<'PY' || exit 0
90
  import os, sys
91
  from huggingface_hub import HfApi, hf_hub_download, HfFolder
 
93
  pkg, py_tag, cu_tag = sys.argv[1], sys.argv[2], sys.argv[3]
94
  repo = os.environ.get("SELF_HF_REPO_ID","carlex3321/aduc-sdr")
95
  api = HfApi(token=os.getenv("HF_TOKEN") or HfFolder.get_token())
96
+
97
  try:
98
+ files = api.list_repo_files(repo_id=repo, repo_type="model")
99
  except Exception:
100
+ raise SystemExit(0)
101
 
102
  cands = [f for f in files if f.endswith(".whl") and f.rsplit("/",1)[-1].startswith(pkg+"-") and py_tag in f]
103
+ pref = [f for f in cands if cu_tag and cu_tag in f] or cands
104
  if not pref:
105
+ raise SystemExit(0)
 
106
  target = sorted(pref, reverse=True)[0]
107
  print(target)
108
  path = hf_hub_download(repo_id=repo, filename=target, repo_type="model", local_dir="/app/wheels")
 
113
  # ===== Builders =====
114
  build_apex () {
115
  local SRC="/app/wheels/src/apex"
116
+ echo "[build] Fonte Apex em ${SRC}"
117
  if [ -d "$SRC/.git" ]; then
118
  git -C "$SRC" fetch --all -p || true
119
  git -C "$SRC" reset --hard HEAD || true
 
122
  rm -rf "$SRC"
123
  git clone --depth 1 https://github.com/NVIDIA/apex "$SRC"
124
  fi
125
+
126
  echo "[build] Compilando Apex -> wheel"
127
  export APEX_CPP_EXT=1 APEX_CUDA_EXT=1 APEX_ALL_CONTRIB_EXT=0
128
+ python -m pip wheel -v --no-build-isolation --no-deps "$SRC" -w /app/wheels || true
129
+
130
  local W="$(ls -t /app/wheels/apex-*.whl 2>/dev/null | head -n1 || true)"
131
  if [ -n "${W}" ]; then
132
+ python -m pip install -v -U --no-deps "${W}" || true
133
+ echo "[build] Apex instalado da wheel: ${W}"
134
  else
135
+ echo "[build] Nenhuma wheel Apex gerada; instalando do source"
136
+ python -m pip install -v --no-build-isolation "$SRC" || true
137
  fi
138
  }
139
 
 
145
  git clone --filter=blob:none "$Q8_REPO" "$SRC"
146
  git -C "$SRC" checkout "$Q8_COMMIT"
147
  git -C "$SRC" submodule update --init --recursive
148
+
149
  echo "[build] Compilando Q8 Kernels -> wheel"
150
+ python -m pip wheel -v --no-build-isolation "$SRC" -w /app/wheels || true
151
+
152
  local W="$(ls -t /app/wheels/q8_kernels-*.whl 2>/dev/null | head -n1 || true)"
153
  if [ -n "${W}" ]; then
154
+ python -m pip install -v -U --no-deps "${W}" || true
155
+ echo "[build] Q8 instalado da wheel: ${W}"
156
  else
157
+ echo "[build] Nenhuma wheel q8_kernels gerada; instalando do source"
158
+ python -m pip install -v --no-build-isolation "$SRC" || true
159
  fi
160
  }
161
 
162
  # ===== Pipeline genérico =====
163
  ensure_pkg () {
164
+ local PKG="$1" # apex | q8_kernels
165
+ local CHECK_FN="$2" # check_apex | check_q8
166
+ local BUILD_FN="$3" # build_apex | build_q8
 
167
  echo "[flow] === ${PKG} ==="
168
+
169
  if ${CHECK_FN}; then
170
  echo "[flow] ${PKG}: já instalado (import OK)"
171
  return 0
 
176
  if [ -n "${HF_OUT:-}" ]; then
177
  WHEEL_PATH="$(printf "%s\n" "${HF_OUT}" | tail -n1)"
178
  echo "[hub] Baixado: ${WHEEL_PATH}"
179
+ python -m pip install -v -U --no-build-isolation "${WHEEL_PATH}" || true
180
  if ${CHECK_FN}; then
181
  echo "[flow] ${PKG}: sucesso via Hub (${WHEEL_PATH})"
182
  return 0
 
194
  return 0
195
  fi
196
 
197
+ echo "[flow] ${PKG}: falhou após build; seguindo adiante"
198
  return 1
199
  }
200
 
 
202
  ensure_pkg "apex" check_apex build_apex || true
203
  ensure_pkg "q8_kernels" check_q8 build_q8 || true
204
 
205
+ # ===== Upload das wheels geradas (opcional) =====
206
  python - <<'PY'
207
  import os
208
  from huggingface_hub import HfApi, HfFolder
209
  repo=os.environ.get("SELF_HF_REPO_ID","carlex3321/aduc-sdr")
210
  token=os.getenv("HF_TOKEN") or HfFolder.get_token()
211
  if not token:
212
+ raise SystemExit("HF_TOKEN ausente; upload desabilitado")
213
  api=HfApi(token=token)
214
  api.upload_folder(
215
+ folder_path="/app/wheels",
216
+ repo_id=repo,
217
+ repo_type="model",
218
+ allow_patterns=["*.whl","NGC-DL-CONTAINER-LICENSE"],
219
+ ignore_patterns=["**/src/**","**/*.log","**/logs/**",".git/**"],
220
  )
221
  print("Upload concluído (wheels + licença).")
222
  PY
223
 
 
224
  chmod -R 777 /app/wheels || true
225
  echo "✅ Builder finalizado."
start.sh CHANGED
@@ -32,28 +32,54 @@ export NCCL_BLOCKING_WAIT=1
32
  export TORCH_NCCL_BLOCKING_WAIT=1
33
  export NCCL_TIMEOUT=600
34
 
35
- # HF caches
36
- export HF_HOME=/app/.cache/huggingface
37
- unset TRANSFORMERS_CACHE
38
-
39
- # ---------------------- Baixar o modelo antes de iniciar ----------------------
40
- echo "Criando diretório de cache se não existir..."
41
- mkdir -p /app/.cache/huggingface/hub
 
 
 
 
 
 
42
 
43
- # Ativando hf_transfer para downloads mais rápidos
 
44
  export HF_HUB_ENABLE_HF_TRANSFER=1
45
  export HF_HUB_DOWNLOAD_TIMEOUT=60
46
 
 
47
  MODEL_REPO="ByteDance-Seed/VINCIE-3B"
 
48
 
49
  retry_count=0
50
  max_retries=3
51
-
52
  while [ $retry_count -lt $max_retries ]; do
53
- echo "Tentativa $((retry_count+1)) de baixar o modelo $MODEL_REPO..."
54
- python -c "import os; from huggingface_hub import snapshot_download; os.environ['HF_HOME']='/app/.cache/huggingface'; from os import getenv; snapshot_download(repo_id='$MODEL_REPO', cache_dir=os.path.join(os.environ['HF_HOME'], 'hub'), resume_download=True, token=getenv('HF_TOKEN'))" && break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  retry_count=$((retry_count+1))
56
- echo "Falha na tentativa $retry_count. Tentando novamente em 10 segundos..."
57
  sleep 10
58
  if [ $retry_count -eq $max_retries ]; then
59
  echo "Erro: Falha ao baixar o modelo após $max_retries tentativas."
@@ -61,19 +87,20 @@ while [ $retry_count -lt $max_retries ]; do
61
  fi
62
  done
63
 
64
- ls -la /app/.cache/huggingface/hub || echo "Cache não encontrado após download."
65
 
66
  # ---------------------- Builder Apex/Q8 ----------------------
67
  if nvidia-smi >/dev/null 2>&1; then
68
  if [ "${DISABLE_BUILDER:-0}" -eq 0 ]; then
69
  echo "Executando builder Apex/Q8..."
70
- chmod +x /app/builder.sh || true
71
- # Executa com GPU disponível, busca wheels no HF e compila se necessário (sem FlashAttention)
72
- SELF_HF_REPO_ID=carlex3321/aduc-sdr # repo de wheels no HF
73
- HF_UPLOAD_WHEELS=0 # publica wheels geradas
74
- BUILDER_TIMEOUT_SEC=6000000 # tempo limite
75
- Q8_REPO="" Q8_COMMIT="" # pin do LTX Q8
76
- timeout ${BUILDER_TIMEOUT_SEC:-60000} bash -lc /app/builder.sh || echo "Builder excedeu tempo/retornou erro, prosseguindo com a aplicação."
 
77
  else
78
  echo "Builder desabilitado por DISABLE_BUILDER=1"
79
  fi
@@ -81,16 +108,21 @@ else
81
  echo "GPU não visível, pulando builder Apex/Q8."
82
  fi
83
 
84
- # ---------------------- Banner ----------------------
85
- ./info.sh || true
86
-
87
-
88
- ls -la /app && ls -R /app | head -n 2000
 
89
 
 
90
  echo "🚀 Subindo serviços..."
91
-
92
- # Exemplo: subir UI mínima SD Img2Img (ajuste conforme seu app)
93
- # python app_animatediff_min.py
94
-
95
- # Ou subir VINCIE UI se for o caso
96
- python app_vince.py
 
 
 
 
32
  export TORCH_NCCL_BLOCKING_WAIT=1
33
  export NCCL_TIMEOUT=600
34
 
35
+ # ---------------------- Persistência: /data ----------------------
36
+ if [ -d /data ]; then
37
+ export HF_HOME="/data/.cache/huggingface"
38
+ export TORCH_HOME="/data/.cache/torch"
39
+ mkdir -p "$HF_HOME/hub" "$TORCH_HOME"
40
+ # Symlink para compatibilidade com /app
41
+ mkdir -p /app/.cache
42
+ ln -sf "$HF_HOME" /app/.cache/huggingface
43
+ else
44
+ export HF_HOME="/app/.cache/huggingface"
45
+ export TORCH_HOME="/app/.cache/torch"
46
+ mkdir -p "$HF_HOME/hub" "$TORCH_HOME"
47
+ fi
48
 
49
+ unset TRANSFORMERS_CACHE # evitar conflito com outros caches
50
+ export HF_HUB_CACHE="${HF_HUB_CACHE:-$HF_HOME/hub}"
51
  export HF_HUB_ENABLE_HF_TRANSFER=1
52
  export HF_HUB_DOWNLOAD_TIMEOUT=60
53
 
54
+ # ---------------------- Pré-download do modelo ----------------------
55
  MODEL_REPO="ByteDance-Seed/VINCIE-3B"
56
+ echo "Baixando o modelo ${MODEL_REPO} para cache persistente em $HF_HUB_CACHE..."
57
 
58
  retry_count=0
59
  max_retries=3
 
60
  while [ $retry_count -lt $max_retries ]; do
61
+ echo "Tentativa $((retry_count+1)) de snapshot_download..."
62
+ python - <<'PY' || true
63
+ import os, time
64
+ from huggingface_hub import snapshot_download
65
+ hf_home = os.environ.get("HF_HOME","/app/.cache/huggingface")
66
+ cache_dir = os.path.join(hf_home, "hub")
67
+ os.makedirs(cache_dir, exist_ok=True)
68
+ repo_id = "ByteDance-Seed/VINCIE-3B"
69
+ token = os.getenv("HF_TOKEN")
70
+ try:
71
+ snapshot_download(repo_id=repo_id, cache_dir=cache_dir, resume_download=True, token=token)
72
+ print("Download concluído.")
73
+ except Exception as e:
74
+ print(f"Erro no download: {e}")
75
+ raise
76
+ PY
77
+ status=$?
78
+ if [ $status -eq 0 ]; then
79
+ break
80
+ fi
81
  retry_count=$((retry_count+1))
82
+ echo "Falha na tentativa $retry_count. Tentando novamente em 10s..."
83
  sleep 10
84
  if [ $retry_count -eq $max_retries ]; then
85
  echo "Erro: Falha ao baixar o modelo após $max_retries tentativas."
 
87
  fi
88
  done
89
 
90
+ ls -la "$HF_HUB_CACHE" || echo "Cache não encontrado após download."
91
 
92
  # ---------------------- Builder Apex/Q8 ----------------------
93
  if nvidia-smi >/dev/null 2>&1; then
94
  if [ "${DISABLE_BUILDER:-0}" -eq 0 ]; then
95
  echo "Executando builder Apex/Q8..."
96
+ # Use o builder revisado (salve como /app/builder-7.sh)
97
+ if [ -f /app/builder-7.sh ]; then
98
+ chmod +x /app/builder-7.sh
99
+ timeout "${BUILDER_TIMEOUT_SEC:-7200}" bash -lc /app/builder-7.sh || echo "Builder excedeu tempo/erro, prosseguindo."
100
+ else
101
+ chmod +x /app/builder.sh || true
102
+ timeout "${BUILDER_TIMEOUT_SEC:-7200}" bash -lc /app/builder.sh || echo "Builder excedeu tempo/erro, prosseguindo."
103
+ fi
104
  else
105
  echo "Builder desabilitado por DISABLE_BUILDER=1"
106
  fi
 
108
  echo "GPU não visível, pulando builder Apex/Q8."
109
  fi
110
 
111
+ # ---------------------- Diagnóstico e listagem ----------------------
112
+ /app/info.sh || true
113
+ echo "Estrutura em /app:"
114
+ ls -la /app | sed -n '1,200p'
115
+ echo "Estrutura em $HF_HOME:"
116
+ ls -la "$HF_HOME" | sed -n '1,200p'
117
 
118
+ # ---------------------- Subindo serviço ----------------------
119
  echo "🚀 Subindo serviços..."
120
+ # Se o seu entrypoint é a UI Gradio:
121
+ if [ -f /app/app_vince.py ]; then
122
+ python /app/app_vince.py
123
+ elif [ -f /app/vince.py ]; then
124
+ python /app/vince.py
125
+ else
126
+ echo "Nenhum entrypoint encontrado (app_vince.py ou vince.py)."
127
+ exit 1
128
+ fi