Add 3D model (GLTF/GLB) support for Text to Image
Browse files- Add Model3D output component for 3D format
- Toggle visibility between Image and Model3D based on format
- Handler returns (image_url, model_url, status) tuple
- 3D format uses generate_image_3d tool and renders as Model3D
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- app.py +1 -0
- src/ui/handlers.py +21 -10
- src/ui/tabs.py +19 -3
app.py
CHANGED
|
@@ -66,6 +66,7 @@ def create_demo():
|
|
| 66 |
],
|
| 67 |
outputs=[
|
| 68 |
tabs["text_to_image"]["output_image"],
|
|
|
|
| 69 |
tabs["text_to_image"]["status"]
|
| 70 |
],
|
| 71 |
api_name=None
|
|
|
|
| 66 |
],
|
| 67 |
outputs=[
|
| 68 |
tabs["text_to_image"]["output_image"],
|
| 69 |
+
tabs["text_to_image"]["output_model"],
|
| 70 |
tabs["text_to_image"]["status"]
|
| 71 |
],
|
| 72 |
api_name=None
|
src/ui/handlers.py
CHANGED
|
@@ -185,12 +185,18 @@ class Handlers:
|
|
| 185 |
prompt: str,
|
| 186 |
format_type: str,
|
| 187 |
api_key: str = ""
|
| 188 |
-
) -> Tuple[Optional[str], str]:
|
| 189 |
-
"""Handle text-to-image generation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
if not prompt.strip():
|
| 191 |
-
return None, "Please enter a description for your image."
|
| 192 |
if not api_key.strip():
|
| 193 |
-
return None, "Please enter your API key in the Settings section."
|
| 194 |
|
| 195 |
client = StackNetClient(api_key=api_key.strip())
|
| 196 |
service = ImageService(client=client)
|
|
@@ -200,24 +206,29 @@ class Handlers:
|
|
| 200 |
asyncio.set_event_loop(loop)
|
| 201 |
|
| 202 |
try:
|
| 203 |
-
|
| 204 |
service.generate_image(
|
| 205 |
prompt=prompt,
|
| 206 |
format_type=format_type
|
| 207 |
)
|
| 208 |
)
|
| 209 |
|
| 210 |
-
if
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
else:
|
| 214 |
-
return None, "No
|
| 215 |
|
| 216 |
finally:
|
| 217 |
loop.close()
|
| 218 |
|
| 219 |
except Exception as e:
|
| 220 |
-
return None, format_error(e)
|
| 221 |
|
| 222 |
finally:
|
| 223 |
service.cleanup()
|
|
|
|
| 185 |
prompt: str,
|
| 186 |
format_type: str,
|
| 187 |
api_key: str = ""
|
| 188 |
+
) -> Tuple[Optional[str], Optional[str], str]:
|
| 189 |
+
"""Handle text-to-image generation.
|
| 190 |
+
|
| 191 |
+
Returns:
|
| 192 |
+
Tuple of (image_url, model_url, status)
|
| 193 |
+
- For image/multi: (url, None, status)
|
| 194 |
+
- For 3d: (None, url, status)
|
| 195 |
+
"""
|
| 196 |
if not prompt.strip():
|
| 197 |
+
return None, None, "Please enter a description for your image."
|
| 198 |
if not api_key.strip():
|
| 199 |
+
return None, None, "Please enter your API key in the Settings section."
|
| 200 |
|
| 201 |
client = StackNetClient(api_key=api_key.strip())
|
| 202 |
service = ImageService(client=client)
|
|
|
|
| 206 |
asyncio.set_event_loop(loop)
|
| 207 |
|
| 208 |
try:
|
| 209 |
+
results = loop.run_until_complete(
|
| 210 |
service.generate_image(
|
| 211 |
prompt=prompt,
|
| 212 |
format_type=format_type
|
| 213 |
)
|
| 214 |
)
|
| 215 |
|
| 216 |
+
if results:
|
| 217 |
+
url = results[0].image_url
|
| 218 |
+
if format_type == "3d":
|
| 219 |
+
# Return as 3D model
|
| 220 |
+
return None, url, "3D model generated successfully!"
|
| 221 |
+
else:
|
| 222 |
+
# Return as image
|
| 223 |
+
return url, None, "Image generated successfully!"
|
| 224 |
else:
|
| 225 |
+
return None, None, "No result was generated. Please try a different prompt."
|
| 226 |
|
| 227 |
finally:
|
| 228 |
loop.close()
|
| 229 |
|
| 230 |
except Exception as e:
|
| 231 |
+
return None, None, format_error(e)
|
| 232 |
|
| 233 |
finally:
|
| 234 |
service.cleanup()
|
src/ui/tabs.py
CHANGED
|
@@ -162,18 +162,34 @@ def create_text_to_image_tab():
|
|
| 162 |
value="image"
|
| 163 |
)
|
| 164 |
|
| 165 |
-
generate_btn = gr.Button("Generate
|
| 166 |
|
| 167 |
status = gr.Textbox(label="Status", interactive=False, visible=False)
|
| 168 |
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
|
| 171 |
return {
|
| 172 |
"prompt": prompt,
|
| 173 |
"format_type": format_type,
|
| 174 |
"generate_btn": generate_btn,
|
| 175 |
"status": status,
|
| 176 |
-
"output_image": output_image
|
|
|
|
| 177 |
}
|
| 178 |
|
| 179 |
|
|
|
|
| 162 |
value="image"
|
| 163 |
)
|
| 164 |
|
| 165 |
+
generate_btn = gr.Button("Generate", variant="primary", size="lg")
|
| 166 |
|
| 167 |
status = gr.Textbox(label="Status", interactive=False, visible=False)
|
| 168 |
|
| 169 |
+
# Image output (for image and multi formats)
|
| 170 |
+
output_image = gr.Image(label="Generated Image", type="filepath", visible=True)
|
| 171 |
+
|
| 172 |
+
# 3D Model output (for 3d format)
|
| 173 |
+
output_model = gr.Model3D(label="Generated 3D Model", visible=False)
|
| 174 |
+
|
| 175 |
+
# Toggle output visibility based on format selection
|
| 176 |
+
format_type.change(
|
| 177 |
+
fn=lambda fmt: (
|
| 178 |
+
gr.update(visible=(fmt != "3d")),
|
| 179 |
+
gr.update(visible=(fmt == "3d"))
|
| 180 |
+
),
|
| 181 |
+
inputs=[format_type],
|
| 182 |
+
outputs=[output_image, output_model],
|
| 183 |
+
api_name=None
|
| 184 |
+
)
|
| 185 |
|
| 186 |
return {
|
| 187 |
"prompt": prompt,
|
| 188 |
"format_type": format_type,
|
| 189 |
"generate_btn": generate_btn,
|
| 190 |
"status": status,
|
| 191 |
+
"output_image": output_image,
|
| 192 |
+
"output_model": output_model
|
| 193 |
}
|
| 194 |
|
| 195 |
|