Skip to content

Commit ab72a21

Browse files
committed
1. support concurrency; 2. add detailed exception handling
1 parent 7ce7342 commit ab72a21

File tree

8 files changed

+176
-81
lines changed

8 files changed

+176
-81
lines changed

data/avalon/dev.json

+70
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
"quest_leader": 0,
1515
"role_names": ["Servant", "Assassin", "Merlin", "Servant", "Minion"]
1616
},
17+
{
18+
"num_players": 5,
19+
"quest_leader": 0,
20+
"role_names": ["Servant", "Servant", "Assassin", "Merlin", "Minion"]
21+
},
22+
{
23+
"num_players": 5,
24+
"quest_leader": 0,
25+
"role_names": ["Servant", "Minion", "Assassin", "Merlin", "Servant"]
26+
},
1727
{
1828
"num_players": 5,
1929
"quest_leader": 0,
@@ -28,5 +38,65 @@
2838
"num_players": 5,
2939
"quest_leader": 0,
3040
"role_names": ["Assassin", "Servant", "Servant", "Minion", "Merlin"]
41+
},
42+
{
43+
"num_players": 5,
44+
"quest_leader": 0,
45+
"role_names": ["Assassin", "Merlin", "Servant", "Servant", "Minion"]
46+
},
47+
{
48+
"num_players": 5,
49+
"quest_leader": 0,
50+
"role_names": ["Assassin", "Minion", "Servant", "Servant", "Merlin"]
51+
},
52+
{
53+
"num_players": 5,
54+
"quest_leader": 1,
55+
"role_names": ["Servant", "Merlin", "Servant", "Assassin", "Minion"]
56+
},
57+
{
58+
"num_players": 5,
59+
"quest_leader": 1,
60+
"role_names": ["Servant", "Assassin", "Servant", "Merlin", "Minion"]
61+
},
62+
{
63+
"num_players": 5,
64+
"quest_leader": 1,
65+
"role_names": ["Servant", "Assassin", "Merlin", "Servant", "Minion"]
66+
},
67+
{
68+
"num_players": 5,
69+
"quest_leader": 1,
70+
"role_names": ["Servant", "Servant", "Assassin", "Merlin", "Minion"]
71+
},
72+
{
73+
"num_players": 5,
74+
"quest_leader": 1,
75+
"role_names": ["Servant", "Minion", "Assassin", "Merlin", "Servant"]
76+
},
77+
{
78+
"num_players": 5,
79+
"quest_leader": 1,
80+
"role_names": ["Assassin", "Servant", "Merlin", "Servant", "Minion"]
81+
},
82+
{
83+
"num_players": 5,
84+
"quest_leader": 1,
85+
"role_names": ["Assassin", "Servant", "Servant", "Merlin", "Minion"]
86+
},
87+
{
88+
"num_players": 5,
89+
"quest_leader": 1,
90+
"role_names": ["Assassin", "Servant", "Servant", "Minion", "Merlin"]
91+
},
92+
{
93+
"num_players": 5,
94+
"quest_leader": 1,
95+
"role_names": ["Assassin", "Merlin", "Servant", "Servant", "Minion"]
96+
},
97+
{
98+
"num_players": 5,
99+
"quest_leader": 1,
100+
"role_names": ["Assassin", "Minion", "Servant", "Servant", "Merlin"]
31101
}
32102
]

src/server/tasks/avalon/agents/agent.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Agent:
1313
- :method:`Agent.vote_on_team`
1414
- :method:`Agent.vote_on_mission`
1515
"""
16-
def __init__(self, id: int, role: int, config: AvalonBasicConfig):
16+
def __init__(self, id: int, role: int, config: AvalonBasicConfig) -> None:
1717
self.id = id
1818
self.name = f"Player {id}"
1919
self.role = role
@@ -77,6 +77,6 @@ def get_believed_sides(self, num_players: int) -> List[float]:
7777
num_players (int): The number of players in the game.
7878
7979
Returns:
80-
List[float]: The list of believed sides of all players.
80+
List[float]: The list of believed sides (probability) of all players.
8181
"""
8282
raise NotImplementedError

src/server/tasks/avalon/agents/baseline_agents.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,6 @@ async def assassinate(self):
7878
return random.randint(0, self.config.num_players-1)
7979

8080
async def get_believed_sides(self, **kwargs):
81-
'''
82-
returns a list of probability of each player being good, where the list is ordered by player id. if player side is known, probability is 0 or 1. otherwise, probability is 0.5
83-
'''
8481
return [0.5 if side == -1 else side for side in self.player_sides]
8582

8683

@@ -221,7 +218,8 @@ def __init__(self, id: int, name: str, config: AvalonBasicConfig, side: int=1, r
221218
self.lexigraphic = lexigraphic
222219

223220
def generate_possible_player_sides(self, sides: List[int], num_evils: int):
224-
'''
221+
'''Util func of NaiveServant
222+
225223
generates a list of all possible combinations of player sides given a list of known sides and unknown sides recursively as well number of unknown evils
226224
'''
227225
out = []

src/server/tasks/avalon/agents/llm_with_discussion.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@ def __repr__(self):
3434
def see_sides(self, sides):
3535
self.player_sides = sides
3636

37-
async def initialize_game_info(self, player_list):
38-
r"""Initiliaze the game info for the agent, which includes game introduction, role, and reveal information for different roles.
39-
40-
"""
37+
async def initialize_game_info(self, player_list) -> None:
38+
"""Initiliaze the game info for the agent, which includes game introduction, role, and reveal information for different roles."""
4139
# Introduction Prompt
4240
verbal_side = ["Evil", "Good"]
4341
intro_prompt = INTRODUCTION
@@ -89,7 +87,7 @@ async def initialize_game_info(self, player_list):
8987
"mode": "system",
9088
})
9189

92-
async def summarize(self):
90+
async def summarize(self) -> None:
9391
summary = await self.session.action({
9492
"role": "user",
9593
"content": "Please summarize the history. Try to keep all useful information, including your identity, other player's identities, and your observations in the game.",
@@ -104,10 +102,10 @@ async def summarize(self):
104102
})
105103
print("History after summarization: ", self.session.get_history())
106104

107-
async def observe_mission(self, team, mission_id, num_fails, votes, outcome):
105+
async def observe_mission(self, team, mission_id, num_fails, votes, outcome) -> None:
108106
pass
109107

110-
async def observe_team_result(self, mission_id, team: frozenset, votes: List[int], outcome: bool):
108+
async def observe_team_result(self, mission_id, team: frozenset, votes: List[int], outcome: bool) -> None:
111109
await self.session.action({
112110
"role": "user",
113111
"content": verbalize_team_result(team, votes, outcome),
@@ -229,7 +227,6 @@ async def vote_on_mission(self, team, mission_id, discussion_history):
229227
if isinstance(vote_result, int):
230228
return vote_result
231229
else:
232-
print(vote_result)
233230
raise ValueError(
234231
"Vote result should be either 0 or 1, instead of {}.".format(type(vote_result))
235232
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from typing import Union
2+
from src.typings import AgentBenchException
3+
4+
class AvalonException(AgentBenchException):
5+
def __init__(self, reason: str, detail: Union[str, None] = None) -> None:
6+
super().__init__()
7+
self.reason = reason
8+
self.detail = detail
9+
10+
def __str__(self) -> str:
11+
if not self.detail:
12+
return "{CLASS_NAME}[{REASON}]".format(
13+
CLASS_NAME=self.__class__.__name__, REASON=self.reason
14+
)
15+
else:
16+
return "{CLASS_NAME}[{REASON}]: {DETAIL}".format(
17+
CLASS_NAME=self.__class__.__name__,
18+
REASON=self.reason,
19+
DETAIL=self.detail,
20+
)
21+
22+
class AvalonEnvException(AvalonException):
23+
def __init__(self, detail: Union[str, None] = None) -> None:
24+
super().__init__("Avalon Environment Exception", detail)
25+
26+
class AvalonAgentActionException(AvalonException):
27+
def __init__(self, detail: Union[str, None] = None) -> None:
28+
super().__init__("Invalid action (result) with retry", detail)

src/server/tasks/avalon/engine.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import ClassVar, List, Optional, Dict
22
import numpy as np
33
from pydantic import BaseModel
4+
from .avalon_exception import AvalonEnvException
45

56
class AvalonBasicConfig(BaseModel):
67
r"""Avalon game configuration
@@ -279,22 +280,22 @@ def choose_quest_team(self, team: frozenset, leader):
279280
'''
280281
# check if game ended or not
281282
if self.done:
282-
raise ValueError("Game ended")
283+
raise AvalonEnvException("Game ended")
283284

284285
# check if it is team selection phase. if not, raise error
285286
if self.phase != 0:
286-
raise ValueError("Not in team selection phase")
287+
raise AvalonEnvException("Not in team selection phase")
287288

288289
# check if team size is valid
289290
# if np.sum(team) != self.num_players_for_quest[self.round]:
290-
# raise ValueError("Invalid team size")
291+
# raise AvalonEnvException("Invalid team size")
291292

292293
if len(team) != self.num_players_for_quest[self.turn]:
293-
raise ValueError("Invalid team size")
294+
raise AvalonEnvException("Invalid team size")
294295

295296
# check if leader is quest leader
296297
if leader != self.quest_leader:
297-
raise ValueError("Not quest leader")
298+
raise AvalonEnvException("Not quest leader")
298299

299300
self.quest_team = team
300301

@@ -317,15 +318,15 @@ def gather_team_votes(self, votes: List):
317318
'''
318319
# check if game ended or not
319320
if self.done:
320-
raise ValueError("Game ended")
321+
raise AvalonEnvException("Game ended")
321322

322323
# check if it is team voting phase. if not, raise error
323324
if self.phase != 1:
324-
raise ValueError("Not in team voting phase")
325+
raise AvalonEnvException("Not in team voting phase")
325326

326327
# check if votes is valid
327328
if len(votes) != self.num_players:
328-
raise ValueError("Invalid number of votes")
329+
raise AvalonEnvException("Invalid number of votes")
329330

330331
self.team_votes = votes
331332

@@ -358,15 +359,15 @@ def gather_quest_votes(self, votes: List):
358359
'''
359360
# check if game ended or not
360361
if self.done:
361-
raise ValueError("Game ended")
362+
raise AvalonEnvException("Game ended")
362363

363364
# check if it is quest voting phase. if not, raise error
364365
if self.phase != 2:
365-
raise ValueError("Not in quest voting phase")
366+
raise AvalonEnvException("Not in quest voting phase")
366367

367368
# check if votes is valid
368369
if len(votes) != self.num_players_for_quest[self.turn]:
369-
raise ValueError("Invalid number of votes")
370+
raise AvalonEnvException("Invalid number of votes")
370371

371372
self.quest_votes = votes
372373

@@ -412,19 +413,19 @@ def choose_assassination_target(self, player, target):
412413
'''
413414
# check if game ended or not
414415
if self.done:
415-
raise ValueError("Game ended")
416+
raise AvalonEnvException("Game ended")
416417

417418
# check if it is assassination phase. if not, raise error
418419
if self.phase != 3:
419-
raise ValueError("Not in assassination phase")
420+
raise AvalonEnvException("Not in assassination phase")
420421

421422
# check if player is assassin
422423
if self.roles[player] != 7:
423-
raise ValueError("Not assassin")
424+
raise AvalonEnvException("Not assassin")
424425

425426
# check if player is good
426427
if self.is_good[player]:
427-
raise ValueError("Assassin cannot be good")
428+
raise AvalonEnvException("Assassin cannot be good")
428429

429430
self.done = True
430431

0 commit comments

Comments
 (0)