Spaces:
Running on Zero
Running on Zero
File size: 19,606 Bytes
9328e91 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | """
Narrative Engine Tab - Layered storytelling with mystery iceberg.
Uses the Narrative Engine for generating narratives with:
- 5-layer mystery iceberg (surface → abyss)
- Character generation with psychological depth
- Demo stories across genres
- Custom narrative creation with mystery layers
"""
import gradio as gr
import json
from typing import Dict, Any, List
# Import narrative engine components
from narrativeengine_hfport import (
NarrativeEngine, NarrativeMode, NodeType, DepthLevel,
get_demo, list_demos, DEMO_STORIES,
CharacterGenerator, GeneratedCharacter
)
def format_story_setup(engine: NarrativeEngine) -> str:
"""Format story setup as markdown."""
if not engine.story_setup:
return "No story setup available."
setup = engine.story_setup
return f"""## {setup.title}
### Setting
**{setup.place['name']}**: {setup.place['description']}
**Key Locations**: {', '.join(setup.place.get('key_locations', []))}
### Time
{setup.time['period']} - {setup.time['specific_moment']}
*{setup.time['tension']}*
### Protagonist
**{setup.protagonist['name']}**: {setup.protagonist['description']}
- **Motivation**: {setup.protagonist['motivation']}
- **Flaw**: {setup.protagonist['flaw']}
### Hook
> {setup.hook}
### Stakes
{setup.stakes}
"""
def format_characters(engine: NarrativeEngine) -> str:
"""Format characters as markdown."""
if not engine.characters:
return "No characters registered."
lines = ["## Characters\n"]
for char_id, char in engine.characters.items():
lines.append(f"### {char.name}")
lines.append(f"**Role**: {char.role}")
lines.append(f"**Background**: {char.background}")
if char.wants:
lines.append("\n**Wants**:")
for want_id, want in char.wants.items():
lines.append(f"- {want['description']}")
lines.append("")
return "\n".join(lines)
def format_clues(engine: NarrativeEngine) -> str:
"""Format story clues as markdown."""
if not engine.story_clues:
return "No clues planted."
lines = ["## Story Clues\n"]
for clue in sorted(engine.story_clues, key=lambda c: c.planted_at):
visibility = "Obvious" if clue.noticeability > 0.6 else "Subtle" if clue.noticeability > 0.3 else "Hidden"
lines.append(f"- **[{clue.clue_type.upper()}]** ({visibility}) {clue.content}")
return "\n".join(lines)
def format_iceberg(iceberg: Dict[str, Any]) -> str:
"""Format iceberg layers as markdown."""
if not iceberg:
return "No iceberg data available."
lines = ["## Narrative Iceberg\n"]
emoji_map = {
"surface": "Surface",
"shallow": "Shallow",
"mid": "Mid-depth",
"deep": "Deep",
"abyss": "Abyss"
}
for level, data in iceberg.items():
label = emoji_map.get(level, level.title())
lines.append(f"### {label}: {data['name']}")
for item in data['content'][:5]:
lines.append(f"- {item}")
if len(data['content']) > 5:
lines.append(f"- *... and {len(data['content']) - 5} more*")
lines.append("")
return "\n".join(lines)
def format_generated_character(char: GeneratedCharacter) -> Dict[str, Any]:
"""Format generated character as dict."""
return {
"id": char.id,
"name": char.name,
"role": char.role,
"background": char.background,
"skill": char.skill,
"trait": char.trait,
"equipment": char.equipment,
"context": char.context,
"psychological_profile": char.psychological_profile,
"personality_traits": char.personality_traits
}
def create_narrative_engine_tab():
"""Create the Narrative Engine tab."""
with gr.Tab("Narrative Engine"):
gr.Markdown("""## Narrative Engine - Layered Mystery & Character Systems
A comprehensive narrative generation system with 7 storytelling paradigms:
- **Struggle Propagation** - Character wants with butterfly effects
- **Mystery Iceberg** - 5-layer depth (Surface to Abyss)
- **Reveal Ripple Observer** - Information timing control
- **Emotional Mechanics** - Setup, Trigger, Sustain, Kill patterns
""")
with gr.Tabs():
# === TAB 1: DEMO STORIES ===
with gr.TabItem("Demo Stories"):
gr.Markdown("### Pre-built Narrative Demonstrations")
gr.Markdown("Explore 6 complete story structures with mystery layers, character dynamics, and planted clues.")
with gr.Row():
demo_dropdown = gr.Dropdown(
choices=[
("The Hartfield Affair (Political Thriller)", "hartfield"),
("The Vanishing Code (Sci-Fi)", "scifi"),
("The Last Performance (Romance/Drama)", "romance"),
("The Gilded Cage (Heist)", "heist"),
("The Hollow Season (Folk Horror)", "horror"),
("The Redemption (Classic Arc)", "redemption")
],
label="Select Demo Story",
value="hartfield"
)
demo_btn = gr.Button("Generate Story", variant="primary")
with gr.Row():
with gr.Column():
demo_narrative = gr.Markdown(label="Story Narrative")
with gr.Column():
demo_iceberg = gr.Markdown(label="Narrative Iceberg")
demo_json = gr.JSON(label="Full Story Data")
def run_demo_story(story_id: str) -> tuple:
"""Run a demo story."""
try:
engine = get_demo(story_id)
# Execute sample actions
action_map = {
"hartfield": [("detective", "acquire", "letter"), ("mayor", "pressure", "testimony")],
"scifi": [("engineer", "reveal", "manifest"), ("ai", "compromise", "escape_pods")],
"romance": [("isabella", "reveal", "letters"), ("lucia", "acquire", "role")],
"heist": [("saint", "acquire", "security"), ("viktor", "reveal", "painting")],
"horror": [("rowan", "reveal", "ritual"), ("agnes", "pressure", "mark")],
"redemption": [("hero", "reveal", "soul"), ("emperor", "pressure", "darkside")]
}
for char, action, target in action_map.get(story_id, []):
engine.execute_action(char, action, target)
iceberg = engine.generate_full_iceberg()
setup_md = format_story_setup(engine)
chars_md = format_characters(engine)
clues_md = format_clues(engine)
iceberg_md = format_iceberg(iceberg)
narrative_md = f"{setup_md}\n\n---\n\n{chars_md}\n\n---\n\n{clues_md}"
full_data = engine.to_dict()
return narrative_md, iceberg_md, full_data
except Exception as e:
error_msg = f"Error: {str(e)}"
return error_msg, error_msg, {"error": str(e)}
demo_btn.click(
fn=run_demo_story,
inputs=[demo_dropdown],
outputs=[demo_narrative, demo_iceberg, demo_json]
)
# === TAB 2: CHARACTER GENERATOR ===
with gr.TabItem("Character Generator"):
gr.Markdown("### Generate Characters with Psychological Depth")
with gr.Row():
char_context = gr.Dropdown(
choices=[
("Detective/Mystery", "detective"),
("Action/Adventure", "action"),
("Horror", "horror"),
("Fantasy", "fantasy"),
("Sci-Fi", "sci_fi"),
("Romance", "romance"),
("Spy/Espionage", "spy"),
("Crime/Heist", "crime")
],
label="Character Context",
value="detective"
)
char_count = gr.Slider(
minimum=1, maximum=10, value=3, step=1,
label="Number of Characters"
)
char_btn = gr.Button("Generate Characters", variant="primary")
char_output = gr.JSON(label="Generated Characters")
def generate_characters(context: str, count: int) -> Dict[str, Any]:
"""Generate characters."""
try:
generator = CharacterGenerator(context)
characters = generator.generate_multiple(int(count))
return {
"context": context,
"count": len(characters),
"characters": [format_generated_character(c) for c in characters]
}
except Exception as e:
return {"error": str(e)}
char_btn.click(
fn=generate_characters,
inputs=[char_context, char_count],
outputs=[char_output]
)
# === TAB 3: CUSTOM NARRATIVE ===
with gr.TabItem("Custom Narrative"):
gr.Markdown("### Create Your Own Narrative Structure")
with gr.Row():
with gr.Column():
custom_title = gr.Textbox(label="Story Title", value="Untitled Story")
custom_setting = gr.Textbox(label="Setting", value="A city on the edge of change")
custom_mode = gr.Dropdown(
choices=[
"Struggle (Plot-driven)",
"Compressed (Emotional density)",
"Meditative (Presence-based)",
"Hybrid (Combined)"
],
label="Narrative Mode",
value="Struggle (Plot-driven)"
)
with gr.Column():
gr.Markdown("**Protagonist**")
protag_name = gr.Textbox(label="Name", value="Alex")
protag_want = gr.Textbox(label="What they want", value="To find the truth")
protag_struggle = gr.Textbox(label="Their struggle", value="Haunted by the past")
with gr.Column():
gr.Markdown("**Antagonist**")
antag_name = gr.Textbox(label="Name", value="Morgan")
antag_want = gr.Textbox(label="What they want", value="To keep secrets buried")
custom_btn = gr.Button("Generate Narrative", variant="primary")
with gr.Row():
custom_narrative = gr.Markdown(label="Generated Narrative")
custom_iceberg = gr.Markdown(label="Iceberg Layers")
custom_json = gr.JSON(label="Full Data")
def create_custom_narrative(title, protag_name_val, protag_want_val, protag_struggle_val,
antag_name_val, antag_want_val, setting, mode) -> tuple:
"""Create custom narrative."""
try:
engine = NarrativeEngine()
mode_map = {
"Struggle (Plot-driven)": NarrativeMode.STRUGGLE,
"Compressed (Emotional density)": NarrativeMode.COMPRESSED,
"Meditative (Presence-based)": NarrativeMode.MEDITATIVE,
"Hybrid (Combined)": NarrativeMode.HYBRID
}
engine.set_mode(mode_map.get(mode, NarrativeMode.STRUGGLE))
engine.register_character("protagonist", protag_name_val, "protagonist")
engine.register_character("antagonist", antag_name_val, "antagonist")
engine.add_want("protagonist", "main_goal", protag_want_val)
engine.add_want("antagonist", "main_goal", antag_want_val)
engine.create_node("objective", NodeType.RESOURCE, "The Objective",
description=f"What both {protag_name_val} and {antag_name_val} seek")
engine.create_node("setting", NodeType.LOCATION, setting)
engine.create_node("deadline", NodeType.TIME_WINDOW, "The Deadline",
metadata={"remaining_time": 24, "unit": "hours"})
engine.connect_nodes("objective", "setting")
engine.connect_nodes("deadline", "objective")
engine.execute_action("protagonist", "reveal", "objective",
narrative_context=protag_struggle_val)
engine.execute_action("antagonist", "pressure", "objective",
narrative_context="opposing the protagonist")
iceberg = engine.generate_full_iceberg()
narrative_lines = [f"# {title}\n"]
narrative_lines.append(f"**Setting**: {setting}\n")
narrative_lines.append(f"**Mode**: {mode}\n")
narrative_lines.append("\n## Characters\n")
narrative_lines.append(f"**{protag_name_val}** (Protagonist)")
narrative_lines.append(f"- Want: {protag_want_val}")
narrative_lines.append(f"- Struggle: {protag_struggle_val}\n")
narrative_lines.append(f"**{antag_name_val}** (Antagonist)")
narrative_lines.append(f"- Want: {antag_want_val}\n")
narrative_lines.append("\n## Narrative Events\n")
for entry in engine.narrative_log:
narrative_lines.append(f"- {entry['surface_event']}")
narrative_md = "\n".join(narrative_lines)
iceberg_md = format_iceberg(iceberg)
return narrative_md, iceberg_md, engine.to_dict()
except Exception as e:
error_msg = f"Error: {str(e)}"
return error_msg, error_msg, {"error": str(e)}
custom_btn.click(
fn=create_custom_narrative,
inputs=[custom_title, protag_name, protag_want, protag_struggle,
antag_name, antag_want, custom_setting, custom_mode],
outputs=[custom_narrative, custom_iceberg, custom_json]
)
# === TAB 4: MYSTERY LAYERS ===
with gr.TabItem("Mystery Layers"):
gr.Markdown("### Add Mystery Depth to Any Event")
gr.Markdown("Transform a simple surface event into layered narrative with multiple interpretations, hidden connections, and unresolved threads.")
with gr.Row():
with gr.Column():
mystery_event = gr.Textbox(
label="Surface Event",
value="The mayor suddenly resigns from office",
lines=2
)
with gr.Row():
mystery_char1 = gr.Textbox(label="Character 1", value="The Mayor")
mystery_char2 = gr.Textbox(label="Character 2", value="The Reporter")
mystery_interps = gr.Slider(
minimum=2, maximum=5, value=3, step=1,
label="Number of Interpretations"
)
mystery_btn = gr.Button("Add Mystery Layers", variant="primary")
mystery_output = gr.JSON(label="Layered Event")
def add_mystery_layers(surface_event, char1, char2, num_interps) -> Dict[str, Any]:
"""Add mystery layers to event."""
try:
engine = NarrativeEngine()
engine.register_character("char1", char1)
engine.register_character("char2", char2)
layered = engine.mystery.add_mystery_layers(
surface_event,
character_ids=["char1", "char2"],
num_interpretations=int(num_interps),
include_hidden_connection=True,
include_unresolved_thread=True
)
iceberg = engine.mystery.generate_iceberg_summary(layered.id)
return {
"surface_event": surface_event,
"characters": [char1, char2],
"interpretations": [
{
"reading": i.reading,
"description": i.description,
"plausibility": round(i.plausibility, 2),
"darkness_level": round(i.darkness_level, 2)
} for i in layered.interpretations
],
"evidence": [
{
"type": e.evidence_type,
"content": e.content,
"is_red_herring": e.is_red_herring
} for e in layered.evidence
],
"hidden_connections": [
{
"type": c.connection_type,
"description": c.description
} for c in layered.hidden_connections
],
"unresolved_threads": [
{
"element": t.element,
"speculation_hooks": t.speculation_hooks
} for t in layered.unresolved_threads
],
"iceberg": iceberg
}
except Exception as e:
return {"error": str(e)}
mystery_btn.click(
fn=add_mystery_layers,
inputs=[mystery_event, mystery_char1, mystery_char2, mystery_interps],
outputs=[mystery_output]
)
gr.Markdown("""
---
**Narrative Engine** integrates 7 storytelling paradigms for rich, layered narratives.
All generation is deterministic - no external APIs required.
""")
|