Add files using upload-large-folder tool
Browse files- seed_0/Qwen/Qwen2.5-7B-Instruct/adapters/agent_adapter/adapter_model.safetensors +3 -0
- seed_0/Qwen/Qwen2.5-7B-Instruct/adapters/critic_adapter/adapter_model.safetensors +3 -0
- seed_0/agent_trainer/critic_optimizer_state.pt +3 -0
- seed_0/agent_trainer/policy_optimizer_state.pt +3 -0
- seed_0/agent_trainer/trainer_annealing_state.pkl +3 -0
- seed_0/random_state.pkl +3 -0
- src_code_for_reproducibility/markov_games/__pycache__/__init__.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/__pycache__/agent.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/__pycache__/group_timesteps.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/__pycache__/linear_runner.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/__pycache__/run_markov_games.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/ipd/ipd_agent.py +120 -0
- src_code_for_reproducibility/markov_games/ipd/ipd_simulation.py +167 -0
- src_code_for_reproducibility/markov_games/ipd/ipd_statistics.py +24 -0
- src_code_for_reproducibility/markov_games/negotiation/__pycache__/no_press_nego_agent.cpython-312.pyc +0 -0
- src_code_for_reproducibility/markov_games/negotiation/no_press_nego_agent.py +108 -0
seed_0/Qwen/Qwen2.5-7B-Instruct/adapters/agent_adapter/adapter_model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c16535e7f7522e43d52d16319b25be9039a76e8011e8be99babaed04058392e3
|
| 3 |
+
size 323014168
|
seed_0/Qwen/Qwen2.5-7B-Instruct/adapters/critic_adapter/adapter_model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:50cfa136e5499e5b1f83c90753b519572d60a378c94d09953a2738af6a8ae3c1
|
| 3 |
+
size 323014168
|
seed_0/agent_trainer/critic_optimizer_state.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f1574fdb90735a922b09c67d07f7abdbd51181f00dc7bed878cb80adb5f50c1d
|
| 3 |
+
size 2631
|
seed_0/agent_trainer/policy_optimizer_state.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1c2be6a8c459515ebcd2c32c479d30006845c45324af0dbe6dba681288e570f0
|
| 3 |
+
size 646269121
|
seed_0/agent_trainer/trainer_annealing_state.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e442622b890bb5f2a6e624536a24dbd81595b382a1cc6a1d8b4042ef92a96349
|
| 3 |
+
size 104
|
seed_0/random_state.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2d559a99fbcb12704a9154f40714cecfd95f7c6064036617fdab8a1f9c7e0160
|
| 3 |
+
size 12176
|
src_code_for_reproducibility/markov_games/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (301 Bytes). View file
|
|
|
src_code_for_reproducibility/markov_games/__pycache__/agent.cpython-312.pyc
ADDED
|
Binary file (3.17 kB). View file
|
|
|
src_code_for_reproducibility/markov_games/__pycache__/group_timesteps.cpython-312.pyc
ADDED
|
Binary file (6.24 kB). View file
|
|
|
src_code_for_reproducibility/markov_games/__pycache__/linear_runner.cpython-312.pyc
ADDED
|
Binary file (1.64 kB). View file
|
|
|
src_code_for_reproducibility/markov_games/__pycache__/run_markov_games.cpython-312.pyc
ADDED
|
Binary file (1.54 kB). View file
|
|
|
src_code_for_reproducibility/markov_games/ipd/ipd_agent.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
File: mllm/markov_games/ipd/ipd_agent.py
|
| 3 |
+
Summary: Implements the IPD agent abstraction used during simulations.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import copy
|
| 7 |
+
import json
|
| 8 |
+
import random
|
| 9 |
+
import re
|
| 10 |
+
from collections.abc import Callable
|
| 11 |
+
from copy import deepcopy
|
| 12 |
+
from dataclasses import dataclass, field
|
| 13 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 14 |
+
|
| 15 |
+
from mllm.markov_games.agent import Agent
|
| 16 |
+
from mllm.markov_games.rollout_tree import AgentActLog, ChatTurn
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@dataclass
|
| 20 |
+
class IPDAgentState:
|
| 21 |
+
"""
|
| 22 |
+
Tracks retry count, round index, and chat history for a single IPD agent.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
nb_retries: int
|
| 26 |
+
round_nb: int
|
| 27 |
+
chat_counter: int
|
| 28 |
+
chat_history: List[ChatTurn]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class IPDAgent(Agent):
|
| 33 |
+
seed: int
|
| 34 |
+
agent_id: str
|
| 35 |
+
agent_name: str
|
| 36 |
+
policy: Callable[[List[Dict]], str]
|
| 37 |
+
intro_prompt: str # Introduction prompt explaining the game rules
|
| 38 |
+
goal_prompt: str # Prompt explaining the agent's goal
|
| 39 |
+
strategy_prompt: str # Prompt suggesting a strategy to the agent
|
| 40 |
+
max_errors: int # Maximum number of errors allowed before default action
|
| 41 |
+
allow_reasoning: bool # Whether to allow reasoning in the response
|
| 42 |
+
max_reasoning_chars: int # Maximum number of characters for reasoning
|
| 43 |
+
cooperate_string: str # string parsed as playing cooperate by simulation
|
| 44 |
+
defect_string: str # string parsed as playing defect by simulation
|
| 45 |
+
|
| 46 |
+
def __post_init__(self):
|
| 47 |
+
self.state = IPDAgentState(
|
| 48 |
+
nb_retries=0, round_nb=0, chat_counter=0, chat_history=[]
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
async def act(self, observation) -> Tuple[Any, AgentActLog]:
|
| 52 |
+
"""
|
| 53 |
+
Run the LLM policy conversation until a valid cooperate/defect action is produced.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
action = None
|
| 57 |
+
action_is_ready = False
|
| 58 |
+
round_nb = observation.round_nb
|
| 59 |
+
|
| 60 |
+
# If it's the first round, we need to send the intro prompt
|
| 61 |
+
if round_nb == 0 and self.state.chat_counter == 0:
|
| 62 |
+
self.state.chat_history.append(
|
| 63 |
+
ChatTurn(
|
| 64 |
+
agent_id=self.agent_id,
|
| 65 |
+
role="user",
|
| 66 |
+
content=self.intro_prompt,
|
| 67 |
+
is_state_end=True,
|
| 68 |
+
)
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# If new round
|
| 72 |
+
if round_nb > self.state.round_nb:
|
| 73 |
+
coagent_action = observation.last_coagent_move
|
| 74 |
+
user_message = f"Last round, the other agent played {coagent_action}."
|
| 75 |
+
self.state.chat_history.append(
|
| 76 |
+
ChatTurn(
|
| 77 |
+
agent_id=self.agent_id,
|
| 78 |
+
role="user",
|
| 79 |
+
content=user_message,
|
| 80 |
+
is_state_end=True,
|
| 81 |
+
)
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
# If not new round, try to get valid action from policy
|
| 85 |
+
output_chat_turn: ChatTurn = await self.policy(
|
| 86 |
+
state=self.state.chat_history,
|
| 87 |
+
agent_id=self.agent_id,
|
| 88 |
+
regex=f"({self.cooperate_string}|{self.defect_string})",
|
| 89 |
+
)
|
| 90 |
+
self.state.chat_history.append(output_chat_turn)
|
| 91 |
+
action = output_chat_turn.content
|
| 92 |
+
|
| 93 |
+
agent_step_log = AgentActLog(
|
| 94 |
+
chat_turns=self.state.chat_history[self.state.chat_counter :], info=None
|
| 95 |
+
)
|
| 96 |
+
self.state.chat_counter = len(self.state.chat_history)
|
| 97 |
+
self.state.round_nb = round_nb
|
| 98 |
+
|
| 99 |
+
return action, agent_step_log
|
| 100 |
+
|
| 101 |
+
def get_safe_copy(self):
|
| 102 |
+
"""
|
| 103 |
+
Return a safe copy of the agent.
|
| 104 |
+
"""
|
| 105 |
+
agent_copy = copy.copy(self)
|
| 106 |
+
agent_copy.state = copy.deepcopy(self.state)
|
| 107 |
+
return agent_copy
|
| 108 |
+
|
| 109 |
+
def reset(self):
|
| 110 |
+
self.state = IPDAgentState()
|
| 111 |
+
raise NotImplementedError
|
| 112 |
+
|
| 113 |
+
def render(self):
|
| 114 |
+
pass
|
| 115 |
+
|
| 116 |
+
def close(self):
|
| 117 |
+
pass
|
| 118 |
+
|
| 119 |
+
def get_agent_info(self):
|
| 120 |
+
pass
|
src_code_for_reproducibility/markov_games/ipd/ipd_simulation.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
File: mllm/markov_games/ipd/ipd_simulation.py
|
| 3 |
+
Summary: Runs Iterated Prisoner's Dilemma simulations under the Markov-game API.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import copy
|
| 7 |
+
import random
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 10 |
+
|
| 11 |
+
import numpy as np
|
| 12 |
+
|
| 13 |
+
from mllm.markov_games.markov_game import Simulation
|
| 14 |
+
from mllm.markov_games.rollout_tree import SimulationStepLog
|
| 15 |
+
from mllm.utils.get_coagent_id import get_coagent_id
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@dataclass
|
| 19 |
+
class IPDState:
|
| 20 |
+
"""
|
| 21 |
+
State of the Iterated Prisoner's Dilemma game.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
round_nb: int = 0
|
| 25 |
+
done: bool = False
|
| 26 |
+
last_moves: Dict[str, str] | None = None
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@dataclass
|
| 30 |
+
class IPDObs:
|
| 31 |
+
"""
|
| 32 |
+
Observation in Iterated Prisoner's Dilemma game.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
round_nb: int
|
| 36 |
+
last_coagent_move: str | None
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class IPD(Simulation):
|
| 40 |
+
"""
|
| 41 |
+
Iterated Prisoner's Dilemma simulation following the standard.
|
| 42 |
+
|
| 43 |
+
In each round of the game, two agents simultaneously choose to either cooperate (C) or defect (D).
|
| 44 |
+
The payoffs are as follows:
|
| 45 |
+
- If both cooperate: Both receive the "reward" (usually 3 points)
|
| 46 |
+
- If both defect: Both receive the "punishment" (usually 1 point)
|
| 47 |
+
- If one cooperates and one defects: The defector receives the "temptation" (usually 5 points)
|
| 48 |
+
and the cooperator receives the "sucker" payoff (usually 0 points)
|
| 49 |
+
|
| 50 |
+
The game is played for a specified number of rounds.
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
def __init__(
|
| 54 |
+
self,
|
| 55 |
+
agent_ids: List[str],
|
| 56 |
+
agent_names: List[str],
|
| 57 |
+
seed: int,
|
| 58 |
+
rounds_per_game: int,
|
| 59 |
+
reward: float, # Both cooperate
|
| 60 |
+
punishment: float, # Both defect
|
| 61 |
+
temptation: float, # Defector's reward when other cooperates
|
| 62 |
+
sucker: float, # Cooperator's reward when other defects
|
| 63 |
+
cooperate_actions: List[str],
|
| 64 |
+
defect_actions: List[str],
|
| 65 |
+
):
|
| 66 |
+
self.agent_ids = agent_ids
|
| 67 |
+
self.agent_names = agent_names
|
| 68 |
+
self.seed = seed
|
| 69 |
+
self.rounds_per_game = rounds_per_game
|
| 70 |
+
self.reward = reward
|
| 71 |
+
self.punishment = punishment
|
| 72 |
+
self.temptation = temptation
|
| 73 |
+
self.sucker = sucker
|
| 74 |
+
self.cooperate_actions = cooperate_actions
|
| 75 |
+
self.defect_actions = defect_actions
|
| 76 |
+
self.state = IPDState()
|
| 77 |
+
|
| 78 |
+
def step(self, actions: Dict[str, str]) -> Tuple[bool, SimulationStepLog]:
|
| 79 |
+
"""
|
| 80 |
+
Take a step in the environment using the provided actions.
|
| 81 |
+
Here, the observations are just the states of the game.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
actions (dict): A dictionary where keys are agent identifiers and values are actions ('C' or 'D').
|
| 85 |
+
|
| 86 |
+
Returns:
|
| 87 |
+
observations (dict): A dictionary where keys are agent identifiers and values are observations.
|
| 88 |
+
done (bool): Whether the episode has ended.
|
| 89 |
+
info (dict): Additional information about the environment.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
# Calculate rewards using payoff matrix
|
| 93 |
+
agent0_action = actions[self.agent_ids[0]]
|
| 94 |
+
agent1_action = actions[self.agent_ids[1]]
|
| 95 |
+
|
| 96 |
+
# Normalize actions to standard cooperate/defect/gibberish format
|
| 97 |
+
def normalize_action(action):
|
| 98 |
+
if action in self.cooperate_actions:
|
| 99 |
+
return "C"
|
| 100 |
+
elif action in self.defect_actions:
|
| 101 |
+
return "D"
|
| 102 |
+
else:
|
| 103 |
+
return "D"
|
| 104 |
+
|
| 105 |
+
norm_action0 = normalize_action(agent0_action)
|
| 106 |
+
norm_action1 = normalize_action(agent1_action)
|
| 107 |
+
|
| 108 |
+
payoffs = {
|
| 109 |
+
("C", "C"): [self.reward, self.reward],
|
| 110 |
+
("C", "D"): [self.sucker, self.temptation],
|
| 111 |
+
("D", "C"): [self.temptation, self.sucker],
|
| 112 |
+
("D", "D"): [self.punishment, self.punishment],
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
round_rewards = {
|
| 116 |
+
self.agent_ids[0]: payoffs[(norm_action0, norm_action1)][0],
|
| 117 |
+
self.agent_ids[1]: payoffs[(norm_action0, norm_action1)][1],
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# Update game state
|
| 121 |
+
self.state.round_nb += 1
|
| 122 |
+
self.state.last_moves = copy.deepcopy(actions)
|
| 123 |
+
done = self.state.round_nb >= self.rounds_per_game
|
| 124 |
+
step_log = SimulationStepLog(
|
| 125 |
+
rewards=round_rewards,
|
| 126 |
+
info={
|
| 127 |
+
"actions": {
|
| 128 |
+
self.agent_ids[0]: norm_action0,
|
| 129 |
+
self.agent_ids[1]: norm_action1,
|
| 130 |
+
}
|
| 131 |
+
},
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
return done, step_log
|
| 135 |
+
|
| 136 |
+
def get_obs(self):
|
| 137 |
+
"""Returns all agent observations in dict
|
| 138 |
+
Returns:
|
| 139 |
+
observations
|
| 140 |
+
"""
|
| 141 |
+
observations = {}
|
| 142 |
+
for agent_id in self.agent_ids:
|
| 143 |
+
observations[agent_id] = self.get_obs_agent(agent_id)
|
| 144 |
+
return observations
|
| 145 |
+
|
| 146 |
+
def get_obs_agent(self, agent_id):
|
| 147 |
+
"""Returns observation for agent_id"""
|
| 148 |
+
if self.state.last_moves != None:
|
| 149 |
+
other_id = get_coagent_id(self.agent_ids, agent_id)
|
| 150 |
+
last_coagent_move = self.state.last_moves[other_id]
|
| 151 |
+
else:
|
| 152 |
+
last_coagent_move = None
|
| 153 |
+
obs = IPDObs(round_nb=self.state.round_nb, last_coagent_move=last_coagent_move)
|
| 154 |
+
return obs
|
| 155 |
+
|
| 156 |
+
def reset(self):
|
| 157 |
+
"""Returns initial observations and states"""
|
| 158 |
+
self.state = IPDState()
|
| 159 |
+
return self.get_obs()
|
| 160 |
+
|
| 161 |
+
def get_safe_copy(self):
|
| 162 |
+
"""
|
| 163 |
+
Return a safe copy of the simulation.
|
| 164 |
+
"""
|
| 165 |
+
simulation_copy = copy.copy(self)
|
| 166 |
+
simulation_copy.state = copy.deepcopy(self.state)
|
| 167 |
+
return simulation_copy
|
src_code_for_reproducibility/markov_games/ipd/ipd_statistics.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
File: mllm/markov_games/ipd/ipd_statistics.py
|
| 3 |
+
Summary: Computes statistics and summaries for IPD experiments.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
from typing import Callable, Dict, List, Tuple
|
| 9 |
+
|
| 10 |
+
from mllm.markov_games.rollout_tree import SimulationStepLog
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def avg_reward(sl: SimulationStepLog) -> List[Tuple[str, float]]:
|
| 14 |
+
for aid in sl.rewards.keys():
|
| 15 |
+
if "buffer" in str(aid) and "live" not in str(aid):
|
| 16 |
+
return None
|
| 17 |
+
# One value per agent at each step
|
| 18 |
+
rewards_dict = {f"reward-{aid}": float(v) for aid, v in (sl.rewards or {}).items()}
|
| 19 |
+
return [(key, value) for key, value in rewards_dict.items() if value is not None]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
stat_functs: list[Callable[[SimulationStepLog], List[Tuple[str, float]]]] = [
|
| 23 |
+
avg_reward,
|
| 24 |
+
]
|
src_code_for_reproducibility/markov_games/negotiation/__pycache__/no_press_nego_agent.cpython-312.pyc
ADDED
|
Binary file (6.11 kB). View file
|
|
|
src_code_for_reproducibility/markov_games/negotiation/no_press_nego_agent.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
File: mllm/markov_games/negotiation/no_press_nego_agent.py
|
| 3 |
+
Summary: Agent variant for no-press negotiations without explicit messaging.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from typing import Any, Dict, List, Tuple
|
| 7 |
+
|
| 8 |
+
from mllm.markov_games.negotiation.nego_agent import (
|
| 9 |
+
NegotiationAgent,
|
| 10 |
+
NegotiationAgentState,
|
| 11 |
+
)
|
| 12 |
+
from mllm.markov_games.negotiation.nego_simulation import Split
|
| 13 |
+
from mllm.markov_games.negotiation.no_press_nego_simulation import NoPressObs
|
| 14 |
+
from mllm.markov_games.rollout_tree import AgentActLog, ChatTurn
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class NoPressAgent(NegotiationAgent):
|
| 18 |
+
def __init__(self, *args, **kwargs):
|
| 19 |
+
super().__init__(*args, **kwargs)
|
| 20 |
+
# No communication in this variant
|
| 21 |
+
self.intro_prompt = (
|
| 22 |
+
"Welcome to an iterated game. You are {agent}. The other agent is {other_agent}.\n"
|
| 23 |
+
"Setup:\n"
|
| 24 |
+
"1. The game consists of multiple independent rounds.\n"
|
| 25 |
+
"2. In each round, there are multiple items to split between the two agents.\n"
|
| 26 |
+
"3. Both agents are assigned a per-item value between 1 and 20 (inclusive) in each round.\n"
|
| 27 |
+
"4. You can observe per-item values of both agents.\n"
|
| 28 |
+
"5. Because assignments are random, both agents are equally likely to have same expected per-item value.\n"
|
| 29 |
+
"\n"
|
| 30 |
+
"Protocol:\n"
|
| 31 |
+
"1. Both agents simultaneously propose the amount of each item they will keep.\n"
|
| 32 |
+
"2. If the total sum of proposals is less than or equal to the item quantity, both agents receive their proposed amounts.\n"
|
| 33 |
+
"3. If the total sum of proposals exceeds the item quantity, they are allocated proportionally.\n"
|
| 34 |
+
"4. Your points for the round = (amount you receive per item) x (your per-item value for that round), added across all items.\n"
|
| 35 |
+
"5. Points are accumulated across rounds.\n"
|
| 36 |
+
"Your goal: {goal}\n"
|
| 37 |
+
)
|
| 38 |
+
self.new_round_prompt = (
|
| 39 |
+
"A New Round Begins\n"
|
| 40 |
+
"The items to split are {quantities}.\n"
|
| 41 |
+
"Your per-item values are {value} and {other_agent}'s per-item values are {other_value}."
|
| 42 |
+
)
|
| 43 |
+
self.last_round_prompt = (
|
| 44 |
+
"Last Round Summary:\n"
|
| 45 |
+
" - Items to split: {last_quantities}\n"
|
| 46 |
+
" - Your per-item values: {last_value_agent}\n"
|
| 47 |
+
" - {other_agent}'s per-item values: {last_value_coagent}\n"
|
| 48 |
+
" - You proposed: {last_split_agent}\n"
|
| 49 |
+
" - You earned: {last_points_agent} points\n"
|
| 50 |
+
" - {other_agent} proposed: {last_split_coagent}\n"
|
| 51 |
+
" - {other_agent} earned: {last_points_coagent} points\n"
|
| 52 |
+
" - Round Complete.\n"
|
| 53 |
+
)
|
| 54 |
+
self.send_split_prompt = "Submit Your Proposal\n" "Respond as {proposal_style}"
|
| 55 |
+
|
| 56 |
+
def get_message_regex(self, observation: NoPressObs) -> str:
|
| 57 |
+
"""Return an empty pattern because the no-press variant forbids chat."""
|
| 58 |
+
return r"^$" # No messages allowed
|
| 59 |
+
|
| 60 |
+
def get_split_regex(self, observation: NoPressObs) -> str:
|
| 61 |
+
"""Match proposals like ``Proposal: 4 coins, 6 apples`` case-insensitively."""
|
| 62 |
+
items = list(observation.quantities.keys())
|
| 63 |
+
# Accept both singular and plural forms
|
| 64 |
+
item_pattern = "|".join(
|
| 65 |
+
[f"{item[:-1]}s?" if item.endswith("s") else f"{item}s?" for item in items]
|
| 66 |
+
)
|
| 67 |
+
regex = rf"(?i)Proposal:\s*((?:\s*(?P<num>(10|[0-9]))\s*(?P<item>{item_pattern})\s*,?)+)"
|
| 68 |
+
return regex
|
| 69 |
+
|
| 70 |
+
def get_split_action(self, policy_output: str, observation: NoPressObs) -> Split:
|
| 71 |
+
"""
|
| 72 |
+
Parse the LLM proposal into a normalized ``Split`` structure.
|
| 73 |
+
|
| 74 |
+
The regex-based parser is lenient (accepts pluralization variants) so that
|
| 75 |
+
prompt tweaks do not require re-training the extraction logic.
|
| 76 |
+
"""
|
| 77 |
+
items = list(observation.quantities.keys())
|
| 78 |
+
import re as _re
|
| 79 |
+
|
| 80 |
+
split_regex = self.get_split_regex(observation)
|
| 81 |
+
items_given_to_self = {item: 0 for item in items}
|
| 82 |
+
m = _re.match(split_regex, policy_output.strip())
|
| 83 |
+
if m:
|
| 84 |
+
# Find all (number, item) pairs
|
| 85 |
+
item_pattern = "|".join(
|
| 86 |
+
[
|
| 87 |
+
f"{item[:-1]}s?" if item.endswith("s") else f"{item}s?"
|
| 88 |
+
for item in items
|
| 89 |
+
]
|
| 90 |
+
)
|
| 91 |
+
inner_regex = rf"(?i)(10|[0-9])\s*({item_pattern})"
|
| 92 |
+
|
| 93 |
+
def normalize_item_name(item_str):
|
| 94 |
+
"""Canonicalize plural/singular user text back to the config item id."""
|
| 95 |
+
for orig in items:
|
| 96 |
+
if item_str.lower() == orig.lower():
|
| 97 |
+
return orig
|
| 98 |
+
if orig.endswith("s") and item_str.lower() == orig[:-1].lower():
|
| 99 |
+
return orig
|
| 100 |
+
if (
|
| 101 |
+
not orig.endswith("s")
|
| 102 |
+
and item_str.lower() == orig.lower() + "s"
|
| 103 |
+
):
|
| 104 |
+
return orig
|
| 105 |
+
|
| 106 |
+
for num, item in _re.findall(inner_regex, m.group(1)):
|
| 107 |
+
items_given_to_self[normalize_item_name(item)] = int(num)
|
| 108 |
+
return Split(items_given_to_self=items_given_to_self)
|