Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -9,7 +9,8 @@ import traceback
|
|
9 |
|
10 |
# --- Core Logic Imports ---
|
11 |
from core.llm_services import initialize_text_llms, is_gemini_text_ready, is_hf_text_ready, generate_text_gemini, generate_text_hf
|
12 |
-
from core.image_services import initialize_image_llms,
|
|
|
13 |
from core.story_engine import Story, Scene
|
14 |
from prompts.narrative_prompts import get_narrative_system_prompt, format_narrative_user_prompt
|
15 |
from prompts.image_style_prompts import STYLE_PRESETS, COMMON_NEGATIVE_PROMPTS, format_image_generation_prompt
|
@@ -17,21 +18,18 @@ from core.utils import basic_text_cleanup
|
|
17 |
|
18 |
# --- Initialize Services ---
|
19 |
initialize_text_llms()
|
20 |
-
initialize_image_llms()
|
21 |
|
22 |
# --- Get API Readiness Status ---
|
23 |
GEMINI_TEXT_IS_READY = is_gemini_text_ready()
|
24 |
-
HF_TEXT_IS_READY = is_hf_text_ready()
|
25 |
-
|
26 |
-
OPENAI_DALLE_IS_READY = OPENAI_DALLE_CONFIGURED
|
27 |
-
HF_IMAGE_IS_READY = HF_IMAGE_API_CONFIGURED # For image models via HF API
|
28 |
|
29 |
# --- Application Configuration (Models, Defaults) ---
|
30 |
TEXT_MODELS = {}
|
31 |
UI_DEFAULT_TEXT_MODEL_KEY = None
|
32 |
if GEMINI_TEXT_IS_READY:
|
33 |
TEXT_MODELS["β¨ Gemini 1.5 Flash (Narrate)"] = {"id": "gemini-1.5-flash-latest", "type": "gemini"}
|
34 |
-
TEXT_MODELS["Legacy Gemini 1.0 Pro (Narrate)"] = {"id": "gemini-1.0-pro-latest", "type": "gemini"}
|
35 |
if HF_TEXT_IS_READY:
|
36 |
TEXT_MODELS["Mistral 7B (Narrate via HF)"] = {"id": "mistralai/Mistral-7B-Instruct-v0.2", "type": "hf_text"}
|
37 |
TEXT_MODELS["Gemma 2B (Narrate via HF)"] = {"id": "google/gemma-2b-it", "type": "hf_text"}
|
@@ -39,29 +37,21 @@ if HF_TEXT_IS_READY:
|
|
39 |
if TEXT_MODELS:
|
40 |
if GEMINI_TEXT_IS_READY and "β¨ Gemini 1.5 Flash (Narrate)" in TEXT_MODELS: UI_DEFAULT_TEXT_MODEL_KEY = "β¨ Gemini 1.5 Flash (Narrate)"
|
41 |
elif HF_TEXT_IS_READY and "Mistral 7B (Narrate via HF)" in TEXT_MODELS: UI_DEFAULT_TEXT_MODEL_KEY = "Mistral 7B (Narrate via HF)"
|
42 |
-
|
43 |
else:
|
44 |
TEXT_MODELS["No Text Models Configured"] = {"id": "dummy_text_error", "type": "none"}
|
45 |
UI_DEFAULT_TEXT_MODEL_KEY = "No Text Models Configured"
|
46 |
|
47 |
-
|
48 |
IMAGE_PROVIDERS = {}
|
49 |
UI_DEFAULT_IMAGE_PROVIDER_KEY = None
|
50 |
-
# Order of preference for default: Stability, then HF, then DALL-E (simulated)
|
51 |
-
if STABILITY_API_IS_READY: IMAGE_PROVIDERS["π¨ Stability AI (SDXL)"] = "stability_ai"
|
52 |
if HF_IMAGE_IS_READY:
|
53 |
-
IMAGE_PROVIDERS["π‘ HF
|
54 |
-
IMAGE_PROVIDERS["π HF
|
55 |
-
|
56 |
-
|
57 |
-
if IMAGE_PROVIDERS:
|
58 |
-
if STABILITY_API_IS_READY and "π¨ Stability AI (SDXL)" in IMAGE_PROVIDERS: UI_DEFAULT_IMAGE_PROVIDER_KEY = "π¨ Stability AI (SDXL)"
|
59 |
-
elif HF_IMAGE_IS_READY and "π‘ HF Model (SDXL Base)" in IMAGE_PROVIDERS: UI_DEFAULT_IMAGE_PROVIDER_KEY = "π‘ HF Model (SDXL Base)"
|
60 |
-
elif OPENAI_DALLE_IS_READY and "πΌοΈ DALL-E 3 (Sim.)" in IMAGE_PROVIDERS: UI_DEFAULT_IMAGE_PROVIDER_KEY = "πΌοΈ DALL-E 3 (Sim.)"
|
61 |
-
else: UI_DEFAULT_IMAGE_PROVIDER_KEY = list(IMAGE_PROVIDERS.keys())[0]
|
62 |
else:
|
63 |
-
IMAGE_PROVIDERS["No Image Providers Configured"] = "none"
|
64 |
-
UI_DEFAULT_IMAGE_PROVIDER_KEY = "No Image Providers Configured"
|
65 |
|
66 |
|
67 |
# --- Gradio UI Theme and CSS ---
|
@@ -85,7 +75,7 @@ body, .gradio-container { background-color: #0F0F1A !important; color: #D0D0E0 !
|
|
85 |
.output-section-header { font-size: 1.8em; font-weight: 600; color: #D0D0FF; margin-top: 15px; margin-bottom: 12px;}
|
86 |
.gr-input input, .gr-input textarea, .gr-dropdown select, .gr-textbox textarea { background-color: #2A2A4A !important; color: #E0E0FF !important; border: 1px solid #4A4A6A !important; border-radius: 8px !important; padding: 10px !important;}
|
87 |
.gr-button { border-radius: 8px !important; font-weight: 500 !important; transition: all 0.2s ease-in-out !important;}
|
88 |
-
.gr-button-primary { padding-top: 10px !important; padding-bottom: 10px !important; }
|
89 |
.gr-button-primary:hover { transform: scale(1.03) translateY(-1px) !important; box-shadow: 0 8px 16px rgba(127,0,255,0.3) !important; }
|
90 |
.panel_image { border-radius: 12px !important; overflow: hidden; box-shadow: 0 6px 15px rgba(0,0,0,0.25) !important; background-color: #23233A;}
|
91 |
.panel_image img { max-height: 600px !important; }
|
@@ -110,7 +100,6 @@ body, .gradio-container { background-color: #0F0F1A !important; color: #D0D0E0 !
|
|
110 |
|
111 |
# --- Helper: Placeholder Image Creation ---
|
112 |
def create_placeholder_image(text="Processing...", size=(512, 512), color="#23233A", text_color="#E0E0FF"):
|
113 |
-
# ... (Full implementation as before) ...
|
114 |
img = Image.new('RGB', size, color=color); draw = ImageDraw.Draw(img)
|
115 |
try: font_path = "arial.ttf" if os.path.exists("arial.ttf") else None
|
116 |
except: font_path = None
|
@@ -120,7 +109,6 @@ def create_placeholder_image(text="Processing...", size=(512, 512), color="#2323
|
|
120 |
else: tw, th = draw.textsize(text, font=font)
|
121 |
draw.text(((size[0]-tw)/2, (size[1]-th)/2), text, font=font, fill=text_color); return img
|
122 |
|
123 |
-
|
124 |
# --- StoryVerse Weaver Orchestrator ---
|
125 |
def add_scene_to_story_orchestrator(
|
126 |
current_story_obj: Story, scene_prompt_text: str, image_style_dropdown: str, artist_style_text: str,
|
@@ -133,20 +121,18 @@ def add_scene_to_story_orchestrator(
|
|
133 |
|
134 |
log_accumulator = [f"**π Scene {current_story_obj.current_scene_number + 1} - {time.strftime('%H:%M:%S')}**"]
|
135 |
|
136 |
-
# Placeholders for the final return tuple
|
137 |
ret_story_state = current_story_obj
|
138 |
ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
|
139 |
-
ret_latest_image =
|
140 |
-
ret_latest_narrative_md_obj = gr.Markdown(value="
|
141 |
-
ret_status_bar_html_obj = gr.HTML(value=
|
142 |
-
#
|
143 |
|
144 |
-
# Initial yield for UI updates
|
145 |
-
# This first yield is for immediate feedback on other components
|
146 |
yield {
|
147 |
-
output_status_bar:
|
148 |
-
output_latest_scene_image: gr.Image(value=
|
149 |
-
output_latest_scene_narrative:
|
150 |
output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator))
|
151 |
}
|
152 |
|
@@ -166,21 +152,13 @@ def add_scene_to_story_orchestrator(
|
|
166 |
text_response = None
|
167 |
if text_model_info["type"] == "gemini": text_response = generate_text_gemini(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
|
168 |
elif text_model_info["type"] == "hf_text": text_response = generate_text_hf(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
elif text_response:
|
174 |
-
narrative_text_generated = f"**Narrative Error ({text_model_key}):** {text_response.error}"
|
175 |
-
log_accumulator.append(f" Narrative: FAILED - {text_response.error}")
|
176 |
-
else:
|
177 |
-
log_accumulator.append(f" Narrative: FAILED - No response object from {text_model_key}.")
|
178 |
-
else:
|
179 |
-
narrative_text_generated = "**Narrative Error:** Selected text model not available or misconfigured."
|
180 |
-
log_accumulator.append(f" Narrative: FAILED - Model '{text_model_key}' not available.")
|
181 |
|
182 |
ret_latest_narrative_str_content = f"## Scene Idea: {scene_prompt_text}\n\n{narrative_text_generated}"
|
183 |
-
ret_latest_narrative_md_obj = gr.Markdown(value=ret_latest_narrative_str_content)
|
184 |
yield { output_latest_scene_narrative: ret_latest_narrative_md_obj,
|
185 |
output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
|
186 |
|
@@ -188,31 +166,51 @@ def add_scene_to_story_orchestrator(
|
|
188 |
progress(0.5, desc="π¨ Conjuring visuals...")
|
189 |
image_generated_pil = None
|
190 |
image_generation_error_message = None
|
191 |
-
|
|
|
|
|
192 |
image_content_prompt_for_gen = narrative_text_generated if narrative_text_generated and "Error" not in narrative_text_generated else scene_prompt_text
|
193 |
quality_keyword = "ultra detailed, intricate, masterpiece, " if image_quality == "High Detail" else ("concept sketch, line art, " if image_quality == "Sketch Concept" else "")
|
194 |
full_image_prompt = format_image_generation_prompt(quality_keyword + image_content_prompt_for_gen[:350], image_style_dropdown, artist_style_text)
|
195 |
-
log_accumulator.append(f" Image: Using {
|
196 |
|
197 |
if selected_image_provider_type and selected_image_provider_type != "none":
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
elif selected_image_provider_type == "hf_image_sdxl_base": image_response = generate_image_hf_model(full_image_prompt, model_id="stabilityai/stable-diffusion-xl-base-1.0", negative_prompt=negative_prompt_text or COMMON_NEGATIVE_PROMPTS)
|
202 |
-
elif selected_image_provider_type == "hf_image_openjourney": image_response = generate_image_hf_model(full_image_prompt, model_id="prompthero/openjourney", negative_prompt=negative_prompt_text or COMMON_NEGATIVE_PROMPTS, width=512, height=512)
|
203 |
-
|
204 |
-
if image_response and image_response.success:
|
205 |
-
image_generated_pil = image_response.image
|
206 |
-
log_accumulator.append(f" Image: Success from {image_response.provider}.")
|
207 |
-
elif image_response:
|
208 |
-
image_generation_error_message = f"**Image Error ({image_response.provider}):** {image_response.error}"
|
209 |
-
log_accumulator.append(f" Image: FAILED - {image_response.error}")
|
210 |
else:
|
211 |
-
|
212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
else:
|
214 |
-
image_generation_error_message = "**Image Error:**
|
215 |
-
log_accumulator.append(f" Image: FAILED - Provider '{
|
216 |
|
217 |
ret_latest_image = image_generated_pil if image_generated_pil else create_placeholder_image("Image Gen Failed", color="#401010")
|
218 |
yield { output_latest_scene_image: gr.Image(value=ret_latest_image),
|
@@ -229,7 +227,7 @@ def add_scene_to_story_orchestrator(
|
|
229 |
narrative_text=narrative_text_generated if "**Narrative Error**" not in narrative_text_generated else "(Narrative gen failed)",
|
230 |
image=image_generated_pil,
|
231 |
image_style_prompt=f"{image_style_dropdown}{f', by {artist_style_text}' if artist_style_text and artist_style_text.strip() else ''}",
|
232 |
-
image_provider=
|
233 |
error_message=final_scene_error
|
234 |
)
|
235 |
ret_story_state = current_story_obj
|
@@ -253,15 +251,20 @@ def add_scene_to_story_orchestrator(
|
|
253 |
log_accumulator.append(f"\n**UNEXPECTED RUNTIME ERROR:** {type(e).__name__} - {e}\n{traceback.format_exc()}")
|
254 |
ret_status_bar_html_obj = gr.HTML(value=f"<p class='error_text status_text'>β UNEXPECTED ERROR: {type(e).__name__}. Check logs.</p>")
|
255 |
ret_latest_narrative_md_obj = gr.Markdown(value=f"## Unexpected Error\n{type(e).__name__}: {e}\nSee log for details.")
|
256 |
-
|
257 |
-
|
258 |
current_total_time = time.time() - start_time
|
259 |
log_accumulator.append(f" Cycle ended at {time.strftime('%H:%M:%S')}. Total time: {current_total_time:.2f}s")
|
260 |
ret_log_md = gr.Markdown(value="\n".join(log_accumulator))
|
261 |
|
|
|
262 |
return (
|
263 |
-
ret_story_state,
|
264 |
-
|
|
|
|
|
|
|
|
|
265 |
)
|
266 |
|
267 |
def clear_story_state_ui_wrapper():
|
@@ -283,34 +286,13 @@ def enable_buttons_after_processing():
|
|
283 |
|
284 |
# --- Gradio UI Definition ---
|
285 |
with gr.Blocks(theme=omega_theme, css=omega_css, title="β¨ StoryVerse Omega β¨") as story_weaver_demo:
|
286 |
-
# Define Python variables for UI components that will be inputs or outputs
|
287 |
story_state_output = gr.State(Story())
|
288 |
-
# Input Column Components
|
289 |
-
scene_prompt_input = gr.Textbox()
|
290 |
-
image_style_input = gr.Dropdown()
|
291 |
-
artist_style_input = gr.Textbox()
|
292 |
-
negative_prompt_input = gr.Textbox()
|
293 |
-
text_model_dropdown = gr.Dropdown()
|
294 |
-
image_provider_dropdown = gr.Dropdown()
|
295 |
-
narrative_length_dropdown = gr.Dropdown()
|
296 |
-
image_quality_dropdown = gr.Dropdown()
|
297 |
-
# Output Column Components
|
298 |
-
output_gallery = gr.Gallery()
|
299 |
-
output_latest_scene_image = gr.Image()
|
300 |
-
output_latest_scene_narrative = gr.Markdown()
|
301 |
-
output_status_bar = gr.HTML()
|
302 |
-
output_interaction_log_markdown = gr.Markdown()
|
303 |
-
# Button components (updated via chained .then() events)
|
304 |
-
engage_button = gr.Button()
|
305 |
-
surprise_button = gr.Button()
|
306 |
-
clear_story_button = gr.Button()
|
307 |
|
308 |
-
# --- Layout the UI ---
|
309 |
gr.Markdown("<div align='center'><h1>β¨ StoryVerse Omega β¨</h1>\n<h3>Craft Immersive Multimodal Worlds with AI</h3></div>")
|
310 |
gr.HTML("<div class='important-note'><strong>Welcome, Worldsmith!</strong> Describe your vision, choose your style, and let Omega help you weave captivating scenes with narrative and imagery. Ensure API keys (<code>STORYVERSE_...</code>) are correctly set in Space Secrets!</div>")
|
311 |
|
312 |
with gr.Accordion("π§ AI Services Status & Info", open=False):
|
313 |
-
status_text_list = []; text_llm_ok, image_gen_ok = (GEMINI_TEXT_IS_READY or HF_TEXT_IS_READY), (STABILITY_API_IS_READY or OPENAI_DALLE_IS_READY or HF_IMAGE_IS_READY)
|
314 |
if not text_llm_ok and not image_gen_ok: status_text_list.append("<p style='color:#FCA5A5;font-weight:bold;'>β οΈ CRITICAL: NO AI SERVICES CONFIGURED.</p>")
|
315 |
else:
|
316 |
if text_llm_ok: status_text_list.append("<p style='color:#A7F3D0;'>β
Text Generation Service(s) Ready.</p>")
|
@@ -326,7 +308,7 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="β¨ StoryVerse Omega β¨
|
|
326 |
scene_prompt_input = gr.Textbox(lines=7, label="Scene Vision (Description, Dialogue, Action):", placeholder="e.g., Amidst swirling cosmic dust...")
|
327 |
with gr.Row(elem_classes=["compact-row"]):
|
328 |
with gr.Column(scale=2):
|
329 |
-
image_style_input = gr.Dropdown(choices=["Default (Cinematic Realism)"] + sorted(list(STYLE_PRESETS.keys())), value="Default (Cinematic Realism)", label="Visual Style Preset", allow_custom_value=True)
|
330 |
with gr.Column(scale=2):
|
331 |
artist_style_input = gr.Textbox(label="Artistic Inspiration (Optional):", placeholder="e.g., Moebius...")
|
332 |
negative_prompt_input = gr.Textbox(lines=2, label="Exclude from Image (Negative Prompt):", value=COMMON_NEGATIVE_PROMPTS)
|
|
|
9 |
|
10 |
# --- Core Logic Imports ---
|
11 |
from core.llm_services import initialize_text_llms, is_gemini_text_ready, is_hf_text_ready, generate_text_gemini, generate_text_hf
|
12 |
+
from core.image_services import initialize_image_llms, is_hf_image_api_ready, generate_image_hf_model, ImageGenResponse
|
13 |
+
# Removed STABILITY_API_CONFIGURED, OPENAI_DALLE_CONFIGURED, generate_image_stabilityai, generate_image_dalle
|
14 |
from core.story_engine import Story, Scene
|
15 |
from prompts.narrative_prompts import get_narrative_system_prompt, format_narrative_user_prompt
|
16 |
from prompts.image_style_prompts import STYLE_PRESETS, COMMON_NEGATIVE_PROMPTS, format_image_generation_prompt
|
|
|
18 |
|
19 |
# --- Initialize Services ---
|
20 |
initialize_text_llms()
|
21 |
+
initialize_image_llms() # This will initialize based on HF_TOKEN for images
|
22 |
|
23 |
# --- Get API Readiness Status ---
|
24 |
GEMINI_TEXT_IS_READY = is_gemini_text_ready()
|
25 |
+
HF_TEXT_IS_READY = is_hf_text_ready()
|
26 |
+
HF_IMAGE_IS_READY = is_hf_image_api_ready() # Primary image status
|
|
|
|
|
27 |
|
28 |
# --- Application Configuration (Models, Defaults) ---
|
29 |
TEXT_MODELS = {}
|
30 |
UI_DEFAULT_TEXT_MODEL_KEY = None
|
31 |
if GEMINI_TEXT_IS_READY:
|
32 |
TEXT_MODELS["β¨ Gemini 1.5 Flash (Narrate)"] = {"id": "gemini-1.5-flash-latest", "type": "gemini"}
|
|
|
33 |
if HF_TEXT_IS_READY:
|
34 |
TEXT_MODELS["Mistral 7B (Narrate via HF)"] = {"id": "mistralai/Mistral-7B-Instruct-v0.2", "type": "hf_text"}
|
35 |
TEXT_MODELS["Gemma 2B (Narrate via HF)"] = {"id": "google/gemma-2b-it", "type": "hf_text"}
|
|
|
37 |
if TEXT_MODELS:
|
38 |
if GEMINI_TEXT_IS_READY and "β¨ Gemini 1.5 Flash (Narrate)" in TEXT_MODELS: UI_DEFAULT_TEXT_MODEL_KEY = "β¨ Gemini 1.5 Flash (Narrate)"
|
39 |
elif HF_TEXT_IS_READY and "Mistral 7B (Narrate via HF)" in TEXT_MODELS: UI_DEFAULT_TEXT_MODEL_KEY = "Mistral 7B (Narrate via HF)"
|
40 |
+
else: UI_DEFAULT_TEXT_MODEL_KEY = list(TEXT_MODELS.keys())[0]
|
41 |
else:
|
42 |
TEXT_MODELS["No Text Models Configured"] = {"id": "dummy_text_error", "type": "none"}
|
43 |
UI_DEFAULT_TEXT_MODEL_KEY = "No Text Models Configured"
|
44 |
|
|
|
45 |
IMAGE_PROVIDERS = {}
|
46 |
UI_DEFAULT_IMAGE_PROVIDER_KEY = None
|
|
|
|
|
47 |
if HF_IMAGE_IS_READY:
|
48 |
+
IMAGE_PROVIDERS["π‘ HF - Stable Diffusion XL Base"] = "hf_sdxl_base"
|
49 |
+
IMAGE_PROVIDERS["π HF - OpenJourney (Midjourney-like)"] = "hf_openjourney"
|
50 |
+
IMAGE_PROVIDERS["π HF - Stable Diffusion v1.5"] = "hf_sd_1_5" # Another option
|
51 |
+
UI_DEFAULT_IMAGE_PROVIDER_KEY = "π‘ HF - Stable Diffusion XL Base"
|
|
|
|
|
|
|
|
|
|
|
52 |
else:
|
53 |
+
IMAGE_PROVIDERS["No Image Providers Configured (HF Token needed)"] = "none"
|
54 |
+
UI_DEFAULT_IMAGE_PROVIDER_KEY = "No Image Providers Configured (HF Token needed)"
|
55 |
|
56 |
|
57 |
# --- Gradio UI Theme and CSS ---
|
|
|
75 |
.output-section-header { font-size: 1.8em; font-weight: 600; color: #D0D0FF; margin-top: 15px; margin-bottom: 12px;}
|
76 |
.gr-input input, .gr-input textarea, .gr-dropdown select, .gr-textbox textarea { background-color: #2A2A4A !important; color: #E0E0FF !important; border: 1px solid #4A4A6A !important; border-radius: 8px !important; padding: 10px !important;}
|
77 |
.gr-button { border-radius: 8px !important; font-weight: 500 !important; transition: all 0.2s ease-in-out !important;}
|
78 |
+
.gr-button-primary { padding-top: 10px !important; padding-bottom: 10px !important; }
|
79 |
.gr-button-primary:hover { transform: scale(1.03) translateY(-1px) !important; box-shadow: 0 8px 16px rgba(127,0,255,0.3) !important; }
|
80 |
.panel_image { border-radius: 12px !important; overflow: hidden; box-shadow: 0 6px 15px rgba(0,0,0,0.25) !important; background-color: #23233A;}
|
81 |
.panel_image img { max-height: 600px !important; }
|
|
|
100 |
|
101 |
# --- Helper: Placeholder Image Creation ---
|
102 |
def create_placeholder_image(text="Processing...", size=(512, 512), color="#23233A", text_color="#E0E0FF"):
|
|
|
103 |
img = Image.new('RGB', size, color=color); draw = ImageDraw.Draw(img)
|
104 |
try: font_path = "arial.ttf" if os.path.exists("arial.ttf") else None
|
105 |
except: font_path = None
|
|
|
109 |
else: tw, th = draw.textsize(text, font=font)
|
110 |
draw.text(((size[0]-tw)/2, (size[1]-th)/2), text, font=font, fill=text_color); return img
|
111 |
|
|
|
112 |
# --- StoryVerse Weaver Orchestrator ---
|
113 |
def add_scene_to_story_orchestrator(
|
114 |
current_story_obj: Story, scene_prompt_text: str, image_style_dropdown: str, artist_style_text: str,
|
|
|
121 |
|
122 |
log_accumulator = [f"**π Scene {current_story_obj.current_scene_number + 1} - {time.strftime('%H:%M:%S')}**"]
|
123 |
|
|
|
124 |
ret_story_state = current_story_obj
|
125 |
ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
|
126 |
+
ret_latest_image = None
|
127 |
+
ret_latest_narrative_md_obj = gr.Markdown(value="## Processing...\nNarrative being woven...")
|
128 |
+
ret_status_bar_html_obj = gr.HTML(value="<p class='processing_text status_text'>Processing...</p>")
|
129 |
+
# ret_log_md will be built up
|
130 |
|
131 |
+
# Initial yield for UI updates (buttons disabled by .then() chain)
|
|
|
132 |
yield {
|
133 |
+
output_status_bar: gr.HTML(value=f"<p class='processing_text status_text'>π Weaving Scene {current_story_obj.current_scene_number + 1}...</p>"),
|
134 |
+
output_latest_scene_image: gr.Image(value=create_placeholder_image("π¨ Conjuring visuals...")),
|
135 |
+
output_latest_scene_narrative: gr.Markdown(value=" Musing narrative..."),
|
136 |
output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator))
|
137 |
}
|
138 |
|
|
|
152 |
text_response = None
|
153 |
if text_model_info["type"] == "gemini": text_response = generate_text_gemini(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
|
154 |
elif text_model_info["type"] == "hf_text": text_response = generate_text_hf(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
|
155 |
+
if text_response and text_response.success: narrative_text_generated = basic_text_cleanup(text_response.text); log_accumulator.append(f" Narrative: Success.")
|
156 |
+
elif text_response: narrative_text_generated = f"**Narrative Error ({text_model_key}):** {text_response.error}"; log_accumulator.append(f" Narrative: FAILED - {text_response.error}")
|
157 |
+
else: log_accumulator.append(f" Narrative: FAILED - No response object from {text_model_key}.")
|
158 |
+
else: narrative_text_generated = "**Narrative Error:** Selected text model not available or misconfigured."; log_accumulator.append(f" Narrative: FAILED - Model '{text_model_key}' not available.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
|
160 |
ret_latest_narrative_str_content = f"## Scene Idea: {scene_prompt_text}\n\n{narrative_text_generated}"
|
161 |
+
ret_latest_narrative_md_obj = gr.Markdown(value=ret_latest_narrative_str_content) # Update the component object
|
162 |
yield { output_latest_scene_narrative: ret_latest_narrative_md_obj,
|
163 |
output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
|
164 |
|
|
|
166 |
progress(0.5, desc="π¨ Conjuring visuals...")
|
167 |
image_generated_pil = None
|
168 |
image_generation_error_message = None
|
169 |
+
selected_image_provider_key_from_ui = image_provider_key # The key from the dropdown
|
170 |
+
selected_image_provider_type = IMAGE_PROVIDERS.get(selected_image_provider_key_from_ui) # The value (e.g., "hf_sdxl_base")
|
171 |
+
|
172 |
image_content_prompt_for_gen = narrative_text_generated if narrative_text_generated and "Error" not in narrative_text_generated else scene_prompt_text
|
173 |
quality_keyword = "ultra detailed, intricate, masterpiece, " if image_quality == "High Detail" else ("concept sketch, line art, " if image_quality == "Sketch Concept" else "")
|
174 |
full_image_prompt = format_image_generation_prompt(quality_keyword + image_content_prompt_for_gen[:350], image_style_dropdown, artist_style_text)
|
175 |
+
log_accumulator.append(f" Image: Using provider key '{selected_image_provider_key_from_ui}' (maps to type '{selected_image_provider_type}'). Style: {image_style_dropdown}.")
|
176 |
|
177 |
if selected_image_provider_type and selected_image_provider_type != "none":
|
178 |
+
if not HF_IMAGE_IS_READY and selected_image_provider_type.startswith("hf_"): # Check if HF image specifically is ready
|
179 |
+
image_generation_error_message = "**Image Error:** Hugging Face Image API not configured (check STORYVERSE_HF_TOKEN)."
|
180 |
+
log_accumulator.append(f" Image: FAILED - {image_generation_error_message}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
else:
|
182 |
+
image_response = None
|
183 |
+
hf_model_id_to_call = None
|
184 |
+
if selected_image_provider_type == "hf_sdxl_base": hf_model_id_to_call = "stabilityai/stable-diffusion-xl-base-1.0"
|
185 |
+
elif selected_image_provider_type == "hf_openjourney": hf_model_id_to_call = "prompthero/openjourney"; img_width, img_height = 512, 512
|
186 |
+
elif selected_image_provider_type == "hf_sd_1_5": hf_model_id_to_call = "runwayml/stable-diffusion-v1-5"; img_width, img_height = 512, 512
|
187 |
+
# Add other specific HF model mappings here
|
188 |
+
|
189 |
+
if hf_model_id_to_call: # If it's an HF model type
|
190 |
+
image_response = generate_image_hf_model(
|
191 |
+
full_image_prompt, model_id=hf_model_id_to_call,
|
192 |
+
negative_prompt=negative_prompt_text or COMMON_NEGATIVE_PROMPTS,
|
193 |
+
width=img_width if 'img_width' in locals() else 768,
|
194 |
+
height=img_height if 'img_height' in locals() else 768
|
195 |
+
)
|
196 |
+
# Add elif for other providers like StabilityAI or DALL-E if you re-enable them
|
197 |
+
# elif selected_image_provider_type == "stability_ai": ...
|
198 |
+
else:
|
199 |
+
image_generation_error_message = f"**Image Error:** Provider type '{selected_image_provider_type}' not handled."
|
200 |
+
log_accumulator.append(f" Image: FAILED - {image_generation_error_message}")
|
201 |
+
|
202 |
+
if image_response and image_response.success:
|
203 |
+
image_generated_pil = image_response.image
|
204 |
+
log_accumulator.append(f" Image: Success from {image_response.provider} (Model: {image_response.model_id_used}).")
|
205 |
+
elif image_response:
|
206 |
+
image_generation_error_message = f"**Image Error ({image_response.provider} - {image_response.model_id_used}):** {image_response.error}"
|
207 |
+
log_accumulator.append(f" Image: FAILED - {image_response.error}")
|
208 |
+
elif not image_generation_error_message: # If no response and no specific error yet
|
209 |
+
image_generation_error_message = f"**Image Error:** No response from image service for {selected_image_provider_key_from_ui}."
|
210 |
+
log_accumulator.append(f" Image: FAILED - No response object.")
|
211 |
else:
|
212 |
+
image_generation_error_message = "**Image Error:** No image provider selected or configured."
|
213 |
+
log_accumulator.append(f" Image: FAILED - Provider key '{selected_image_provider_key_from_ui}' not found or type is 'none'.")
|
214 |
|
215 |
ret_latest_image = image_generated_pil if image_generated_pil else create_placeholder_image("Image Gen Failed", color="#401010")
|
216 |
yield { output_latest_scene_image: gr.Image(value=ret_latest_image),
|
|
|
227 |
narrative_text=narrative_text_generated if "**Narrative Error**" not in narrative_text_generated else "(Narrative gen failed)",
|
228 |
image=image_generated_pil,
|
229 |
image_style_prompt=f"{image_style_dropdown}{f', by {artist_style_text}' if artist_style_text and artist_style_text.strip() else ''}",
|
230 |
+
image_provider=selected_image_provider_key_from_ui, # Use the UI key for display
|
231 |
error_message=final_scene_error
|
232 |
)
|
233 |
ret_story_state = current_story_obj
|
|
|
251 |
log_accumulator.append(f"\n**UNEXPECTED RUNTIME ERROR:** {type(e).__name__} - {e}\n{traceback.format_exc()}")
|
252 |
ret_status_bar_html_obj = gr.HTML(value=f"<p class='error_text status_text'>β UNEXPECTED ERROR: {type(e).__name__}. Check logs.</p>")
|
253 |
ret_latest_narrative_md_obj = gr.Markdown(value=f"## Unexpected Error\n{type(e).__name__}: {e}\nSee log for details.")
|
254 |
+
|
255 |
+
# Final log update happens as part of the main return now
|
256 |
current_total_time = time.time() - start_time
|
257 |
log_accumulator.append(f" Cycle ended at {time.strftime('%H:%M:%S')}. Total time: {current_total_time:.2f}s")
|
258 |
ret_log_md = gr.Markdown(value="\n".join(log_accumulator))
|
259 |
|
260 |
+
# This is the FINAL return. It must be a tuple matching the `outputs` list of engage_button.click()
|
261 |
return (
|
262 |
+
ret_story_state,
|
263 |
+
ret_gallery,
|
264 |
+
ret_latest_image,
|
265 |
+
ret_latest_narrative_md_obj,
|
266 |
+
ret_status_bar_html_obj,
|
267 |
+
ret_log_md
|
268 |
)
|
269 |
|
270 |
def clear_story_state_ui_wrapper():
|
|
|
286 |
|
287 |
# --- Gradio UI Definition ---
|
288 |
with gr.Blocks(theme=omega_theme, css=omega_css, title="β¨ StoryVerse Omega β¨") as story_weaver_demo:
|
|
|
289 |
story_state_output = gr.State(Story())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
290 |
|
|
|
291 |
gr.Markdown("<div align='center'><h1>β¨ StoryVerse Omega β¨</h1>\n<h3>Craft Immersive Multimodal Worlds with AI</h3></div>")
|
292 |
gr.HTML("<div class='important-note'><strong>Welcome, Worldsmith!</strong> Describe your vision, choose your style, and let Omega help you weave captivating scenes with narrative and imagery. Ensure API keys (<code>STORYVERSE_...</code>) are correctly set in Space Secrets!</div>")
|
293 |
|
294 |
with gr.Accordion("π§ AI Services Status & Info", open=False):
|
295 |
+
status_text_list = []; text_llm_ok, image_gen_ok = (GEMINI_TEXT_IS_READY or HF_TEXT_IS_READY), (STABILITY_API_IS_READY or OPENAI_DALLE_IS_READY or HF_IMAGE_IS_READY)
|
296 |
if not text_llm_ok and not image_gen_ok: status_text_list.append("<p style='color:#FCA5A5;font-weight:bold;'>β οΈ CRITICAL: NO AI SERVICES CONFIGURED.</p>")
|
297 |
else:
|
298 |
if text_llm_ok: status_text_list.append("<p style='color:#A7F3D0;'>β
Text Generation Service(s) Ready.</p>")
|
|
|
308 |
scene_prompt_input = gr.Textbox(lines=7, label="Scene Vision (Description, Dialogue, Action):", placeholder="e.g., Amidst swirling cosmic dust...")
|
309 |
with gr.Row(elem_classes=["compact-row"]):
|
310 |
with gr.Column(scale=2):
|
311 |
+
image_style_input = gr.Dropdown(choices=["Default (Cinematic Realism)"] + sorted(list(STYLE_PRESETS.keys())), value="Default (Cinematic Realism)", label="Visual Style Preset", allow_custom_value=True)
|
312 |
with gr.Column(scale=2):
|
313 |
artist_style_input = gr.Textbox(label="Artistic Inspiration (Optional):", placeholder="e.g., Moebius...")
|
314 |
negative_prompt_input = gr.Textbox(lines=2, label="Exclude from Image (Negative Prompt):", value=COMMON_NEGATIVE_PROMPTS)
|