kaiser-data commited on
Commit
44d09c8
Β·
verified Β·
1 Parent(s): 0dadc4e

Instant arrival: hero image + options + download

Browse files
Files changed (1) hide show
  1. app/app.py +55 -8
app/app.py CHANGED
@@ -29,9 +29,16 @@ _IMG_POOL = concurrent.futures.ThreadPoolExecutor(max_workers=2)
29
  # zero latency, zero cold-start. Activates only if a (CC0/royalty-free) file is
30
  # present, so the app never breaks without it. Drop one at assets/dream-ambient.mp3.
31
  import os as _os # noqa: E402
32
- MUSIC_PATH = str(pathlib.Path(__file__).resolve().parent.parent / "assets" / "dream-ambient.mp3")
 
33
  HAS_MUSIC = _os.path.exists(MUSIC_PATH)
34
 
 
 
 
 
 
 
35
  SPEAKER = {
36
  "Dreamweaver": "🌌 *Dreamweaver*",
37
  "Nightmare": "πŸ‘ *Nightmare*",
@@ -106,6 +113,32 @@ def card_md() -> str:
106
  )
107
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  def _btn_updates():
110
  ups = []
111
  for i in range(NBTN):
@@ -143,7 +176,9 @@ def _stream_turn(intent, tier, history):
143
  """Shared streaming loop -> [chatbot, state, btn1..N, intent, card, printbtn, dream]."""
144
  history = history or []
145
  history.append({"role": "user", "content": f"πŸ§‘β€πŸš€ {intent}"})
146
- yield history, state_md(), *_hidden_btns(), "", *_card_updates(), gr.update()
 
 
147
 
148
  current = None
149
  die_shown = False
@@ -156,12 +191,12 @@ def _stream_turn(intent, tier, history):
156
  if not die_shown and engine.last_outcome is not None:
157
  die_shown = True # reveal the seeded roll the instant it's decided
158
  history.append(_die_bubble())
159
- yield history, gr.update(), *_hidden_btns(), "", *_card_updates(), gr.update()
160
  if speaker != current:
161
  current = speaker
162
  history.append({"role": "assistant", "content": SPEAKER.get(speaker, speaker) + ": "})
163
  history[-1]["content"] += delta
164
- yield history, gr.update(), *_hidden_btns(), "", *_card_updates(), gr.update()
165
 
166
  # Show the gambit buttons the INSTANT narration + Hobbes are done β€” don't make
167
  # the player wait on the slower image. (Buttons in ~8s, not ~17s.)
@@ -181,9 +216,18 @@ def _stream_turn(intent, tier, history):
181
  def begin(env_id, seed, history):
182
  engine.start(env_id, seed=(seed or "dream").strip())
183
  env = ENVIRONMENTS[env_id]
184
- history = [{"role": "assistant", "content": f"{env.emoji} *{env.opening}*"}]
185
- # Clear the prior dream's image on a fresh start.
186
- yield history, state_md(), *_hidden_btns(), "", *_card_updates(), gr.update(value=None, visible=False)
 
 
 
 
 
 
 
 
 
187
  yield from _stream_turn("(You arrive and take in the scene.)", None, history)
188
 
189
 
@@ -285,7 +329,9 @@ with gr.Blocks(title="DAYDREAM") as demo:
285
  scale=8, show_label=False, autofocus=True)
286
  go = gr.Button("Do it", variant="primary", scale=1)
287
  card = gr.Markdown(visible=False, elem_id="card")
288
- printbtn = gr.Button("πŸ“Έ Freeze the dream card", visible=False)
 
 
289
  with gr.Column(scale=2):
290
  dream_img = gr.Image(label="🌌 The dream", height=380, visible=False,
291
  interactive=False, elem_id="dream", show_label=False)
@@ -304,6 +350,7 @@ with gr.Blocks(title="DAYDREAM") as demo:
304
  for i, b in enumerate(btns):
305
  b.click(make_choose(i), [chat], outs)
306
  printbtn.click(lambda: gr.update(visible=True), None, card)
 
307
 
308
 
309
  if __name__ == "__main__":
 
29
  # zero latency, zero cold-start. Activates only if a (CC0/royalty-free) file is
30
  # present, so the app never breaks without it. Drop one at assets/dream-ambient.mp3.
31
  import os as _os # noqa: E402
32
+ _ASSETS = pathlib.Path(__file__).resolve().parent.parent / "assets"
33
+ MUSIC_PATH = str(_ASSETS / "dream-ambient.mp3")
34
  HAS_MUSIC = _os.path.exists(MUSIC_PATH)
35
 
36
+ # Pre-generated world hero images shown the INSTANT you Begin β€” so the world is
37
+ # never empty while the live per-beat image generates. Maps env_id -> path | None.
38
+ def _world_hero(env_id: str):
39
+ p = _ASSETS / "worlds" / f"{env_id}.png"
40
+ return str(p) if p.exists() else None
41
+
42
  SPEAKER = {
43
  "Dreamweaver": "🌌 *Dreamweaver*",
44
  "Nightmare": "πŸ‘ *Nightmare*",
 
113
  )
114
 
115
 
116
+ def build_story_md(history) -> str:
117
+ """The player's full dream as a keepsake markdown β€” every beat + the journal."""
118
+ s = engine.state
119
+ title = f"# πŸŒ™ DAYDREAM β€” {s.env_name if s else 'a dream'}\n"
120
+ meta = (f"*Seed `{s.seed}` Β· {s.turn} turns Β· "
121
+ f"dreamed by a fleet of small models*\n\n" if s else "")
122
+ out = [title, meta, "---\n"]
123
+ for m in (history or []):
124
+ c = (m.get("content") or "").strip()
125
+ if c:
126
+ out.append(c + "\n")
127
+ if s and s.over:
128
+ out.append("\n---\n\n" + card_md())
129
+ out.append("\n\n*Made with DAYDREAM Β· small models, big dreams πŸŒ™*")
130
+ return "\n".join(out)
131
+
132
+
133
+ def download_story(history):
134
+ """Write the story to a temp .md and hand the path to the DownloadButton."""
135
+ import tempfile
136
+ seed = engine.state.seed if engine.state else "dream"
137
+ path = pathlib.Path(tempfile.gettempdir()) / f"daydream-{seed}.md"
138
+ path.write_text(build_story_md(history), encoding="utf-8")
139
+ return str(path)
140
+
141
+
142
  def _btn_updates():
143
  ups = []
144
  for i in range(NBTN):
 
176
  """Shared streaming loop -> [chatbot, state, btn1..N, intent, card, printbtn, dream]."""
177
  history = history or []
178
  history.append({"role": "user", "content": f"πŸ§‘β€πŸš€ {intent}"})
179
+ # Keep the gambit buttons VISIBLE and stable through the whole turn (no flicker).
180
+ # Gradio queues clicks, so a tap mid-stream just runs as the next turn.
181
+ yield history, state_md(), *_btn_updates(), "", *_card_updates(), gr.update()
182
 
183
  current = None
184
  die_shown = False
 
191
  if not die_shown and engine.last_outcome is not None:
192
  die_shown = True # reveal the seeded roll the instant it's decided
193
  history.append(_die_bubble())
194
+ yield history, gr.update(), *_btn_updates(), "", *_card_updates(), gr.update()
195
  if speaker != current:
196
  current = speaker
197
  history.append({"role": "assistant", "content": SPEAKER.get(speaker, speaker) + ": "})
198
  history[-1]["content"] += delta
199
+ yield history, gr.update(), *_btn_updates(), "", *_card_updates(), gr.update()
200
 
201
  # Show the gambit buttons the INSTANT narration + Hobbes are done β€” don't make
202
  # the player wait on the slower image. (Buttons in ~8s, not ~17s.)
 
216
  def begin(env_id, seed, history):
217
  engine.start(env_id, seed=(seed or "dream").strip())
218
  env = ENVIRONMENTS[env_id]
219
+ history = [{"role": "assistant", "content": f"{env.emoji} *{env.opening}*"},
220
+ {"role": "assistant",
221
+ "content": "🎲 **Your move** β€” pick a gambit below (riskier = bigger "
222
+ "reward, bigger chance to fail), or type your own action."}]
223
+ # Show the world's hero image + three gambit options INSTANTLY, so the world is
224
+ # never empty and you always know what to do. The live narration, Hobbes' own
225
+ # creative labels, and the per-beat image all layer in over the next few seconds.
226
+ engine.gambits = engine._make_gambits([]) # default labels, upgraded by Hobbes
227
+ hero = _world_hero(env_id)
228
+ hero_up = (gr.update(value=hero, visible=True) if hero
229
+ else gr.update(value=None, visible=False))
230
+ yield history, state_md(), *_btn_updates(), "", *_card_updates(), hero_up
231
  yield from _stream_turn("(You arrive and take in the scene.)", None, history)
232
 
233
 
 
329
  scale=8, show_label=False, autofocus=True)
330
  go = gr.Button("Do it", variant="primary", scale=1)
331
  card = gr.Markdown(visible=False, elem_id="card")
332
+ with gr.Row():
333
+ printbtn = gr.Button("πŸ“Έ Freeze the dream card", visible=False)
334
+ dlbtn = gr.DownloadButton("πŸ“₯ Download my story", size="sm")
335
  with gr.Column(scale=2):
336
  dream_img = gr.Image(label="🌌 The dream", height=380, visible=False,
337
  interactive=False, elem_id="dream", show_label=False)
 
350
  for i, b in enumerate(btns):
351
  b.click(make_choose(i), [chat], outs)
352
  printbtn.click(lambda: gr.update(visible=True), None, card)
353
+ dlbtn.click(download_story, [chat], dlbtn) # export the full dream as markdown
354
 
355
 
356
  if __name__ == "__main__":