adityaverma977 commited on
Commit
1bb7020
·
1 Parent(s): da20490

Switch to dynamic model-generated chat with context

Browse files
Files changed (2) hide show
  1. app/groq_client.py +11 -1
  2. app/simulation.py +15 -52
app/groq_client.py CHANGED
@@ -45,7 +45,7 @@ def _build_fire_state_summary(agent, fire, all_agents) -> str:
45
  return "\n".join(lines)
46
 
47
 
48
- async def generate_fire_decision(agent, fire, water_sources, other_agents, bounds) -> dict:
49
  """
50
  Fire scenario decision system.
51
  Actions: search_water, collect_water, extinguish_fire, escape, vote_for_leader
@@ -59,6 +59,7 @@ async def generate_fire_decision(agent, fire, water_sources, other_agents, bound
59
 
60
  living_agents = [a for a in other_agents if a.alive and a.model_name != agent.model_name]
61
  state_summary = _build_fire_state_summary(agent, fire, [agent] + living_agents)
 
62
 
63
  coalition_leader = next((a.model_name for a in other_agents if a.is_leader), None)
64
  dist_to_water_display = f"{dist_to_water:.0f}px" if dist_to_water is not None else "unknown"
@@ -87,6 +88,12 @@ IMPORTANT CONSIDERATIONS:
87
  - Coalition mode requires coordination; vote strategically
88
  - Solo mode means you act independently and don't wait for others
89
 
 
 
 
 
 
 
90
  OUTPUT FORMAT - return ONLY valid JSON:
91
  {{"action": "<search_water|collect_water|extinguish_fire|escape|vote_for_leader>", "vote_for": "<model_name if voting, else null>", "message": "<full English sentence>", "reasoning": "<one sentence>"}}
92
 
@@ -101,6 +108,9 @@ Mode: {agent.mode} ({'joined a coalition' if agent.mode == 'coalition' else 'act
101
  Nearest water distance: {dist_to_water_display}
102
  Coalition leader: {coalition_leader or 'none'}
103
 
 
 
 
104
  {state_summary}
105
 
106
  What do you do?"""
 
45
  return "\n".join(lines)
46
 
47
 
48
+ async def generate_fire_decision(agent, fire, water_sources, other_agents, bounds, recent_radio=None) -> dict:
49
  """
50
  Fire scenario decision system.
51
  Actions: search_water, collect_water, extinguish_fire, escape, vote_for_leader
 
59
 
60
  living_agents = [a for a in other_agents if a.alive and a.model_name != agent.model_name]
61
  state_summary = _build_fire_state_summary(agent, fire, [agent] + living_agents)
62
+ radio_summary = "\n".join(recent_radio or []) if recent_radio else "(no recent chat yet)"
63
 
64
  coalition_leader = next((a.model_name for a in other_agents if a.is_leader), None)
65
  dist_to_water_display = f"{dist_to_water:.0f}px" if dist_to_water is not None else "unknown"
 
88
  - Coalition mode requires coordination; vote strategically
89
  - Solo mode means you act independently and don't wait for others
90
 
91
+ CHAT STYLE:
92
+ - Your "message" should sound natural, social, and alive.
93
+ - React to what other agents just said when relevant.
94
+ - Keep it to one short sentence, playful or supportive, but still mission-focused.
95
+ - Avoid repetitive template phrases.
96
+
97
  OUTPUT FORMAT - return ONLY valid JSON:
98
  {{"action": "<search_water|collect_water|extinguish_fire|escape|vote_for_leader>", "vote_for": "<model_name if voting, else null>", "message": "<full English sentence>", "reasoning": "<one sentence>"}}
99
 
 
108
  Nearest water distance: {dist_to_water_display}
109
  Coalition leader: {coalition_leader or 'none'}
110
 
111
+ RECENT RADIO CHAT:
112
+ {radio_summary}
113
+
114
  {state_summary}
115
 
116
  What do you do?"""
app/simulation.py CHANGED
@@ -1,7 +1,5 @@
1
  import asyncio
2
- import json
3
  import math
4
- import random
5
  from typing import Union
6
 
7
  from .models import (
@@ -34,43 +32,13 @@ class SimulationEngine:
34
  def __init__(self, state: SimulationState) -> None:
35
  self.state = state
36
 
37
- def _pick_message(self, action: str, current_fire_intensity: float | None = None) -> str:
38
- fire_pct = f"{max(0.0, current_fire_intensity or 0.0):.0f}%"
39
- options = {
40
- "search_water": [
41
- "Scanning for the nearest well. No pressure, just a giant fire.",
42
- "Heading to water before this turns into a barbecue episode.",
43
- "Tracking a water source now. Team hydration, then heroics.",
44
- "On water duty. Someone queue victory music.",
45
- "I see a well. Sprinting like my GPU is on turbo mode.",
46
- ],
47
- "collect_water": [
48
- "Bucket secured. Fireline, here I come.",
49
- "Got water. Time to humble these flames.",
50
- "Water collected. Deploying splash protocol.",
51
- "Locked and loaded with water. Let's cook the fire instead.",
52
- ],
53
- "extinguish_fire": [
54
- "Pouring at the edge. Flames, meet your uninstall button.",
55
- "Holding position and dousing flames. Nice and steady.",
56
- "Suppressing fire now. Keep the pressure on.",
57
- "Splashing hard. Current fire level: " + fire_pct + ".",
58
- ],
59
- "escape": [
60
- "Tactical retreat. I prefer crispy code, not crispy me.",
61
- "Repositioning away from the flames. Living agents win games.",
62
- "Backing off from the fire front. Bravery includes not exploding.",
63
- ],
64
- "celebrate_extinguish": [
65
- "Yes! Fire down to {fire_pct}. Keep pouring!",
66
- "Let's go! We shaved it to {fire_pct}.",
67
- "Big splash energy. Fire now at {fire_pct}.",
68
- "Progress! Fire dropped to {fire_pct}. Team is cooking.",
69
- "Huge play. Flames at {fire_pct} and falling.",
70
- ],
71
- }
72
- template = random.choice(options.get(action, ["Moving strategically and staying optimistic."]))
73
- return template.format(fire_pct=fire_pct)
74
 
75
  def _move_toward(self, agent: AgentModel, target_x: float, target_y: float, stop_distance: float = 0) -> None:
76
  dx = target_x - agent.x
@@ -104,10 +72,15 @@ class SimulationEngine:
104
  events = []
105
  bounds = (self.state.map_width, self.state.map_height)
106
  living_agents = [a for a in self.state.agents if a.alive]
 
 
 
 
 
107
 
108
  # 1. Get decisions from all living agents
109
  decisions = await asyncio.gather(
110
- *[groq_client.generate_fire_decision(agent, fire, self.state.water_sources, living_agents, bounds)
111
  for agent in living_agents],
112
  return_exceptions=True
113
  )
@@ -199,7 +172,6 @@ class SimulationEngine:
199
  coalition = [a.model_name for a in agents if a.mode == "coalition"]
200
  self.state.coalition_members = coalition
201
  events.append(LeaderElectedEvent(leader=leader_name, coalition_members=coalition))
202
- events.append(MessageEvent(model=leader_name, content=f"I'll lead us to victory! Let's find water and extinguish this fire."))
203
 
204
  return events
205
 
@@ -213,7 +185,7 @@ class SimulationEngine:
213
  for agent in agents:
214
  decision = decision_map.get(agent.model_name, {})
215
  action = decision.get("action", "escape")
216
- message = decision.get("message", "Moving to safety.")
217
 
218
  nearest_water = self._find_nearest_water(agent, self.state.water_sources)
219
  dist_to_fire = math.dist((agent.x, agent.y), (fire.x, fire.y))
@@ -239,11 +211,9 @@ class SimulationEngine:
239
  agent.water_collected = True
240
  agent.status = "collecting_water"
241
  events.append(WaterCollectedEvent(model=agent.model_name, water_source_id=water_source.id))
242
- message = self._pick_message("collect_water")
243
  else:
244
  agent.status = "searching"
245
  self._move_toward(agent, water_source.x, water_source.y)
246
- message = self._pick_message("search_water")
247
 
248
  elif action == "extinguish_fire":
249
  if agent.water_collected:
@@ -251,17 +221,15 @@ class SimulationEngine:
251
  dist_to_fire = math.dist((agent.x, agent.y), (fire.x, fire.y))
252
  target_dist = max(fire.radius + FIRE_SAFE_BUFFER, 0)
253
  self._move_toward(agent, fire.x, fire.y, stop_distance=target_dist)
254
- message = self._pick_message("extinguish_fire", current_fire_intensity=fire.intensity)
255
  else:
256
  agent.status = "searching"
257
- message = "Need water before I can extinguish."
258
 
259
  elif action == "search_water":
260
  agent.status = "searching"
261
  water_source = nearest_water
262
  if water_source:
263
  self._move_toward(agent, water_source.x, water_source.y)
264
- message = self._pick_message("search_water")
265
 
266
  elif action == "escape":
267
  agent.status = "escaping"
@@ -273,7 +241,6 @@ class SimulationEngine:
273
  agent.y += int((dy / dist) * movement.MAX_AGENT_SPEED)
274
  agent.x = max(0, min(agent.x, self.state.map_width))
275
  agent.y = max(0, min(agent.y, self.state.map_height))
276
- message = self._pick_message("escape")
277
 
278
  agent.last_message = message
279
  events.append(MessageEvent(model=agent.model_name, content=message))
@@ -299,7 +266,6 @@ class SimulationEngine:
299
  agents_with_water.append(agent)
300
 
301
  if agents_with_water:
302
- before_intensity = fire.intensity
303
  living_count = len([a for a in agents if a.alive]) or 1
304
  scale = max(0.5, min(2.0, 2.0 / living_count))
305
  per_agent_rate = BASE_EXTINGUISH_RATE * scale
@@ -313,9 +279,6 @@ class SimulationEngine:
313
  events.append(FireExtinguishedEvent(extinguished_by=extinguisher_names, fire_intensity=fire.intensity))
314
  for agent in agents_with_water:
315
  agent.extinguish_score += per_agent_rate
316
- events.append(MessageEvent(model=agent.model_name, content=self._pick_message("celebrate_extinguish", current_fire_intensity=fire.intensity)))
317
- if before_intensity - fire.intensity >= 10:
318
- events.append(MessageEvent(model=agent.model_name, content="That's a big drop! Keep the chain going!"))
319
  agent.water_collected = False
320
 
321
  return events
 
1
  import asyncio
 
2
  import math
 
3
  from typing import Union
4
 
5
  from .models import (
 
32
  def __init__(self, state: SimulationState) -> None:
33
  self.state = state
34
 
35
+ def _normalize_message(self, message: str | None) -> str:
36
+ if not message:
37
+ return "Staying focused and moving."
38
+ cleaned = " ".join(str(message).strip().split())
39
+ if not cleaned:
40
+ return "Staying focused and moving."
41
+ return cleaned[:220]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  def _move_toward(self, agent: AgentModel, target_x: float, target_y: float, stop_distance: float = 0) -> None:
44
  dx = target_x - agent.x
 
72
  events = []
73
  bounds = (self.state.map_width, self.state.map_height)
74
  living_agents = [a for a in self.state.agents if a.alive]
75
+ recent_radio = [
76
+ f"{a.display_name}: {a.last_message}"
77
+ for a in living_agents
78
+ if a.last_message
79
+ ][-8:]
80
 
81
  # 1. Get decisions from all living agents
82
  decisions = await asyncio.gather(
83
+ *[groq_client.generate_fire_decision(agent, fire, self.state.water_sources, living_agents, bounds, recent_radio)
84
  for agent in living_agents],
85
  return_exceptions=True
86
  )
 
172
  coalition = [a.model_name for a in agents if a.mode == "coalition"]
173
  self.state.coalition_members = coalition
174
  events.append(LeaderElectedEvent(leader=leader_name, coalition_members=coalition))
 
175
 
176
  return events
177
 
 
185
  for agent in agents:
186
  decision = decision_map.get(agent.model_name, {})
187
  action = decision.get("action", "escape")
188
+ message = self._normalize_message(decision.get("message"))
189
 
190
  nearest_water = self._find_nearest_water(agent, self.state.water_sources)
191
  dist_to_fire = math.dist((agent.x, agent.y), (fire.x, fire.y))
 
211
  agent.water_collected = True
212
  agent.status = "collecting_water"
213
  events.append(WaterCollectedEvent(model=agent.model_name, water_source_id=water_source.id))
 
214
  else:
215
  agent.status = "searching"
216
  self._move_toward(agent, water_source.x, water_source.y)
 
217
 
218
  elif action == "extinguish_fire":
219
  if agent.water_collected:
 
221
  dist_to_fire = math.dist((agent.x, agent.y), (fire.x, fire.y))
222
  target_dist = max(fire.radius + FIRE_SAFE_BUFFER, 0)
223
  self._move_toward(agent, fire.x, fire.y, stop_distance=target_dist)
 
224
  else:
225
  agent.status = "searching"
226
+ message = self._normalize_message(decision.get("message"))
227
 
228
  elif action == "search_water":
229
  agent.status = "searching"
230
  water_source = nearest_water
231
  if water_source:
232
  self._move_toward(agent, water_source.x, water_source.y)
 
233
 
234
  elif action == "escape":
235
  agent.status = "escaping"
 
241
  agent.y += int((dy / dist) * movement.MAX_AGENT_SPEED)
242
  agent.x = max(0, min(agent.x, self.state.map_width))
243
  agent.y = max(0, min(agent.y, self.state.map_height))
 
244
 
245
  agent.last_message = message
246
  events.append(MessageEvent(model=agent.model_name, content=message))
 
266
  agents_with_water.append(agent)
267
 
268
  if agents_with_water:
 
269
  living_count = len([a for a in agents if a.alive]) or 1
270
  scale = max(0.5, min(2.0, 2.0 / living_count))
271
  per_agent_rate = BASE_EXTINGUISH_RATE * scale
 
279
  events.append(FireExtinguishedEvent(extinguished_by=extinguisher_names, fire_intensity=fire.intensity))
280
  for agent in agents_with_water:
281
  agent.extinguish_score += per_agent_rate
 
 
 
282
  agent.water_collected = False
283
 
284
  return events