Spaces:
Sleeping
Sleeping
adityaverma977 commited on
Commit ·
1bb7020
1
Parent(s): da20490
Switch to dynamic model-generated chat with context
Browse files- app/groq_client.py +11 -1
- 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
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 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"
|
| 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 =
|
| 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
|