euIaxs22 commited on
Commit
fc8e69c
·
verified ·
1 Parent(s): 954df00

Create app_vince.py

Browse files
Files changed (1) hide show
  1. app_vince.py +245 -0
app_vince.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ VINCIE Service UI (Gradio)
4
+
5
+ - Automatic setup runs on app load (no manual setup button).
6
+ - Multi-turn editing and multi-concept composition front-end.
7
+ - Designed for NVIDIA L40S (SM 8.9) environments aligned with CUDA 12.x.
8
+ - Functional reference: ByteDance-Seed/VINCIE.
9
+ - Space and Docker developed by Carlex (contact below).
10
+ """
11
+
12
+ import os
13
+ from pathlib import Path
14
+ from typing import List, Tuple, Optional
15
+
16
+ import gradio as gr
17
+
18
+ # Adapt this import to the project layout.
19
+ # Provide a VincieService with:
20
+ # - ensure_repo(): clones/updates upstream repo if missing
21
+ # - ensure_model(): downloads/validates checkpoints to /app/ckpt/VINCIE-3B
22
+ # - multi_turn_edit(image_path: str, turns: List[str], *, cfg_scale: Optional[float], resolution_input: Optional[int], aspect_ratio_input: Optional[str], steps: Optional[int]) -> str
23
+ # - multi_concept_compose(files: List[str], descs: List[str], final_prompt: str, *, cfg_scale: Optional[float], resolution_input: Optional[int], aspect_ratio_input: Optional[str], steps: Optional[int]) -> str
24
+ from services.vincie import VincieService # change path if needed
25
+
26
+ # Instantiate the service (defaults to /app/VINCIE and /app/ckpt/VINCIE-3B)
27
+ svc = VincieService()
28
+
29
+ def setup_auto() -> str:
30
+ """
31
+ Run an idempotent setup on interface load:
32
+ - Ensure the upstream repository is present and ready.
33
+ - Ensure the model checkpoint is downloaded and ready.
34
+ Returns an English status string for the UI.
35
+ """
36
+ try:
37
+ svc.ensure_repo()
38
+ svc.ensure_model()
39
+ return (
40
+ "Setup completed successfully: repository and checkpoint are ready "
41
+ "for inference on an NVIDIA L40S environment."
42
+ )
43
+ except Exception as e:
44
+ return f"Setup encountered an error: {e}"
45
+
46
+ def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
47
+ """
48
+ Enumerate resulting images and the most recent video from an output directory.
49
+ Args:
50
+ out_dir: Path to the directory where the service wrote its results.
51
+ max_images: Upper bound on how many images to surface in the gallery.
52
+ Returns:
53
+ A tuple (images, video) where:
54
+ - images is a list of file paths to images sorted by modified time,
55
+ - video is the path to the latest .mp4 if found, otherwise None.
56
+ """
57
+ img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
58
+ images: List[Path] = []
59
+ for pat in img_globs:
60
+ images += list(out_dir.rglob(pat))
61
+ images = sorted(images, key=lambda p: p.stat().st_mtime)
62
+ image_paths = [str(p) for p in images[-max_images:]] if images else []
63
+ videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
64
+ video_path = str(videos[-1]) if videos else None
65
+ return image_paths, video_path
66
+
67
+ def ui_multi_turn(
68
+ input_image: Optional[str],
69
+ turns_text: Optional[str],
70
+ steps_input: int,
71
+ cfg_scale_input: float,
72
+ aspect_ratio_input: str,
73
+ resolution_input: int,
74
+ ):
75
+ """
76
+ Multi-turn image editing entrypoint for the UI.
77
+ Args:
78
+ input_image: Path to a single input image on disk.
79
+ turns_text: User-provided editing turns, one instruction per line.
80
+ steps_input: Override for generation steps.
81
+ cfg_scale_input: Override for guidance scale.
82
+ aspect_ratio_input: Override for aspect ratio selection.
83
+ resolution_input: Override for shorter-side resolution.
84
+ Returns:
85
+ (gallery, video, status) for Gradio components.
86
+ """
87
+ if not input_image or not str(input_image).strip():
88
+ return [], None, "Please provide an input image."
89
+ if not turns_text or not turns_text.strip():
90
+ return [], None, "Please provide edit turns (one per line)."
91
+
92
+ turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
93
+ try:
94
+ out_dir = svc.multi_turn_edit(
95
+ input_image,
96
+ turns,
97
+ cfg_scale=float(cfg_scale_input) if cfg_scale_input is not None else None,
98
+ resolution_input=int(resolution_input) if resolution_input is not None else None,
99
+ aspect_ratio_input=str(aspect_ratio_input) if aspect_ratio_input else None,
100
+ steps=int(steps_input) if steps_input is not None else None,
101
+ )
102
+ except Exception as e:
103
+ return [], None, f"Generation error: {e}"
104
+
105
+ imgs, vid = _list_media(Path(out_dir))
106
+ status = f"Outputs saved to: {out_dir}"
107
+ return imgs, vid, status
108
+
109
+ def ui_multi_concept(
110
+ files: Optional[List[str]],
111
+ descs_text: Optional[str],
112
+ final_prompt: Optional[str],
113
+ steps_input: int,
114
+ cfg_scale_input: float,
115
+ aspect_ratio_input: str,
116
+ resolution_input: int,
117
+ ):
118
+ """
119
+ Multi-concept composition entrypoint for the UI.
120
+ Args:
121
+ files: List of paths to concept images on disk.
122
+ descs_text: Per-image descriptions (one line per image, in the same order).
123
+ final_prompt: A final composition prompt that aggregates the concepts.
124
+ steps_input: Override for generation steps.
125
+ cfg_scale_input: Override for guidance scale.
126
+ aspect_ratio_input: Override for aspect ratio selection.
127
+ resolution_input: Override for shorter-side resolution.
128
+ Returns:
129
+ (gallery, video, status) for Gradio components.
130
+ """
131
+ if not files:
132
+ return [], None, "Please upload concept images."
133
+ if not descs_text or not descs_text.strip():
134
+ return [], None, "Please provide descriptions (one per line)."
135
+ if not final_prompt or not final_prompt.strip():
136
+ return [], None, "Please provide a final prompt."
137
+
138
+ descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
139
+ if len(descs) != len(files):
140
+ return [], None, f"Descriptions count ({len(descs)}) must match images count ({len(files)})."
141
+
142
+ try:
143
+ out_dir = svc.multi_concept_compose(
144
+ files,
145
+ descs,
146
+ final_prompt,
147
+ cfg_scale=float(cfg_scale_input) if cfg_scale_input is not None else None,
148
+ resolution_input=int(resolution_input) if resolution_input is not None else None,
149
+ aspect_ratio_input=str(aspect_ratio_input) if aspect_ratio_input else None,
150
+ steps=int(steps_input) if steps_input is not None else None,
151
+ )
152
+ except Exception as e:
153
+ return [], None, f"Generation error: {e}"
154
+
155
+ imgs, vid = _list_media(Path(out_dir))
156
+ status = f"Outputs saved to: {out_dir}"
157
+ return imgs, vid, status
158
+
159
+ with gr.Blocks(title="VINCIE Service") as demo:
160
+ # Header and credits
161
+ gr.Markdown(
162
+ "\n".join(
163
+ [
164
+ "# VINCIE Service — Multi-turn Editing and Multi-concept Composition",
165
+ "- Automatic setup runs at startup; setup status appears below.",
166
+ "- Hardware requirement: NVIDIA L40S (SM 8.9) is recommended for this build.",
167
+ "- Functional upstream model: ByteDance-Seed/VINCIE (see project repository).",
168
+ "- Space and Docker were developed by Carlex.",
169
+ "- Contact: Email: [email protected] | GitHub: carlex22",
170
+ ]
171
+ )
172
+ )
173
+
174
+ with gr.Row():
175
+ setup_out = gr.Textbox(label="Setup Status", interactive=False)
176
+
177
+ # Advanced options shared by both tabs
178
+ with gr.Accordion("Opções Avançadas (para Abas 1 e 2)", open=False):
179
+ steps_input = gr.Slider(
180
+ label="Passos de Inferência",
181
+ minimum=10,
182
+ maximum=100,
183
+ step=1,
184
+ value=50,
185
+ )
186
+ cfg_scale_input = gr.Slider(
187
+ label="Escala de Orientação (CFG)",
188
+ minimum=1.0,
189
+ maximum=15.0,
190
+ step=0.5,
191
+ value=7.5,
192
+ )
193
+ aspect_ratio_input = gr.Dropdown(
194
+ label="Aspect Ratio",
195
+ choices=["keep_ratio", "1:1", "16:9", "9:16", "4:3", "3:4"],
196
+ value="keep_ratio",
197
+ )
198
+ resolution_input = gr.Slider(
199
+ label="Resolução (lado menor)",
200
+ minimum=256,
201
+ maximum=1024,
202
+ step=64,
203
+ value=512,
204
+ )
205
+
206
+ with gr.Tab("Multi-turn Editing"):
207
+ with gr.Row():
208
+ img = gr.Image(type="filepath", label="Input image")
209
+ turns = gr.Textbox(lines=8, label="Turns (one per line)")
210
+ run1 = gr.Button("Run")
211
+ out_gallery = gr.Gallery(label="Images", columns=4, height="auto")
212
+ out_video = gr.Video(label="Video (if available)")
213
+ out_status = gr.Textbox(label="Output", interactive=False)
214
+
215
+ run1.click(
216
+ ui_multi_turn,
217
+ inputs=[img, turns, steps_input, cfg_scale_input, aspect_ratio_input, resolution_input],
218
+ outputs=[out_gallery, out_video, out_status],
219
+ )
220
+
221
+ with gr.Tab("Multi-concept Composition"):
222
+ files = gr.File(file_count="multiple", type="filepath", label="Concept images")
223
+ descs = gr.Textbox(lines=8, label="Descriptions (one per line, same order as images)")
224
+ final_prompt = gr.Textbox(lines=2, label="Final prompt")
225
+ run2 = gr.Button("Run")
226
+ out_gallery2 = gr.Gallery(label="Images", columns=4, height="auto")
227
+ out_video2 = gr.Video(label="Video (if available)")
228
+ out_status2 = gr.Textbox(label="Output", interactive=False)
229
+
230
+ run2.click(
231
+ ui_multi_concept,
232
+ inputs=[files, descs, final_prompt, steps_input, cfg_scale_input, aspect_ratio_input, resolution_input],
233
+ outputs=[out_gallery2, out_video2, out_status2],
234
+ )
235
+
236
+ # Auto-setup on load (no manual button)
237
+ demo.load(fn=setup_auto, outputs=setup_out)
238
+
239
+ if __name__ == "__main__":
240
+ demo.launch(
241
+ server_name="0.0.0.0",
242
+ server_port=int(os.getenv("PORT", "7860")),
243
+ allowed_paths=["/app/outputs", "/app/ckpt"],
244
+ )
245
+