Spaces:
Runtime error
Runtime error
File size: 6,111 Bytes
8909bb1 |
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 |
"""
bedtime_stories_gradio.py Β· interactive 3-scene bedtime stories with emojis π
"""
import os, re
import openai
import gradio as gr
from enrich_idea import generate_enriched_idea # prompt-enricher
# ---------- CONFIG ---------------------------------------------------------
openai.api_key = os.getenv("OPENAI_API_KEY")
MODEL = "gpt-4o-mini"
SCENE_TEMPLATE = """
You are a childrenβs storyteller. Write **SCENE {scene_no}/3** of an
age-5-to-10 bedtime story (β 150 words).
The childβs idea is: **β{idea}β**.
π *Make sure this idea is central to the scene and appears within the first two sentences.*
### Style rules
1. Use vivid, friendly language and **plenty of relevant emojis** (πππͺππ etc.).
2. Keep sentences short and clear.
3. Leave a blank line between paragraphs.
4. **Scenes 1 & 2:** end with *exactly two* numbered choices (β1.β and β2.β).
5. **Scene 3:** wrap up the tale (no choices). Do **not** write βThe end.β in scenes 1 or 2.
Story so far:
\"\"\"{story_so_far}\"\"\"
`last_choice` = "{last_choice}"
If `last_choice` == "N/A", this is the opening scene; otherwise, nod to the childβs last choice in one friendly sentence before continuing.
"""
# ---------- LLM HELPERS ----------------------------------------------------
def call_llm(prompt: str) -> str:
resp = openai.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
temperature=0.4,
max_tokens=350,
)
return resp.choices[0].message.content.strip()
def generate_scene(scene_no: int,
story_so_far: str,
last_choice: str,
idea: str) -> str:
prompt = SCENE_TEMPLATE.format(
scene_no=scene_no,
story_so_far=story_so_far,
last_choice=last_choice,
idea=idea,
)
return call_llm(prompt)
# ---------- UTILS ----------------------------------------------------------
def _strip_early_ending(text: str, scene_no: int) -> str:
if scene_no < 3:
text = re.sub(r"^\s*(The\s+end\.?)\s*$", "", text, flags=re.I | re.M)
return text
def _extract_options(scene_text: str):
lines = [ln.strip() for ln in scene_text.splitlines()]
opts = [ln for ln in lines if ln.startswith(("1.", "2."))]
return opts if len(opts) == 2 else ["1. Continue bravely π", "2. Take a quiet turn π€"]
def _maybe_enrich(raw: str) -> str:
if len(raw.split()) < 4:
try:
return generate_enriched_idea(raw)
except Exception as e:
print("Enrich failed:", e)
return raw
return raw
# ---------- GRADIO CALLBACKS ----------------------------------------------
def start_story(child_prompt, state):
raw = child_prompt.strip()
if not raw:
return (gr.update(value="π *Please type a story idea first!*"),
*(gr.update(visible=False),)*3, state)
idea = _maybe_enrich(raw)
scene1 = generate_scene(1, "", "N/A", idea)
scene1 = _strip_early_ending(scene1, 1)
state = {"scene_no": 1, "story": scene1, "idea": idea}
opt1, opt2 = _extract_options(scene1)
return (gr.update(value=scene1),
gr.update(value=opt1, visible=True),
gr.update(value=opt2, visible=True),
gr.update(visible=False), state)
def choose(option_text, state):
scene_no = state["scene_no"] + 1
combined_story = (state["story"] +
f"\n\nπ² **You chose:** {option_text}\n\n")
new_scene = generate_scene(scene_no, state["story"], option_text, state["idea"])
new_scene = _strip_early_ending(new_scene, scene_no)
combined_story += new_scene
state.update({"scene_no": scene_no, "story": combined_story})
if scene_no < 3:
opt1, opt2 = _extract_options(new_scene)
return (gr.update(value=combined_story),
gr.update(value=opt1, visible=True),
gr.update(value=opt2, visible=True),
gr.update(visible=False), state)
else:
ending = combined_story + "\n\nπ **The End!** π"
return (gr.update(value=ending),
*(gr.update(visible=False),)*2,
gr.update(visible=True), state)
def new_story(state):
state = {"scene_no": 0, "story": "", "idea": ""}
return (gr.update(value=""),
*(gr.update(visible=False),)*3, state)
# ---------- UI -------------------------------------------------------------
with gr.Blocks(title="π Interactive Bedtime Stories") as demo:
gr.Markdown("## π **Interactive Bedtime Stories**\n"
"Describe an adventure, then guide the tale with your choices!")
story_state = gr.State({"scene_no": 0, "story": "", "idea": ""})
with gr.Row():
prompt_box = gr.Textbox(
label="β¨ Your story idea",
placeholder="e.g. A friendly dragon who loves cookies...",
lines=1,
)
start_btn = gr.Button("π Start Story", variant="primary")
story_display = gr.Markdown()
with gr.Row():
choice_btn1 = gr.Button(visible=False)
choice_btn2 = gr.Button(visible=False)
restart_btn = gr.Button("π Tell me another story!", visible=False)
start_btn.click(start_story,
inputs=[prompt_box, story_state],
outputs=[story_display, choice_btn1, choice_btn2, restart_btn, story_state])
choice_btn1.click(choose,
inputs=[choice_btn1, story_state],
outputs=[story_display, choice_btn1, choice_btn2, restart_btn, story_state])
choice_btn2.click(choose,
inputs=[choice_btn2, story_state],
outputs=[story_display, choice_btn1, choice_btn2, restart_btn, story_state])
restart_btn.click(new_story,
inputs=[story_state],
outputs=[story_display, choice_btn1, choice_btn2, restart_btn, story_state])
# --------------------------------------------------------------------------
if __name__ == "__main__":
demo.launch() |