byte-vortex commited on
Commit
0930a4b
Β·
verified Β·
1 Parent(s): 5c0d199

Deploy Myco from CI

Browse files
Files changed (2) hide show
  1. game/engine.py +8 -37
  2. ui/gradio_app.py +13 -35
game/engine.py CHANGED
@@ -44,7 +44,7 @@ def _get_model_and_tokenizer():
44
  _model = AutoModelForCausalLM.from_pretrained(
45
  model_id,
46
  token=token,
47
- dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
48
  device_map=DEVICE,
49
  trust_remote_code=True
50
  )
@@ -111,6 +111,13 @@ def _set_reply(text: str, tag: str = "chat"):
111
  _LAST_REPLY["text"] = "\n\n".join(_REPLY_LOG)
112
 
113
 
 
 
 
 
 
 
 
114
  # Shared store for the most recent raw LLM output, so the front end can
115
  # display it live for debugging (server-side prints aren't visible once deployed).
116
  _LAST_DEBUG = {"text": "πŸ„ Waiting for Myco's first thought..."}
@@ -911,39 +918,3 @@ def _llm_action(*args, **kwargs):
911
  """Action-mode LLM call. Delegates to the JSON-aware single-turn _llm()."""
912
  return _llm(*args, **kwargs)
913
 
914
-
915
- def narrate(event, score):
916
- prompt = f"""
917
- You are Myco, a mystical forest spirit inside a game.
918
-
919
- RULES:
920
- - Respond with EXACTLY ONE sentence.
921
- - Maximum 20 words.
922
- - No line breaks or lists.
923
-
924
- Event: {event}
925
- Score: {score}
926
- """
927
-
928
- inputs = _tokenizer(prompt, return_tensors="pt").to(_model.device)
929
-
930
- output = _model.generate(
931
- **inputs,
932
- max_new_tokens=60,
933
- temperature=0.9,
934
- do_sample=True,
935
- eos_token_id=_tokenizer.eos_token_id,
936
- pad_token_id=_tokenizer.eos_token_id,
937
- )
938
-
939
- text = _tokenizer.decode(
940
- output[0][inputs["input_ids"].shape[-1]:],
941
- skip_special_tokens=True
942
- ).strip()
943
-
944
- # hard enforce: single sentence + 20 words max
945
- text = re.split(r'[.!?]', text)[0].strip()
946
- text = " ".join(text.split()[:20])
947
-
948
- return text
949
-
 
44
  _model = AutoModelForCausalLM.from_pretrained(
45
  model_id,
46
  token=token,
47
+ torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
48
  device_map=DEVICE,
49
  trust_remote_code=True
50
  )
 
111
  _LAST_REPLY["text"] = "\n\n".join(_REPLY_LOG)
112
 
113
 
114
+ def get_myco_log() -> str:
115
+ """Return the full running Myco log (narrative/action/chat) β€” non-blocking,
116
+ intended to be polled by a gr.Timer rather than streamed from an infinite
117
+ generator (which can starve the Gradio queue for other events)."""
118
+ return _LAST_REPLY["text"]
119
+
120
+
121
  # Shared store for the most recent raw LLM output, so the front end can
122
  # display it live for debugging (server-side prints aren't visible once deployed).
123
  _LAST_DEBUG = {"text": "πŸ„ Waiting for Myco's first thought..."}
 
918
  """Action-mode LLM call. Delegates to the JSON-aware single-turn _llm()."""
919
  return _llm(*args, **kwargs)
920
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/gradio_app.py CHANGED
@@ -5,7 +5,6 @@ import json
5
  import copy
6
  import random
7
  import threading
8
- import time
9
  import logging
10
  from typing import Any, cast
11
  import gradio as gr
@@ -33,7 +32,7 @@ from ui.renderers import (
33
  )
34
  from ui.interface import render_shroom_tab
35
 
36
- from game.engine import _LAST_REPLY
37
 
38
  logger = logging.getLogger(__name__)
39
  EMPTY_COLLECTION = None
@@ -64,10 +63,7 @@ def _autonomous_tick(current, collection, active_mushrooms, position):
64
  with _ROAM_LOCK:
65
  x, y = _ROAM["position"]
66
  safe_mushrooms = list(active_mushrooms or [])
67
- coll = list(collection or [])
68
-
69
- # 1. Define the variable here, before any logic happens
70
- initial_coll_len = len(coll)
71
 
72
  # Check pickup at current position first
73
  if safe_mushrooms:
@@ -88,10 +84,6 @@ def _autonomous_tick(current, collection, active_mushrooms, position):
88
  nx, ny, current, coll, safe_mushrooms
89
  )
90
 
91
- # 2. Now you can safely use it here because it was defined above
92
- sound_to_play = "./assets/sounds/mushroom_pickup.wav" if len(coll) > initial_coll_len else None
93
- print(f"DEBUG: Attempting to play sound: {sound_to_play}")
94
-
95
  return (
96
  list(pos),
97
  forest_scene(current, coll, pos, active_mushrooms=safe_mushrooms),
@@ -103,7 +95,6 @@ def _autonomous_tick(current, collection, active_mushrooms, position):
103
  gr.update(),
104
  dex_markdown(coll),
105
  progress_markdown(coll),
106
- sound_to_play, # <-- Ensure this is in your return statement
107
  )
108
 
109
  # ---------------------------------------------------------------------------
@@ -304,19 +295,12 @@ def move_player(position, direction, current, collection, active_mushrooms):
304
 
305
  safe_mushrooms = list(active_mushrooms or [])
306
  coll = list(collection or [])
307
-
308
- # --- FIX: Initialize this BEFORE calling check_auto_pickup ---
309
- initial_coll_len = len(coll)
310
- # -----------------------------------------------------------
311
 
312
- # Collision check
313
  current, coll, safe_mushrooms = check_auto_pickup(
314
  x, y, current, coll, safe_mushrooms
315
  )
316
 
317
- # Now compare it to the length after the pickup
318
- sound_to_play = "./assets/sounds/mushroom_pickup.wav" if len(coll) > initial_coll_len else "assets/sounds/bot_roam.wav"
319
-
320
  # Update roam tracker
321
  with _ROAM_LOCK:
322
  _ROAM["position"] = [x, y]
@@ -333,7 +317,6 @@ def move_player(position, direction, current, collection, active_mushrooms):
333
  gr.update(), # leave chat untouched
334
  dex_markdown(coll),
335
  progress_markdown(coll),
336
- sound_to_play, # The sound output
337
  )
338
 
339
  # ---------------------------------------------------------------------------
@@ -420,16 +403,6 @@ def _safe_pos(position):
420
  x, y = position
421
  return (max(0,min(2,int(x))), max(0,min(2,int(y))))
422
 
423
- def _stream_myco():
424
- """Streams only Myco's AI responses β€” no errors, no logs."""
425
- seen = ""
426
- while True:
427
- current = _LAST_REPLY["text"]
428
- if current != seen:
429
- seen = current
430
- yield seen
431
- time.sleep(0.3)
432
-
433
 
434
  # ---------------------------------------------------------------------------
435
  # Guide text
@@ -478,8 +451,6 @@ def build_app():
478
  # Autonomous roam timer β€” every 5 seconds, pure Python movement
479
  roam_timer = gr.Timer(5)
480
 
481
- sound_player = gr.Audio(visible=False, autoplay=True)
482
-
483
  with gr.Tabs(selected="home") as tabs:
484
 
485
  with gr.Tab("🏠 Home", id="home"):
@@ -570,14 +541,21 @@ def build_app():
570
  elem_id="myco-live-log",
571
  )
572
 
573
- demo.load(fn=_stream_myco, outputs=[myco_live])
 
 
 
 
 
 
 
 
574
 
575
  # ── Shared output lists ──
576
  _move_outputs = [
577
  player_position, scene, status, discovery,
578
  current_mushroom, collection, active_mushrooms,
579
- chat, dex, progress,
580
- sound_player
581
  ]
582
 
583
  # ── Event wiring ──
 
5
  import copy
6
  import random
7
  import threading
 
8
  import logging
9
  from typing import Any, cast
10
  import gradio as gr
 
32
  )
33
  from ui.interface import render_shroom_tab
34
 
35
+ from game.engine import get_myco_log
36
 
37
  logger = logging.getLogger(__name__)
38
  EMPTY_COLLECTION = None
 
63
  with _ROAM_LOCK:
64
  x, y = _ROAM["position"]
65
  safe_mushrooms = list(active_mushrooms or [])
66
+ coll = list(collection or [])
 
 
 
67
 
68
  # Check pickup at current position first
69
  if safe_mushrooms:
 
84
  nx, ny, current, coll, safe_mushrooms
85
  )
86
 
 
 
 
 
87
  return (
88
  list(pos),
89
  forest_scene(current, coll, pos, active_mushrooms=safe_mushrooms),
 
95
  gr.update(),
96
  dex_markdown(coll),
97
  progress_markdown(coll),
 
98
  )
99
 
100
  # ---------------------------------------------------------------------------
 
295
 
296
  safe_mushrooms = list(active_mushrooms or [])
297
  coll = list(collection or [])
 
 
 
 
298
 
299
+ # Collision check β€” pure Python, no LLM
300
  current, coll, safe_mushrooms = check_auto_pickup(
301
  x, y, current, coll, safe_mushrooms
302
  )
303
 
 
 
 
304
  # Update roam tracker
305
  with _ROAM_LOCK:
306
  _ROAM["position"] = [x, y]
 
317
  gr.update(), # leave chat untouched
318
  dex_markdown(coll),
319
  progress_markdown(coll),
 
320
  )
321
 
322
  # ---------------------------------------------------------------------------
 
403
  x, y = position
404
  return (max(0,min(2,int(x))), max(0,min(2,int(y))))
405
 
 
 
 
 
 
 
 
 
 
 
406
 
407
  # ---------------------------------------------------------------------------
408
  # Guide text
 
451
  # Autonomous roam timer β€” every 5 seconds, pure Python movement
452
  roam_timer = gr.Timer(5)
453
 
 
 
454
  with gr.Tabs(selected="home") as tabs:
455
 
456
  with gr.Tab("🏠 Home", id="home"):
 
541
  elem_id="myco-live-log",
542
  )
543
 
544
+ # Poll the running Myco log (narrative/action/chat) once per second.
545
+ # NOTE: previously this used demo.load(fn=_stream_myco, ...) with an
546
+ # infinite `while True` generator. An infinite generator from
547
+ # demo.load permanently occupies a queue worker/stream for that
548
+ # session β€” other events (button clicks) can then execute their
549
+ # Python function fine (visible in server logs) but never get their
550
+ # result delivered back to the browser. A periodic Timer avoids this.
551
+ log_timer = gr.Timer(1)
552
+ log_timer.tick(fn=get_myco_log, outputs=myco_live)
553
 
554
  # ── Shared output lists ──
555
  _move_outputs = [
556
  player_position, scene, status, discovery,
557
  current_mushroom, collection, active_mushrooms,
558
+ chat, dex, progress,
 
559
  ]
560
 
561
  # ── Event wiring ──