mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-02 14:45:17 +02:00
fix code
This commit is contained in:
parent
94a04afc72
commit
d4a40bd988
9 changed files with 126 additions and 60 deletions
|
|
@ -19,7 +19,7 @@ from metagpt.logs import logger
|
|||
from metagpt.utils.common import load_mc_skills_code, read_json_file, write_json_file
|
||||
|
||||
|
||||
class MinecraftEnv(Environment, MinecraftExtEnv):
|
||||
class MinecraftEnv(MinecraftExtEnv, Environment):
|
||||
"""MinecraftEnv, including shared memory of cache and information between roles"""
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
|
|
|||
|
|
@ -30,78 +30,92 @@ class RoleActionRes(Enum):
|
|||
PASS = "pass" # ignore current action output
|
||||
|
||||
|
||||
empty_set = set()
|
||||
|
||||
# the ordered rules by the moderator to announce to everyone each step
|
||||
STEP_INSTRUCTIONS = {
|
||||
0: {
|
||||
"content": "It’s dark, everyone close your eyes. I will talk with you/your team secretly at night.",
|
||||
"send_to": {"Moderator"}, # for moderator to continue speaking
|
||||
"restricted_to": {},
|
||||
"send_to": {RoleType.MODERATOR.value}, # for moderator to continue speaking
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
1: {
|
||||
"content": "Guard, please open your eyes!",
|
||||
"send_to": {"Moderator"}, # for moderator to continue speaking
|
||||
"restricted_to": {},
|
||||
"send_to": {RoleType.MODERATOR.value}, # for moderator to continue speaking
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
2: {
|
||||
"content": """Guard, now tell me who you protect tonight?
|
||||
You only choose one from the following living options please: {living_players}.
|
||||
Or you can pass. For example: Protect ...""",
|
||||
"send_to": {"Guard"},
|
||||
"restricted_to": {"Moderator", "Guard"},
|
||||
"send_to": {RoleType.GUARD.value},
|
||||
"restricted_to": {RoleType.MODERATOR.value, RoleType.GUARD.value},
|
||||
},
|
||||
3: {"content": "Guard, close your eyes", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
4: {
|
||||
"content": "Werewolves, please open your eyes!",
|
||||
"send_to": {RoleType.MODERATOR.value},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
3: {"content": "Guard, close your eyes", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
4: {"content": "Werewolves, please open your eyes!", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
5: {
|
||||
"content": """Werewolves, I secretly tell you that {werewolf_players} are
|
||||
all of the 2 werewolves! Keep in mind you are teammates. The rest players are not werewolves.
|
||||
choose one from the following living options please:
|
||||
{living_players}. For example: Kill ...""",
|
||||
"send_to": {"Werewolf"},
|
||||
"restricted_to": {"Moderator", "Werewolf"},
|
||||
"send_to": {RoleType.WEREWOLF.value},
|
||||
"restricted_to": {RoleType.MODERATOR.value, RoleType.WEREWOLF.value},
|
||||
},
|
||||
6: {"content": "Werewolves, close your eyes", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
7: {"content": "Witch, please open your eyes!", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
6: {"content": "Werewolves, close your eyes", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
7: {"content": "Witch, please open your eyes!", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
8: {
|
||||
"content": """Witch, tonight {player_hunted} has been killed by the werewolves.
|
||||
You have a bottle of antidote, would you like to save him/her? If so, say "Save", else, say "Pass".""",
|
||||
"send_to": {"Witch"},
|
||||
"restricted_to": {"Moderator", "Witch"},
|
||||
"send_to": {RoleType.WITCH.value},
|
||||
"restricted_to": {RoleType.MODERATOR.value, RoleType.WITCH.value},
|
||||
}, # 要先判断女巫是否有解药,再去询问女巫是否使用解药救人
|
||||
9: {
|
||||
"content": """Witch, you also have a bottle of poison, would you like to use it to kill one of the living players?
|
||||
Choose one from the following living options: {living_players}.
|
||||
If so, say ONLY "Poison PlayerX", replace PlayerX with the actual player name, else, say "Pass".""",
|
||||
"send_to": {"Witch"},
|
||||
"restricted_to": {"Moderator", "Witch"},
|
||||
"send_to": {RoleType.WITCH.value},
|
||||
"restricted_to": {RoleType.MODERATOR.value, RoleType.WITCH.value},
|
||||
}, #
|
||||
10: {"content": "Witch, close your eyes", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
11: {"content": "Seer, please open your eyes!", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
10: {"content": "Witch, close your eyes", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
11: {"content": "Seer, please open your eyes!", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
12: {
|
||||
"content": """Seer, you can check one player's identity. Who are you going to verify its identity tonight?
|
||||
Choose only one from the following living options:{living_players}.""",
|
||||
"send_to": {"Seer"},
|
||||
"restricted_to": {"Moderator", "Seer"},
|
||||
"send_to": {RoleType.SEER.value},
|
||||
"restricted_to": {RoleType.MODERATOR.value, RoleType.SEER.value},
|
||||
},
|
||||
13: {"content": "Seer, close your eyes", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
13: {"content": "Seer, close your eyes", "send_to": {RoleType.MODERATOR.value}, "restricted_to": empty_set},
|
||||
# The 1-st daytime
|
||||
14: {
|
||||
"content": """It's daytime. Everyone woke up except those who had been killed.""",
|
||||
"send_to": {"Moderator"},
|
||||
"restricted_to": {},
|
||||
"send_to": {RoleType.MODERATOR.value},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
15: {
|
||||
"content": "{player_current_dead} was killed last night!",
|
||||
"send_to": {RoleType.MODERATOR.value},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
15: {"content": "{player_current_dead} was killed last night!", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
16: {
|
||||
"content": """Living players: {living_players}, now freely talk about the current situation based on your observation and
|
||||
reflection with a few sentences. Decide whether to reveal your identity based on your reflection.""",
|
||||
"send_to": {MESSAGE_ROUTE_TO_ALL}, # send to all to speak in daytime
|
||||
"restricted_to": {},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
17: {
|
||||
"content": """Now vote and tell me who you think is the werewolf. Don’t mention your role.
|
||||
You only choose one from the following living options please:
|
||||
{living_players}. Say ONLY: I vote to eliminate ...""",
|
||||
"send_to": {MESSAGE_ROUTE_TO_ALL},
|
||||
"restricted_to": {},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
18: {
|
||||
"content": """{player_current_dead} was eliminated.""",
|
||||
"send_to": {RoleType.MODERATOR.value},
|
||||
"restricted_to": empty_set,
|
||||
},
|
||||
18: {"content": """{player_current_dead} was eliminated.""", "send_to": {"Moderator"}, "restricted_to": {}},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Desc : MG Werewolf Env
|
||||
|
||||
from typing import Iterable
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.environment.base_env import Environment
|
||||
|
|
@ -9,15 +11,26 @@ from metagpt.environment.werewolf.werewolf_ext_env import WerewolfExtEnv
|
|||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class WerewolfEnv(Environment, WerewolfExtEnv):
|
||||
timestamp: int = Field(default=0)
|
||||
class WerewolfEnv(WerewolfExtEnv, Environment):
|
||||
round_cnt: int = Field(default=0)
|
||||
|
||||
def add_roles(self, roles: Iterable["Role"]):
|
||||
"""增加一批在当前环境的角色
|
||||
Add a batch of characters in the current environment
|
||||
"""
|
||||
for role in roles:
|
||||
self.roles[role.name] = role # use name as key here, due to multi-player can have same profile
|
||||
|
||||
for role in roles: # setup system message with roles
|
||||
role.context = self.context
|
||||
role.set_env(self)
|
||||
|
||||
def publish_message(self, message: Message, add_timestamp: bool = True):
|
||||
"""Post information to the current environment"""
|
||||
if add_timestamp:
|
||||
# Because the content of the message may be repeated, for example, killing the same person in two nights
|
||||
# Therefore, a unique timestamp prefix needs to be added so that the same message will not be automatically deduplicated when added to the memory.
|
||||
message.content = f"{self.timestamp} | " + message.content
|
||||
# Therefore, a unique round_cnt prefix needs to be added so that the same message will not be automatically deduplicated when added to the memory.
|
||||
message.content = f"{self.round_cnt} | " + message.content
|
||||
super().publish_message(message)
|
||||
|
||||
async def run(self, k=1):
|
||||
|
|
@ -25,4 +38,4 @@ class WerewolfEnv(Environment, WerewolfExtEnv):
|
|||
for _ in range(k):
|
||||
for role in self.roles.values():
|
||||
await role.run()
|
||||
self.timestamp += 1
|
||||
self.round_cnt += 1
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class WerewolfExtEnv(ExtEnv):
|
|||
|
||||
round_idx: int = Field(default=0) # the current round
|
||||
step_idx: int = Field(default=0) # the current step of current round
|
||||
eval_step_idx: int = Field(default=0)
|
||||
eval_step_idx: list[int] = Field(default=[])
|
||||
per_round_steps: int = Field(default=len(STEP_INSTRUCTIONS))
|
||||
|
||||
# game global states
|
||||
|
|
@ -259,7 +259,7 @@ class WerewolfExtEnv(ExtEnv):
|
|||
def wolf_kill_someone(self, wolf_name: str, player_name: str):
|
||||
if not self._check_valid_role(wolf_name, RoleType.WEREWOLF.value):
|
||||
return
|
||||
if not self._check_player_continue(wolf_name, particular_step=5): # 5=step no
|
||||
if not self._check_player_continue(wolf_name, particular_step=6): # 5=step no
|
||||
return
|
||||
|
||||
self.round_hunts[wolf_name] = player_name
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import json
|
|||
from tenacity import retry, stop_after_attempt, wait_fixed
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.logs import logger
|
||||
from metagpt.utils.common import parse_json_code_block
|
||||
|
||||
|
||||
class Speak(Action):
|
||||
|
|
@ -228,6 +230,8 @@ class Reflect(Action):
|
|||
|
||||
rsp = await self._aask(prompt)
|
||||
rsp = rsp.replace("\n", " ")
|
||||
rsp_json = json.loads(rsp)
|
||||
logger.debug(f"{self.name} result: {rsp}")
|
||||
json_blocks = parse_json_code_block(rsp)
|
||||
rsp_json = json.loads(json_blocks[0])
|
||||
|
||||
return json.dumps(rsp_json["REFLECTION"])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import re
|
||||
|
||||
from pydantic import Field, SerializeAsAny
|
||||
from pydantic import Field, SerializeAsAny, model_validator
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.environment.werewolf.const import RoleState, RoleType
|
||||
|
|
@ -16,6 +16,7 @@ from metagpt.ext.werewolf.actions import (
|
|||
from metagpt.ext.werewolf.schema import RoleExperience, WwMessage
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.utils.common import any_to_str
|
||||
|
||||
|
||||
class BasePlayer(Role):
|
||||
|
|
@ -44,6 +45,12 @@ class BasePlayer(Role):
|
|||
logger.warning("You must enable use_reflection before using experience")
|
||||
self.use_experience = False
|
||||
|
||||
@model_validator(mode="after")
|
||||
def check_addresses(self):
|
||||
if not self.addresses:
|
||||
self.addresses = {any_to_str(self), self.name, self.profile} if self.name else {any_to_str(self)}
|
||||
return self
|
||||
|
||||
async def _observe(self, ignore_memory=False) -> int:
|
||||
if self.status != RoleState.ALIVE:
|
||||
# 死者不再参与游戏
|
||||
|
|
@ -71,7 +78,7 @@ class BasePlayer(Role):
|
|||
|
||||
async def _think(self):
|
||||
news = self.rc.news[0]
|
||||
assert news.cause_by == InstructSpeak # 消息为来自Moderator的指令时,才去做动作
|
||||
assert news.cause_by == any_to_str(InstructSpeak) # 消息为来自Moderator的指令时,才去做动作
|
||||
if not news.restricted_to:
|
||||
# 消息接收范围为全体角色的,做公开发言(发表投票观点也算发言)
|
||||
self.rc.todo = Speak()
|
||||
|
|
@ -115,14 +122,13 @@ class BasePlayer(Role):
|
|||
reflection=reflection,
|
||||
experiences=experiences,
|
||||
)
|
||||
restricted_to = {}
|
||||
restricted_to = set()
|
||||
|
||||
elif isinstance(todo, NighttimeWhispers):
|
||||
rsp = await todo.run(
|
||||
profile=self.profile, name=self.name, context=memories, reflection=reflection, experiences=experiences
|
||||
)
|
||||
restricted_to = {RoleType.MODERATOR.value, self.profile} # 给Moderator发送使用特殊技能的加密消息
|
||||
|
||||
msg = WwMessage(
|
||||
content=rsp,
|
||||
role=self.profile,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from datetime import datetime
|
|||
from typing import Union
|
||||
|
||||
from metagpt.actions.add_requirement import UserRequirement
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT, MESSAGE_ROUTE_TO_ALL
|
||||
from metagpt.environment.werewolf.const import (
|
||||
STEP_INSTRUCTIONS,
|
||||
RoleActionRes,
|
||||
|
|
@ -20,6 +20,7 @@ from metagpt.ext.werewolf.actions.moderator_actions import (
|
|||
from metagpt.ext.werewolf.roles.base_player import BasePlayer
|
||||
from metagpt.ext.werewolf.schema import WwMessage
|
||||
from metagpt.logs import logger
|
||||
from metagpt.utils.common import any_to_str
|
||||
|
||||
|
||||
class Moderator(BasePlayer):
|
||||
|
|
@ -63,8 +64,6 @@ class Moderator(BasePlayer):
|
|||
role.record_experiences(round_id=timestamp, outcome=outcome, game_setup=self.game_setup)
|
||||
|
||||
async def _parse_speak(self, memories):
|
||||
logger.info(f"step_idx: {self.step_idx}")
|
||||
|
||||
latest_msg = memories[-1]
|
||||
latest_msg_content = latest_msg.content
|
||||
|
||||
|
|
@ -76,25 +75,25 @@ class Moderator(BasePlayer):
|
|||
restricted_to = set()
|
||||
|
||||
msg_cause_by = latest_msg.cause_by
|
||||
if msg_cause_by == Hunt:
|
||||
if msg_cause_by == any_to_str(Hunt):
|
||||
self.rc.env.step(
|
||||
EnvAction(
|
||||
action_type=EnvActionType.WOLF_KILL, player_name=latest_msg.send_from, target_player_name=target
|
||||
action_type=EnvActionType.WOLF_KILL, player_name=latest_msg.sent_from, target_player_name=target
|
||||
)
|
||||
)
|
||||
elif msg_cause_by == Protect:
|
||||
elif msg_cause_by == any_to_str(Protect):
|
||||
self.rc.env.step(
|
||||
EnvAction(
|
||||
action_type=EnvActionType.GUARD_PROTECT, player_name=latest_msg.send_from, target_player_name=target
|
||||
action_type=EnvActionType.GUARD_PROTECT, player_name=latest_msg.sent_from, target_player_name=target
|
||||
)
|
||||
)
|
||||
elif msg_cause_by == Verify:
|
||||
elif msg_cause_by == any_to_str(Verify):
|
||||
if target in self.werewolf_players:
|
||||
msg_content = f"{target} is a werewolf"
|
||||
else:
|
||||
msg_content = f"{target} is a good guy"
|
||||
restricted_to = {RoleType.MODERATOR.value, RoleType.SEER.value}
|
||||
elif msg_cause_by == Save:
|
||||
elif msg_cause_by == any_to_str(Save):
|
||||
if RoleActionRes.PASS.value in latest_msg_content.lower():
|
||||
# the role ignore to response, answer `pass`
|
||||
pass
|
||||
|
|
@ -105,11 +104,11 @@ class Moderator(BasePlayer):
|
|||
self.rc.env.step(
|
||||
EnvAction(
|
||||
action_type=EnvActionType.WITCH_SAVE,
|
||||
player_name=latest_msg.send_from,
|
||||
player_name=latest_msg.sent_from,
|
||||
target_player_name=target,
|
||||
)
|
||||
)
|
||||
elif msg_cause_by == Poison:
|
||||
elif msg_cause_by == any_to_str(Poison):
|
||||
if RoleActionRes.PASS.value in latest_msg_content.lower():
|
||||
pass
|
||||
elif not self.witch_poison_left:
|
||||
|
|
@ -119,7 +118,7 @@ class Moderator(BasePlayer):
|
|||
self.rc.env.step(
|
||||
EnvAction(
|
||||
action_type=EnvActionType.WITCH_POISON,
|
||||
player_name=latest_msg.send_from,
|
||||
player_name=latest_msg.sent_from,
|
||||
target_player_name=target,
|
||||
)
|
||||
)
|
||||
|
|
@ -132,12 +131,32 @@ class Moderator(BasePlayer):
|
|||
self.update_player_status(player_current_dead)
|
||||
|
||||
def _record_game_history(self, step_idx: int):
|
||||
if step_idx % len(STEP_INSTRUCTIONS) == 0 or self.winner:
|
||||
if step_idx and step_idx % len(STEP_INSTRUCTIONS) == 0 or self.winner:
|
||||
logger.info("a night and day cycle completed, examine all history")
|
||||
logger.debug(f"all_memories: {self.get_all_memories()}")
|
||||
with open(DEFAULT_WORKSPACE_ROOT / "werewolf_transcript.txt", "w") as f:
|
||||
f.write(self.get_all_memories())
|
||||
|
||||
async def _observe(self, ignore_memory=False) -> int:
|
||||
news = []
|
||||
if not news:
|
||||
news = self.rc.msg_buffer.pop_all()
|
||||
old_messages = [] if ignore_memory else self.rc.memory.get()
|
||||
for m in news:
|
||||
if len(m.restricted_to) and self.profile not in m.restricted_to and self.name not in m.restricted_to:
|
||||
# if the msg is not send to the whole audience ("") nor this role (self.profile or self.name),
|
||||
# then this role should not be able to receive it and record it into its memory
|
||||
continue
|
||||
self.rc.memory.add(m)
|
||||
# add `MESSAGE_ROUTE_TO_ALL in n.send_to` make it to run `ParseSpeak`
|
||||
self.rc.news = [
|
||||
n
|
||||
for n in news
|
||||
if (n.cause_by in self.rc.watch or self.profile in n.send_to or MESSAGE_ROUTE_TO_ALL in n.send_to)
|
||||
and n not in old_messages
|
||||
]
|
||||
return len(self.rc.news)
|
||||
|
||||
async def _think(self):
|
||||
if self.winner:
|
||||
self.rc.todo = AnnounceGameResult()
|
||||
|
|
@ -155,6 +174,7 @@ class Moderator(BasePlayer):
|
|||
|
||||
def _init_fields_from_obj(self, obs: dict[str, Union[int, str, list[str]]]):
|
||||
self.game_setup = obs.get("game_setup", "")
|
||||
self.step_idx = obs.get("step_idx", 0)
|
||||
self.winner = obs.get("winner")
|
||||
self.win_reason = obs.get("win_reason")
|
||||
self.werewolf_players = obs.get("werewolf_players", [])
|
||||
|
|
@ -168,7 +188,6 @@ class Moderator(BasePlayer):
|
|||
memories = self.get_all_memories(mode="msg")
|
||||
|
||||
obs, _, _, _, _ = self.rc.env.step(action=EnvAction(action_type=EnvActionType.NONE))
|
||||
step_idx = obs["step_idx"]
|
||||
living_players = obs["living_players"]
|
||||
werewolf_players = obs["werewolf_players"]
|
||||
player_hunted = obs["player_hunted"]
|
||||
|
|
@ -176,17 +195,17 @@ class Moderator(BasePlayer):
|
|||
self._init_fields_from_obj(obs)
|
||||
|
||||
# 若进行完一夜一日的循环,打印和记录一次完整发言历史
|
||||
self._record_game_history(step_idx)
|
||||
self._record_game_history(self.step_idx)
|
||||
|
||||
# 若一晚或一日周期结束,对当晚或当日的死者进行总结,并更新玩家状态
|
||||
self._update_player_status(step_idx, player_current_dead)
|
||||
self._update_player_status(self.step_idx, player_current_dead)
|
||||
if self.winner:
|
||||
self._record_all_experiences()
|
||||
|
||||
# 根据_think的结果,执行InstructSpeak还是ParseSpeak, 并将结果返回
|
||||
if isinstance(todo, InstructSpeak):
|
||||
msg_content, msg_to_send_to, msg_restricted_to = await InstructSpeak().run(
|
||||
step_idx,
|
||||
self.step_idx,
|
||||
living_players=living_players,
|
||||
werewolf_players=werewolf_players,
|
||||
player_hunted=player_hunted,
|
||||
|
|
@ -201,6 +220,7 @@ class Moderator(BasePlayer):
|
|||
send_to=msg_to_send_to,
|
||||
restricted_to=msg_restricted_to,
|
||||
)
|
||||
logger.info(f"current step_idx: {self.step_idx}")
|
||||
self.rc.env.step(EnvAction(action_type=EnvActionType.PROGRESS_STEP)) # to update step_idx
|
||||
|
||||
elif isinstance(todo, ParseSpeak):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from metagpt.environment.werewolf.const import RoleType
|
||||
from metagpt.ext.werewolf.actions import InstructSpeak, Poison, Save, Speak
|
||||
from metagpt.ext.werewolf.roles.base_player import BasePlayer
|
||||
from metagpt.utils.common import any_to_str
|
||||
|
||||
|
||||
class Witch(BasePlayer):
|
||||
|
|
@ -11,7 +12,7 @@ class Witch(BasePlayer):
|
|||
async def _think(self):
|
||||
"""女巫涉及两个特殊技能,因此在此需要改写_think进行路由"""
|
||||
news = self.rc.news[0]
|
||||
assert news.cause_by == InstructSpeak # 消息为来自Moderator的指令时,才去做动作
|
||||
assert news.cause_by == any_to_str(InstructSpeak) # 消息为来自Moderator的指令时,才去做动作
|
||||
if not news.restricted_to:
|
||||
# 消息接收范围为全体角色的,做公开发言(发表投票观点也算发言)
|
||||
self.rc.todo = Speak()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
from pydantic import BaseModel, Field
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.common import any_to_str_set
|
||||
|
||||
|
||||
class RoleExperience(BaseModel):
|
||||
|
|
@ -18,4 +21,9 @@ class RoleExperience(BaseModel):
|
|||
|
||||
class WwMessage(Message):
|
||||
# Werewolf Message
|
||||
restricted_to: set[str] = Field(default={}, validate_default=True)
|
||||
restricted_to: set[str] = Field(default=set(), validate_default=True)
|
||||
|
||||
@field_validator("restricted_to", mode="before")
|
||||
@classmethod
|
||||
def check_restricted_to(cls, restricted_to: Any):
|
||||
return any_to_str_set(restricted_to if restricted_to else set())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue