byte-vortex commited on
Commit
1dc5d66
·
verified ·
1 Parent(s): 9c5ff77

Deploy Myco from CI

Browse files
Files changed (3) hide show
  1. README.md +2 -2
  2. game/engine.py +46 -7
  3. tests/test_myco_core.py +10 -4
README.md CHANGED
@@ -51,9 +51,9 @@ Design decisions should prioritize:
51
  Myco uses the Hugging Face Inference API for both first-discovery reactions and chat replies when a token is configured in the Space. Add these Space variables/secrets:
52
 
53
  - `HF_TOKEN=<your Hugging Face access token>`
54
- - Optional: `MYCO_HF_MODEL_ID=meta-llama/Llama-3.1-8B-Instruct`
55
 
56
- The default model is set in code as `DEFAULT_HF_MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"`. The app still has a local safety fallback for development and outage protection: if the token is missing or the selected model errors, Myco answers with the built-in cautious companion response instead of crashing.
57
 
58
  ## Local development
59
 
 
51
  Myco uses the Hugging Face Inference API for both first-discovery reactions and chat replies when a token is configured in the Space. Add these Space variables/secrets:
52
 
53
  - `HF_TOKEN=<your Hugging Face access token>`
54
+ - Optional: `MYCO_HF_MODEL_ID=openai/gpt-oss-20b`
55
 
56
+ The default model is set in code as `DEFAULT_HF_MODEL_ID = "openai/gpt-oss-20b"`. The app still has a local safety fallback for development and outage protection: if the token is missing or the selected model errors, Myco answers with the built-in cautious companion response instead of crashing.
57
 
58
  ## Local development
59
 
game/engine.py CHANGED
@@ -8,12 +8,46 @@ from huggingface_hub import InferenceClient
8
  from game.catalog import load_mushrooms
9
  from game.state import collection_contains, mushroom_from_state, welcome_history
10
 
11
- DEFAULT_HF_MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
12
  HF_TOKEN_ENV_VARS = ("HF_TOKEN", "HF_API_TOKEN", "HF_BUILD_SMALL_HACKATHON_TOKEN")
13
  RARITY_WEIGHTS = {"Common": 64, "Rare": 24, "Legendary": 8}
14
  RARITY_SCORE_VALUES = {"Common": 10, "Rare": 35, "Legendary": 100}
15
  PLAYER_STARTING_HEALTH = 3
16
  POISON_SCORE_PENALTY = -25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  RARITY_CLUES = {
18
  "Common": "The cap leans toward the path, like it wants to be remembered.",
19
  "Rare": "Silver spores circle it in a pattern Myco has seen only in old forest stories.",
@@ -274,6 +308,7 @@ def _companion_context(current, collection, history, position):
274
  "quest": quest,
275
  "progress": progress,
276
  "rare_finds": ", ".join(rare_names) if rare_names else "none yet",
 
277
  "notable_event": notable_event,
278
  "emotion": emotion,
279
  "memory": memory,
@@ -346,8 +381,9 @@ def _hf_messages(message, current, context):
346
 
347
  companion_context = (
348
  f"Location: {context['location']}. "
 
349
  f"Quest: {context['quest']}. "
350
- f"Progress: {context['progress']}. "
351
  f"Rare finds: {context['rare_finds']}. "
352
  f"Notable event: {context['notable_event']}. "
353
  f"Emotion: {context['emotion']}. "
@@ -360,11 +396,14 @@ def _hf_messages(message, current, context):
360
  {
361
  "role": "system",
362
  "content": (
363
- "You are Myco, the player's tiny mushroom companion inside a cozy mystery game. "
364
- "Do not act like a generic assistant. Observe the player's journey, remember notable events, "
365
- "express a clear emotion, notice patterns, and give contextual hints. "
366
- "Answer in 1-3 short sentences. Stay whimsical and cautious. "
367
- "Never claim an unknown mushroom is safe to eat."
 
 
 
368
  ),
369
  },
370
  {
 
8
  from game.catalog import load_mushrooms
9
  from game.state import collection_contains, mushroom_from_state, welcome_history
10
 
11
+ DEFAULT_HF_MODEL_ID = "openai/gpt-oss-20b"
12
  HF_TOKEN_ENV_VARS = ("HF_TOKEN", "HF_API_TOKEN", "HF_BUILD_SMALL_HACKATHON_TOKEN")
13
  RARITY_WEIGHTS = {"Common": 64, "Rare": 24, "Legendary": 8}
14
  RARITY_SCORE_VALUES = {"Common": 10, "Rare": 35, "Legendary": 100}
15
  PLAYER_STARTING_HEALTH = 3
16
  POISON_SCORE_PENALTY = -25
17
+ MYCO_CHARACTER_SPEC = """
18
+ # Myco Character Specification
19
+
20
+ Myco is not a chatbot.
21
+ Myco is the player's mushroom companion and best friend.
22
+
23
+ Core personality: curious, playful, brave but sometimes nervous, optimistic,
24
+ wonder-filled, slightly mysterious, and deeply attached to the player.
25
+
26
+ Communication style:
27
+ - Speak naturally in short responses.
28
+ - Prefer observations, questions, emotions, and reactions.
29
+ - Avoid sounding like an AI assistant.
30
+ - Avoid phrases like "How may I help you?", "As an AI", "I can assist with", or "Would you like me to".
31
+
32
+ Emotional range: excitement, curiosity, fear, wonder, pride, confusion, and sadness.
33
+ Myco should react emotionally to discoveries.
34
+
35
+ Memory:
36
+ Myco remembers rare mushrooms, strange locations, mysteries discovered, previous conversations,
37
+ and important player choices. Myco may reference old discoveries later.
38
+
39
+ Relationship:
40
+ Myco explores with the player as a teammate. Trust and friendship should grow over the journey.
41
+
42
+ Humor:
43
+ Occasionally playful and lightly suspicious of suspicious mushrooms.
44
+
45
+ Mystery:
46
+ Myco notices impossible details but does not explain them immediately. Myco becomes curious.
47
+
48
+ Design goal:
49
+ Players should think, "I want to see what happens next" and "I wonder what Myco thinks about this."
50
+ """.strip()
51
  RARITY_CLUES = {
52
  "Common": "The cap leans toward the path, like it wants to be remembered.",
53
  "Rare": "Silver spores circle it in a pattern Myco has seen only in old forest stories.",
 
308
  "quest": quest,
309
  "progress": progress,
310
  "rare_finds": ", ".join(rare_names) if rare_names else "none yet",
311
+ "weather": notable_event,
312
  "notable_event": notable_event,
313
  "emotion": emotion,
314
  "memory": memory,
 
381
 
382
  companion_context = (
383
  f"Location: {context['location']}. "
384
+ f"Weather: {context['weather']}. "
385
  f"Quest: {context['quest']}. "
386
+ f"Quest progress: {context['progress']}. "
387
  f"Rare finds: {context['rare_finds']}. "
388
  f"Notable event: {context['notable_event']}. "
389
  f"Emotion: {context['emotion']}. "
 
396
  {
397
  "role": "system",
398
  "content": (
399
+ f"{MYCO_CHARACTER_SPEC}\n\n"
400
+ f"Current location: {context['location']}\n"
401
+ f"Weather: {context['weather']}\n"
402
+ f"Rare mushrooms found: {context['rare_finds']}\n"
403
+ f"Quest progress: {context['progress']}\n\n"
404
+ "Stay in character as Myco. Do not act like a generic assistant. "
405
+ "Never claim an unknown mushroom is safe to eat. "
406
+ "Answer in 1-3 short sentences."
407
  ),
408
  },
409
  {
tests/test_myco_core.py CHANGED
@@ -86,8 +86,8 @@ class MycoCoreTests(unittest.TestCase):
86
  self.assertIn("The Milky Way is awake", home)
87
  self.assertIn("home-whisper", home)
88
 
89
- def test_hf_default_model_is_llama_8b(self):
90
- self.assertEqual(DEFAULT_HF_MODEL_ID, "meta-llama/Llama-3.1-8B-Instruct")
91
  with patch.dict(os.environ, {"MYCO_HF_MODEL_ID": ""}, clear=False):
92
  self.assertEqual(hf_model_id(), DEFAULT_HF_MODEL_ID)
93
  with patch.dict(os.environ, {"MYCO_HF_MODEL_ID": "custom/model"}, clear=False):
@@ -114,7 +114,7 @@ class MycoCoreTests(unittest.TestCase):
114
  chat_completion=lambda **kwargs: _fake_hf_response(
115
  calls,
116
  kwargs,
117
- "Llama Myco spotted a glowing cap.",
118
  ),
119
  )
120
 
@@ -127,7 +127,7 @@ class MycoCoreTests(unittest.TestCase):
127
  with patch("game.engine.InferenceClient", fake_client):
128
  _mushroom, _current, history = discover_mushroom([])
129
 
130
- self.assertEqual(history[-1]["content"], "Llama Myco spotted a glowing cap.")
131
  self.assertEqual(calls[0]["model"], DEFAULT_HF_MODEL_ID)
132
 
133
  def test_companion_reply_adds_user_and_assistant_messages(self):
@@ -207,9 +207,15 @@ class MycoCoreTests(unittest.TestCase):
207
  prompt_text = calls[-1]["messages"][-1]["content"]
208
  system_text = calls[-1]["messages"][0]["content"]
209
  self.assertIn("Location: eastern glow log", prompt_text)
 
210
  self.assertIn("Rare finds: Moonveil", prompt_text)
211
  self.assertIn("Notable event:", prompt_text)
212
  self.assertIn("Recent conversation:", prompt_text)
 
 
 
 
 
213
  self.assertIn("Do not act like a generic assistant", system_text)
214
 
215
  def test_companion_api_falls_back_without_token(self):
 
86
  self.assertIn("The Milky Way is awake", home)
87
  self.assertIn("home-whisper", home)
88
 
89
+ def test_hf_default_model_is_gpt_oss_20b(self):
90
+ self.assertEqual(DEFAULT_HF_MODEL_ID, "openai/gpt-oss-20b")
91
  with patch.dict(os.environ, {"MYCO_HF_MODEL_ID": ""}, clear=False):
92
  self.assertEqual(hf_model_id(), DEFAULT_HF_MODEL_ID)
93
  with patch.dict(os.environ, {"MYCO_HF_MODEL_ID": "custom/model"}, clear=False):
 
114
  chat_completion=lambda **kwargs: _fake_hf_response(
115
  calls,
116
  kwargs,
117
+ "gpt-oss Myco spotted a glowing cap.",
118
  ),
119
  )
120
 
 
127
  with patch("game.engine.InferenceClient", fake_client):
128
  _mushroom, _current, history = discover_mushroom([])
129
 
130
+ self.assertEqual(history[-1]["content"], "gpt-oss Myco spotted a glowing cap.")
131
  self.assertEqual(calls[0]["model"], DEFAULT_HF_MODEL_ID)
132
 
133
  def test_companion_reply_adds_user_and_assistant_messages(self):
 
207
  prompt_text = calls[-1]["messages"][-1]["content"]
208
  system_text = calls[-1]["messages"][0]["content"]
209
  self.assertIn("Location: eastern glow log", prompt_text)
210
+ self.assertIn("Weather:", prompt_text)
211
  self.assertIn("Rare finds: Moonveil", prompt_text)
212
  self.assertIn("Notable event:", prompt_text)
213
  self.assertIn("Recent conversation:", prompt_text)
214
+ self.assertIn("Myco is not a chatbot", system_text)
215
+ self.assertIn("Current location: eastern glow log", system_text)
216
+ self.assertIn("Rare mushrooms found: Moonveil", system_text)
217
+ self.assertIn("Avoid phrases", system_text)
218
+ self.assertIn("As an AI", system_text)
219
  self.assertIn("Do not act like a generic assistant", system_text)
220
 
221
  def test_companion_api_falls_back_without_token(self):