USTC975 commited on
Commit
a06e98d
1 Parent(s): 1d838f0

build gradio app

Browse files
agentreview/backends/openai.py CHANGED
@@ -64,6 +64,7 @@ class OpenAIChat(IntelligenceBackend):
64
  def _get_response(self, messages):
65
  # Refer to https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/switching-endpoints for how to
66
  # make API calls
 
67
 
68
  if self.client_type == "openai":
69
  completion = self.client.chat.completions.create(
 
64
  def _get_response(self, messages):
65
  # Refer to https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/switching-endpoints for how to
66
  # make API calls
67
+ return 'hello' # FIXME: remove this line
68
 
69
  if self.client_type == "openai":
70
  completion = self.client.chat.completions.create(
agentreview/environments/paper_review.py CHANGED
@@ -57,7 +57,7 @@ class PaperReview(Conversation):
57
 
58
  if self._phases is not None:
59
  return self._phases
60
-
61
  reviewer_names = [name for name in self.player_names if name.startswith("Reviewer")]
62
 
63
  num_reviewers = len(reviewer_names)
@@ -180,13 +180,10 @@ class PaperReview(Conversation):
180
  "Phase V. (AC makes decisions).")
181
 
182
  else:
183
- logger.info(f"Phase {self.phase_index}: end of the speaking order. Move to Phase ({self.phase_index + 1}).")
184
  self.phase_index += 1
185
  self._current_turn += 1
186
 
187
-
188
-
189
-
190
  else:
191
  self._next_player_index += 1
192
 
@@ -200,7 +197,7 @@ class PaperReview(Conversation):
200
 
201
  def get_next_player(self) -> str:
202
  """Get the next player in the current phase."""
203
- speaking_order = self.phases[self.phase_index]["speaking_order"]
204
  next_player = speaking_order[self._next_player_index]
205
  return next_player
206
 
 
57
 
58
  if self._phases is not None:
59
  return self._phases
60
+
61
  reviewer_names = [name for name in self.player_names if name.startswith("Reviewer")]
62
 
63
  num_reviewers = len(reviewer_names)
 
180
  "Phase V. (AC makes decisions).")
181
 
182
  else:
183
+ print(f"Phase {self.phase_index}: end of the speaking order. Move to Phase ({self.phase_index + 1}).")
184
  self.phase_index += 1
185
  self._current_turn += 1
186
 
 
 
 
187
  else:
188
  self._next_player_index += 1
189
 
 
197
 
198
  def get_next_player(self) -> str:
199
  """Get the next player in the current phase."""
200
+ speaking_order = self.phases[self.phase_index]["speaking_order"]
201
  next_player = speaking_order[self._next_player_index]
202
  return next_player
203
 
agentreview/paper_review_arena.py CHANGED
@@ -101,6 +101,7 @@ class PaperReviewArena(Arena):
101
  player.role_desc = get_reviewer_description(phase="reviewer_ac_discussion",
102
  **self.environment.experiment_setting["players"][
103
  'Reviewer'][reviewer_index - 1])
 
104
 
105
  elif self.environment.phase_index == 5: # Phase 5 AC Makes Decisions
106
 
 
101
  player.role_desc = get_reviewer_description(phase="reviewer_ac_discussion",
102
  **self.environment.experiment_setting["players"][
103
  'Reviewer'][reviewer_index - 1])
104
+
105
 
106
  elif self.environment.phase_index == 5: # Phase 5 AC Makes Decisions
107
 
agentreview/paper_review_message.py CHANGED
@@ -60,7 +60,7 @@ class PaperReviewMessagePool(MessagePool):
60
  visible_messages = []
61
 
62
  elif phase_index == 4:
63
- if agent_name.startswith("AC"):
64
  area_chair_type = self.experiment_setting['players']['AC'][0]["area_chair_type"]
65
 
66
  # 'BASELINE' means we do not specify the area chair's characteristics in the config file
@@ -86,7 +86,6 @@ class PaperReviewMessagePool(MessagePool):
86
  else:
87
  raise ValueError(f"Unknown Area chair type: {area_chair_type}.")
88
 
89
-
90
  else:
91
 
92
  visible_messages = []
 
60
  visible_messages = []
61
 
62
  elif phase_index == 4:
63
+ if agent_name.startswith("AC"):
64
  area_chair_type = self.experiment_setting['players']['AC'][0]["area_chair_type"]
65
 
66
  # 'BASELINE' means we do not specify the area chair's characteristics in the config file
 
86
  else:
87
  raise ValueError(f"Unknown Area chair type: {area_chair_type}.")
88
 
 
89
  else:
90
 
91
  visible_messages = []
agentreview/paper_review_player.py CHANGED
@@ -78,6 +78,7 @@ class PaperExtractorPlayer(Player):
78
  paper_decision: str,
79
  conference: str,
80
  backend: Union[BackendConfig, IntelligenceBackend],
 
81
  global_prompt: str = None,
82
  **kwargs,
83
  ):
@@ -85,6 +86,9 @@ class PaperExtractorPlayer(Player):
85
  self.paper_id = paper_id
86
  self.paper_decision = paper_decision
87
  self.conference: str = conference
 
 
 
88
 
89
  def act(self, observation: List[Message]) -> str:
90
  """
@@ -96,12 +100,17 @@ class PaperExtractorPlayer(Player):
96
  Returns:
97
  str: The action (response) of the player.
98
  """
99
-
100
- logging.info(f"Loading {self.conference} paper {self.paper_id} ({self.paper_decision}) ...")
 
 
101
 
102
  loader = PDFReader()
103
- document_path = Path(os.path.join(self.args.data_dir, self.conference, "paper", self.paper_decision,
104
- f"{self.paper_id}.pdf")) #
 
 
 
105
  documents = loader.load_data(file=document_path)
106
 
107
  num_words = 0
@@ -118,5 +127,7 @@ class PaperExtractorPlayer(Player):
118
  main_contents += text + ' '
119
  if FLAG:
120
  break
121
-
 
 
122
  return main_contents
 
78
  paper_decision: str,
79
  conference: str,
80
  backend: Union[BackendConfig, IntelligenceBackend],
81
+ paper_pdf_path: str = None,
82
  global_prompt: str = None,
83
  **kwargs,
84
  ):
 
86
  self.paper_id = paper_id
87
  self.paper_decision = paper_decision
88
  self.conference: str = conference
89
+
90
+ if paper_pdf_path is not None:
91
+ self.paper_pdf_path = paper_pdf_path
92
 
93
  def act(self, observation: List[Message]) -> str:
94
  """
 
100
  Returns:
101
  str: The action (response) of the player.
102
  """
103
+ if self.paper_pdf_path is not None:
104
+ logging.info(f"Loading paper from {self.paper_pdf_path} ...")
105
+ else:
106
+ logging.info(f"Loading {self.conference} paper {self.paper_id} ({self.paper_decision}) ...")
107
 
108
  loader = PDFReader()
109
+ if self.paper_pdf_path is not None:
110
+ document_path = Path(self.paper_pdf_path)
111
+ else:
112
+ document_path = Path(os.path.join(self.args.data_dir, self.conference, "paper", self.paper_decision,
113
+ f"{self.paper_id}.pdf")) #
114
  documents = loader.load_data(file=document_path)
115
 
116
  num_words = 0
 
127
  main_contents += text + ' '
128
  if FLAG:
129
  break
130
+
131
+ print(main_contents)
132
+
133
  return main_contents
agentreview/utility/authentication_utils.py CHANGED
@@ -16,13 +16,6 @@ def get_openai_client(client_type: str):
16
 
17
  assert client_type in ["azure_openai", "openai"]
18
 
19
- endpoint: str = os.environ['AZURE_ENDPOINT']
20
-
21
- if not endpoint.startswith("https://"):
22
- endpoint = f"https://{endpoint}.openai.azure.com"
23
-
24
- os.environ['AZURE_ENDPOINT'] = endpoint
25
-
26
  if not os.environ.get('OPENAI_API_VERSION'):
27
  os.environ['OPENAI_API_VERSION'] = "2023-05-15"
28
 
@@ -32,6 +25,13 @@ def get_openai_client(client_type: str):
32
  )
33
 
34
  elif client_type == "azure_openai":
 
 
 
 
 
 
 
35
  client = openai.AzureOpenAI(
36
  api_key=os.environ['AZURE_OPENAI_KEY'],
37
  azure_endpoint=os.environ['AZURE_ENDPOINT'], # f"https://YOUR_END_POINT.openai.azure.com"
 
16
 
17
  assert client_type in ["azure_openai", "openai"]
18
 
 
 
 
 
 
 
 
19
  if not os.environ.get('OPENAI_API_VERSION'):
20
  os.environ['OPENAI_API_VERSION'] = "2023-05-15"
21
 
 
25
  )
26
 
27
  elif client_type == "azure_openai":
28
+ endpoint: str = os.environ['AZURE_ENDPOINT']
29
+
30
+ if not endpoint.startswith("https://"):
31
+ endpoint = f"https://{endpoint}.openai.azure.com"
32
+
33
+ os.environ['AZURE_ENDPOINT'] = endpoint
34
+
35
  client = openai.AzureOpenAI(
36
  api_key=os.environ['AZURE_OPENAI_KEY'],
37
  azure_endpoint=os.environ['AZURE_ENDPOINT'], # f"https://YOUR_END_POINT.openai.azure.com"
app.py CHANGED
@@ -1,7 +1,613 @@
 
 
 
 
 
1
  import gradio as gr
2
 
3
- def echo_text(text):
4
- return text
5
 
6
- iface = gr.Interface(fn=echo_text, inputs="text", outputs="text")
7
- iface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import re
3
+ from glob import glob
4
+ from argparse import Namespace
5
+
6
  import gradio as gr
7
 
 
 
8
 
9
+ from agentreview import const
10
+ from agentreview.config import AgentConfig
11
+ from agentreview.agent import Player
12
+ from agentreview.backends import BACKEND_REGISTRY
13
+ from agentreview.environments import PaperReview
14
+ from agentreview.paper_review_arena import PaperReviewArena
15
+ from agentreview.utility.experiment_utils import initialize_players
16
+ from agentreview.paper_review_player import PaperExtractorPlayer, AreaChair, Reviewer
17
+ from agentreview.role_descriptions import get_reviewer_description, get_ac_description, get_author_config, get_paper_extractor_config
18
+
19
+ # 该文件的使命是前端交互:构建前端页面,从页面中获取用户的配置,传入后端运行,将结果实时展示到相应模块
20
+
21
+ css = """#col-container {max-width: 90%; margin-left: auto; margin-right: auto; display: flex; flex-direction: column;}
22
+ #header {text-align: center;}
23
+ #col-chatbox {flex: 1; max-height: min(900px, 100%);}
24
+ #label {font-size: 2em; padding: 0.5em; margin: 0;}
25
+ .message {font-size: 1.2em;}
26
+ .message-wrap {max-height: min(700px, 100vh);}
27
+ """
28
+ # .wrap {min-width: min(640px, 100vh)}
29
+ # #env-desc {max-height: 100px; overflow-y: auto;}
30
+ # .textarea {height: 100px; max-height: 100px;}
31
+ # #chatbot-tab-all {height: 750px; max-height: min(750px, 100%);}
32
+ # #chatbox {height: min(750px, 100%); max-height: min(750px, 100%);}
33
+ # #chatbox.block {height: 730px}
34
+ # .wrap {max-height: 680px;}
35
+ # .scroll-hide {overflow-y: scroll; max-height: 100px;}
36
+
37
+ DEBUG = False
38
+
39
+ DEFAULT_BACKEND = "openai-chat"
40
+ MAX_NUM_PLAYERS = 4
41
+ DEFAULT_NUM_PLAYERS = 4
42
+ CURRENT_STEP_INDEX = 0
43
+
44
+ def load_examples():
45
+ example_configs = {}
46
+ # Load json config files from examples folder
47
+ example_files = glob("examples/*.json")
48
+ for example_file in example_files:
49
+ with open(example_file, encoding="utf-8") as f:
50
+ example = json.load(f)
51
+ try:
52
+ example_configs[example["name"]] = example
53
+ except KeyError:
54
+ print(f"Example {example_file} is missing a name field. Skipping.")
55
+ return example_configs
56
+
57
+
58
+ EXAMPLE_REGISTRY = load_examples()
59
+
60
+ # DB = SupabaseDB() if supabase_available else None
61
+
62
+ def get_player_components(name, visible):
63
+ with gr.Row():
64
+ with gr.Column():
65
+ role_name = gr.Textbox(
66
+ lines=1,
67
+ show_label=False,
68
+ interactive=True,
69
+ visible=False,
70
+ value=name,
71
+ )
72
+
73
+ # is benign, is_knowledgeable, is_responsible,
74
+ # player_config = gr.CheckboxGroup(
75
+ # choices=["Benign", "Knowledgeable", "Responsible"],
76
+ # label="Reviewer Type",
77
+ # visible=visible,
78
+ # )
79
+
80
+ with gr.Row():
81
+ # 将三个属性做成dropdown
82
+ Intention_config = gr.Dropdown(
83
+ choices=["Benign", "Malicious", "Neutral"],
84
+ interactive=True,
85
+ label = "Intention",
86
+ show_label=True,
87
+ value="Neutral",
88
+ )
89
+
90
+ Knowledge_config = gr.Dropdown(
91
+ choices=["Knowledgeable", "Unknownledgeable", "Normal"],
92
+ interactive=True,
93
+ label = "Knowledgeability",
94
+ show_label=True,
95
+ value="Normal",
96
+ )
97
+
98
+ Responsibility_config = gr.Dropdown(
99
+ choices=["Responsible", "Lazy", "Normal"],
100
+ interactive=True,
101
+ label = "Responsibility",
102
+ show_label=True,
103
+ value="Normal",
104
+ )
105
+
106
+
107
+ role_desc = gr.Textbox(
108
+ lines=8,
109
+ max_lines=8,
110
+ show_label=False,
111
+ interactive=True,
112
+ visible=visible,
113
+ autoscroll=False,
114
+ value=get_reviewer_description()
115
+ )
116
+
117
+ # role_desc = gr.Markdown(value=get_reviewer_description(),
118
+ # visible=visible)
119
+
120
+ def update_role_desc(Intention_config, Knowledge_config, Responsibility_config):
121
+
122
+ is_benign = True if Intention_config == "Benign" else (False if Intention_config == "Malicious" else None)
123
+ is_knowledgeable = True if Knowledge_config == "Knowledgeable" else (False if Knowledge_config == "Unknownledgeable" else None)
124
+ is_responsible = True if Responsibility_config == "Responsible" else (False if Responsibility_config == "Lazy" else None)
125
+
126
+ phase = 'reviewer_write_reviews' if CURRENT_STEP_INDEX < 2 else 'reviewer_ac_discussion'
127
+ return get_reviewer_description(is_benign, is_knowledgeable, is_responsible, phase=phase) # FIXME:依据阶段变化
128
+
129
+ Intention_config.select(fn=update_role_desc, inputs=[Intention_config, Knowledge_config, Responsibility_config], outputs=[role_desc])
130
+ Knowledge_config.select(fn=update_role_desc, inputs=[Intention_config, Knowledge_config, Responsibility_config], outputs=[role_desc])
131
+ Responsibility_config.select(fn=update_role_desc, inputs=[Intention_config, Knowledge_config, Responsibility_config], outputs=[role_desc])
132
+
133
+ with gr.Column():
134
+ backend_type = gr.Dropdown(
135
+ show_label=False,
136
+ choices=list(BACKEND_REGISTRY.keys()),
137
+ interactive=True,
138
+ visible=visible,
139
+ value=DEFAULT_BACKEND,
140
+ )
141
+ with gr.Accordion(
142
+ f"{name} Parameters", open=False, visible=visible
143
+ ) as accordion:
144
+ temperature = gr.Slider(
145
+ minimum=0,
146
+ maximum=2.0,
147
+ step=0.1,
148
+ interactive=True,
149
+ visible=visible,
150
+ label="temperature",
151
+ value=0.7,
152
+ )
153
+ max_tokens = gr.Slider(
154
+ minimum=10,
155
+ maximum=500,
156
+ step=10,
157
+ interactive=True,
158
+ visible=visible,
159
+ label="max tokens",
160
+ value=200,
161
+ )
162
+
163
+ return [role_name, Intention_config, Knowledge_config, Responsibility_config, backend_type, accordion, temperature, max_tokens]
164
+
165
+ def get_area_chair_components(name, visible):
166
+ with gr.Row():
167
+ with gr.Column():
168
+
169
+ role_name = gr.Textbox(
170
+ lines=1,
171
+ show_label=False,
172
+ interactive=True,
173
+ visible=False,
174
+ value=name,
175
+ )
176
+
177
+ AC_type = gr.Dropdown(
178
+ label = "AC Type",
179
+ show_label=True,
180
+ choices=["Inclusive", "Conformist", "Authoritarian", "Normal"],
181
+ interactive=True,
182
+ visible=visible,
183
+ value="Normal",
184
+ )
185
+
186
+ role_desc = gr.Textbox(
187
+ lines=8,
188
+ max_lines=8,
189
+ show_label=False,
190
+ interactive=True,
191
+ visible=visible,
192
+ value=get_ac_description("BASELINE", "ac_write_metareviews", 'None', 1),
193
+ )
194
+
195
+ def update_role_desc(AC_type):
196
+ ac_type = 'BASELINE' if AC_type == "Normal" else AC_type.lower()
197
+ return get_ac_description(ac_type, "ac_write_metareviews", "None", 1) # FIXME:依据阶段变化
198
+
199
+ AC_type.select(fn=update_role_desc, inputs=[AC_type], outputs=[role_desc])
200
+
201
+ with gr.Column():
202
+ backend_type = gr.Dropdown(
203
+ show_label=False,
204
+ choices=list(BACKEND_REGISTRY.keys()),
205
+ interactive=True,
206
+ visible=visible,
207
+ value=DEFAULT_BACKEND,
208
+ )
209
+ with gr.Accordion(
210
+ f"{name} Parameters", open=False, visible=visible
211
+ ) as accordion:
212
+ temperature = gr.Slider(
213
+ minimum=0,
214
+ maximum=2.0,
215
+ step=0.1,
216
+ interactive=True,
217
+ visible=visible,
218
+ label="temperature",
219
+ value=0.7,
220
+ )
221
+ max_tokens = gr.Slider(
222
+ minimum=10,
223
+ maximum=500,
224
+ step=10,
225
+ interactive=True,
226
+ visible=visible,
227
+ label="max tokens",
228
+ value=200,
229
+ )
230
+
231
+ return [role_name, AC_type, backend_type, accordion, temperature, max_tokens]
232
+
233
+
234
+ def get_empty_state():
235
+ return gr.State({"arena": None})
236
+
237
+
238
+ with gr.Blocks(css=css) as demo:
239
+ state = get_empty_state()
240
+ all_components = []
241
+
242
+ with gr.Column(elem_id="col-container"):
243
+ gr.Markdown(
244
+ """# 🤖 AgentReview<br>
245
+ Using Multi-Agent to review your paper!.
246
+ **[Project Homepage](https://github.com/Ahren09/AgentReview)**""",
247
+ elem_id="header",
248
+ )
249
+
250
+ # Environment configuration
251
+ env_desc_textbox = gr.Textbox(
252
+ show_label=True,
253
+ lines=2,
254
+ visible=True,
255
+ label="Environment Description",
256
+ interactive=True,
257
+ # placeholder="Enter a description of a scenario or the game rules.",
258
+ value=const.GLOBAL_PROMPT,
259
+ )
260
+
261
+ all_components += [env_desc_textbox]
262
+
263
+ with gr.Row():
264
+ with gr.Column(elem_id="col-chatbox"):
265
+ with gr.Tab("All", visible=True):
266
+ chatbot = gr.Chatbot(
267
+ elem_id="chatbox", visible=True, show_label=False, height=600
268
+ )
269
+
270
+ player_chatbots = []
271
+ for i in range(MAX_NUM_PLAYERS):
272
+ player_name = f"Reviewer {i + 1}" if i < MAX_NUM_PLAYERS-1 else "AC"
273
+ with gr.Tab(player_name, visible=(i < DEFAULT_NUM_PLAYERS)):
274
+ player_chatbot = gr.Chatbot(
275
+ elem_id=f"chatbox-{i}",
276
+ visible=i < DEFAULT_NUM_PLAYERS,
277
+ label=player_name,
278
+ show_label=False,
279
+ height=600, # FIXME: 无效设置
280
+ )
281
+ player_chatbots.append(player_chatbot)
282
+
283
+ all_components += [chatbot, *player_chatbots]
284
+
285
+ with gr.Column(elem_id="col-config"): # Player Configuration
286
+ # gr.Markdown("Player Configuration")
287
+
288
+ # parallel_checkbox = gr.Checkbox(
289
+ # label="Parallel Actions", value=False, visible=True
290
+ # )
291
+
292
+ all_players_components, players_idx2comp = [], {}
293
+ with gr.Blocks():
294
+ for i in range(MAX_NUM_PLAYERS):
295
+
296
+ player_name = f"Reviewer {i + 1}" if i < MAX_NUM_PLAYERS-1 else "AC"
297
+ with gr.Tab(
298
+ player_name, visible=(i < DEFAULT_NUM_PLAYERS)
299
+ ) as tab:
300
+ if player_name != "AC":
301
+ player_comps = get_player_components(
302
+ player_name, visible=(i < DEFAULT_NUM_PLAYERS)
303
+ )
304
+ else:
305
+ player_comps = get_area_chair_components(
306
+ player_name, visible=(i < DEFAULT_NUM_PLAYERS)
307
+ )
308
+
309
+ players_idx2comp[i] = player_comps + [tab]
310
+ all_players_components += player_comps + [tab]
311
+
312
+ all_components += all_players_components
313
+
314
+ # human_input_textbox = gr.Textbox(
315
+ # show_label=True,
316
+ # label="Human Input",
317
+ # lines=1,
318
+ # visible=True,
319
+ # interactive=True,
320
+ # placeholder="Upload your paper here",
321
+ # )
322
+
323
+ upload_file_box = gr.File(
324
+ visible=True,
325
+ height = 100,
326
+ )
327
+
328
+ with gr.Row():
329
+ btn_step = gr.Button("Submit")
330
+ btn_restart = gr.Button("Clear")
331
+
332
+ all_components += [upload_file_box, btn_step, btn_restart]
333
+
334
+
335
+ def _convert_to_chatbot_output(all_messages, display_recv=False):
336
+ chatbot_output = []
337
+ for i, message in enumerate(all_messages):
338
+ agent_name, msg, recv = (
339
+ message.agent_name,
340
+ message.content,
341
+ str(message.visible_to),
342
+ )
343
+ new_msg = re.sub(
344
+ r"\n+", "<br>", msg.strip()
345
+ ) # Preprocess message for chatbot output
346
+ if display_recv:
347
+ new_msg = f"**{agent_name} (-> {recv})**: {new_msg}" # Add role to the message
348
+ else:
349
+ new_msg = f"**{agent_name}**: {new_msg}"
350
+
351
+ if agent_name == "Moderator":
352
+ chatbot_output.append((new_msg, None))
353
+ else:
354
+ chatbot_output.append((None, new_msg))
355
+ return chatbot_output
356
+
357
+ def _create_arena_config_from_components(all_comps: dict):
358
+
359
+ env_desc = all_comps[env_desc_textbox]
360
+ paper_pdf_path = all_comps[upload_file_box]
361
+
362
+ # Step 1: Initialize the players
363
+ num_players = MAX_NUM_PLAYERS
364
+
365
+ # 为了适应之前的接口填充无意义数据
366
+ conference = "EMNLP 2024"
367
+ paper_decision = "Accept"
368
+ data_dir = ''
369
+ paper_id = "12345"
370
+ args = Namespace(openai_client_type="openai",
371
+ experiment_name="test",
372
+ max_num_words=16384)
373
+
374
+ # 在paper_decision 阶段 中只启用 AC
375
+ players = []
376
+
377
+ # 不能直接获取role_desc,需要根据Intention_config, Knowledge_config, Responsibility_config生成一个配置
378
+ # self.environment.experiment_setting["players"]['Reviewer'][reviewer_index - 1]
379
+
380
+ experiment_setting = {
381
+ "paper_id": paper_id,
382
+ "paper_decision": paper_decision,
383
+ "players": {
384
+
385
+ # Paper Extractor is a special player that extracts a paper from the dataset.
386
+ # Its constructor does not take any arguments.
387
+ "Paper Extractor": [{}],
388
+
389
+ # Assume there is only one area chair (AC) in the experiment.
390
+ "AC": [],
391
+
392
+ # Author role with default configuration.
393
+ "Author": [{}],
394
+
395
+ # Reviewer settings are generated based on reviewer types provided in the settings.
396
+ "Reviewer": [],
397
+ },
398
+ # "global_settings": setting['global_settings']
399
+ }
400
+
401
+
402
+ for i in range(num_players):
403
+ if i < num_players-1: # reviewer
404
+ role_name, intention_config, knowledge_config, responsibility_config, backend_type, temperature, max_tokens = (
405
+ all_comps[c]
406
+ for c in players_idx2comp[i]
407
+ if not isinstance(c, (gr.Accordion, gr.Tab))
408
+ )
409
+
410
+ is_benign = True if intention_config == "Benign" else (False if intention_config == "Malicious" else None)
411
+ is_knowledgeable = True if knowledge_config == "Knowledgeable" else (False if knowledge_config == "Unknownledgeable" else None)
412
+ is_responsible = True if responsibility_config == "Responsible" else (False if responsibility_config == "Lazy" else None)
413
+
414
+ experiment_setting["players"]['Reviewer'].append({"is_benign": is_benign,
415
+ "is_knowledgeable": is_knowledgeable,
416
+ "is_responsible": is_responsible,
417
+ "knows_authors": 'unfamous'})
418
+
419
+ role_desc = get_reviewer_description(is_benign, is_knowledgeable, is_responsible)
420
+
421
+ if i == num_players-1: # AC
422
+ role_name, ac_type, backend_type, temperature, max_tokens = (
423
+ all_comps[c]
424
+ for c in players_idx2comp[i]
425
+ if not isinstance(c, (gr.Accordion, gr.Tab))
426
+ )
427
+
428
+ ac_type = 'BASELINE' if ac_type == "Normal" else ac_type.lower()
429
+
430
+ experiment_setting["players"]['AC'].append({"area_chair_type": ac_type})
431
+
432
+ role_desc = get_ac_description(ac_type, "ac_write_metareviews", "None", 1)
433
+
434
+ # common config for all players
435
+ player_config = {
436
+ "name": role_name,
437
+ "role_desc": role_desc,
438
+ "global_prompt": env_desc,
439
+ "backend": {
440
+ "backend_type": backend_type,
441
+ "temperature": temperature,
442
+ "max_tokens": max_tokens,
443
+ },
444
+ }
445
+
446
+ player_config = AgentConfig(**player_config)
447
+
448
+ if i < num_players-1:
449
+ player = Reviewer(data_dir=data_dir, conference=conference, args=args, **player_config)
450
+ else:
451
+ player_config["env_type"] = "paper_review"
452
+ player = AreaChair(data_dir=data_dir, conference=conference, args=args, **player_config)
453
+
454
+ players.append(player)
455
+
456
+ # 根据上面的player_config和人造生成该阶段的players
457
+ # if CURRENT_STEP == "paper_review":
458
+
459
+ # 人为加入paper extractor
460
+ paper_extractor_config = get_paper_extractor_config(max_tokens=2048)
461
+
462
+ paper_extractor = PaperExtractorPlayer( paper_pdf_path=paper_pdf_path,
463
+ data_dir=data_dir, paper_id=paper_id,
464
+ paper_decision=paper_decision, args=args,
465
+ conference=conference, **paper_extractor_config)
466
+ players.append(paper_extractor)
467
+
468
+ # 人为加入author
469
+ author_config = get_author_config()
470
+ author = Player(data_dir=data_dir, conference=conference, args=args,
471
+ **author_config)
472
+
473
+ players.append(author)
474
+
475
+
476
+ player_names = [player.name for player in players]
477
+
478
+ # Step 2: Initialize the environment
479
+ env = PaperReview(player_names=player_names, paper_decision=paper_decision, paper_id=paper_id,
480
+ args=args, experiment_setting=experiment_setting)
481
+
482
+ # Step 3: Initialize the Arena
483
+ arena = PaperReviewArena(players=players, environment=env, args=args, global_prompt=env_desc)
484
+
485
+ return arena
486
+
487
+
488
+ def step_game(all_comps: dict):
489
+ global CURRENT_STEP_INDEX
490
+
491
+ yield {
492
+ btn_step: gr.update(value="Running...", interactive=False),
493
+ btn_restart: gr.update(interactive=False),
494
+ }
495
+
496
+ cur_state = all_comps[state]
497
+
498
+ # If arena is not yet created, create it
499
+ if cur_state["arena"] is None:
500
+ # Create the Arena
501
+ arena = _create_arena_config_from_components(all_comps)
502
+ cur_state["arena"] = arena
503
+ else:
504
+ arena = cur_state["arena"]
505
+
506
+ # 当timestep.terminal 为真时才停止运行
507
+ # TODO: 连续运行
508
+
509
+ timestep = arena.step()
510
+
511
+ CURRENT_STEP_INDEX = int(arena.environment.phase_index)
512
+
513
+ # 更新前端信息
514
+ if timestep:
515
+ all_messages = timestep.observation
516
+ all_messages[0].content = 'Paper content has been extracted.'
517
+ chatbot_output = _convert_to_chatbot_output(all_messages, display_recv=True)
518
+ update_dict = {
519
+ chatbot: chatbot_output,
520
+ btn_step: gr.update(
521
+ value="Next Step", interactive=not timestep.terminal
522
+ ),
523
+ btn_restart: gr.update(interactive=True),
524
+ state: cur_state,
525
+ }
526
+
527
+ # Reviewer 1, 2, 3 Area Chair, Paper Extractor, Author
528
+
529
+ for i, player in enumerate(arena.players):
530
+ if 'Reviewer' in player.name and arena.environment.phase_index < 4: # FIXME: 临时逻辑
531
+ player_messages = arena.environment.get_observation(player.name)
532
+ # 不要显示第一条长段的信息,只显示 文章内容已被抽取
533
+ player_messages[0].content = 'Paper content has been extracted.'
534
+ player_output = _convert_to_chatbot_output(player_messages)
535
+ # Update the player's chatbot output
536
+ update_dict[player_chatbots[i]] = player_output
537
+ elif arena.environment.phase_index in [4, 5]: # FIXME: 临时逻辑
538
+ player_messages = arena.environment.get_observation('AC')
539
+ player_messages[0].content = 'Paper content has been extracted.'
540
+ player_output = _convert_to_chatbot_output(player_messages)
541
+ # Update the player's chatbot output
542
+ update_dict[player_chatbots[3]] = player_output
543
+
544
+ yield update_dict
545
+
546
+
547
+ def restart_game(all_comps: dict):
548
+ global CURRENT_STEP_INDEX
549
+ CURRENT_STEP_INDEX = 0
550
+
551
+ cur_state = all_comps[state]
552
+ cur_state["arena"] = None
553
+ yield {
554
+ chatbot: [],
555
+ btn_restart: gr.update(interactive=False),
556
+ btn_step: gr.update(interactive=False),
557
+ state: cur_state,
558
+ }
559
+
560
+ # arena_config = _create_arena_config_from_components(all_comps)
561
+ # arena = Arena.from_config(arena_config)
562
+ # log_arena(arena, database=DB)
563
+ # cur_state["arena"] = arena
564
+
565
+ yield {
566
+ btn_step: gr.update(value="Start", interactive=True),
567
+ btn_restart: gr.update(interactive=True),
568
+ upload_file_box: gr.update(value=None),
569
+ state: cur_state,
570
+ }
571
+
572
+ # Remove Accordion and Tab from the list of components
573
+ all_components = [
574
+ comp for comp in all_components if not isinstance(comp, (gr.Accordion, gr.Tab))
575
+ ]
576
+
577
+ # update component
578
+ # env_desc_textbox.change()
579
+
580
+ # If any of the Textbox, Slider, Checkbox, Dropdown, RadioButtons is changed, the Step button is disabled
581
+ for comp in all_components:
582
+
583
+ def _disable_step_button(state):
584
+ if state["arena"] is not None:
585
+ return gr.update(interactive=False)
586
+ else:
587
+ return gr.update()
588
+
589
+ if (
590
+ isinstance(
591
+ comp, (gr.Textbox, gr.Slider, gr.Checkbox, gr.Dropdown, gr.Radio)
592
+ )
593
+ and comp is not upload_file_box
594
+ ):
595
+ comp.change(_disable_step_button, state, btn_step)
596
+
597
+
598
+ # print(set(all_components + [state]))
599
+ btn_step.click(
600
+ step_game,
601
+ set(all_components + [state]),
602
+ [chatbot, *player_chatbots, btn_step, btn_restart, state, upload_file_box],
603
+ )
604
+
605
+ btn_restart.click(
606
+ restart_game,
607
+ set(all_components + [state]),
608
+ [chatbot, *player_chatbots, btn_step, btn_restart, state, upload_file_box],
609
+ )
610
+
611
+
612
+ demo.queue()
613
+ demo.launch(debug=DEBUG, server_port=8082)
template.py ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import re
3
+ from glob import glob
4
+
5
+ import gradio as gr
6
+
7
+ from chatarena.arena import Arena, TooManyInvalidActions
8
+ from chatarena.backends import BACKEND_REGISTRY
9
+ from chatarena.backends.human import HumanBackendError
10
+ from chatarena.config import ArenaConfig
11
+ from chatarena.database import SupabaseDB, log_arena, log_messages, supabase_available
12
+ from chatarena.environments import ENV_REGISTRY
13
+ from chatarena.message import Message
14
+
15
+ css = """#col-container {max-width: 90%; margin-left: auto; margin-right: auto; display: flex; flex-direction: column;}
16
+ #header {text-align: center;}
17
+ #col-chatbox {flex: 1; max-height: min(750px, 100%);}
18
+ #label {font-size: 2em; padding: 0.5em; margin: 0;}
19
+ .message {font-size: 1.2em;}
20
+ .message-wrap {max-height: min(700px, 100vh);}
21
+ """
22
+ # .wrap {min-width: min(640px, 100vh)}
23
+ # #env-desc {max-height: 100px; overflow-y: auto;}
24
+ # .textarea {height: 100px; max-height: 100px;}
25
+ # #chatbot-tab-all {height: 750px; max-height: min(750px, 100%);}
26
+ # #chatbox {height: min(750px, 100%); max-height: min(750px, 100%);}
27
+ # #chatbox.block {height: 730px}
28
+ # .wrap {max-height: 680px;}
29
+ # .scroll-hide {overflow-y: scroll; max-height: 100px;}
30
+
31
+
32
+ DEBUG = False
33
+
34
+ DEFAULT_BACKEND = "openai-chat"
35
+ DEFAULT_ENV = "conversation"
36
+ MAX_NUM_PLAYERS = 6
37
+ DEFAULT_NUM_PLAYERS = 2
38
+
39
+
40
+ def load_examples():
41
+ example_configs = {}
42
+ # Load json config files from examples folder
43
+ example_files = glob("examples/*.json")
44
+ for example_file in example_files:
45
+ with open(example_file, encoding="utf-8") as f:
46
+ example = json.load(f)
47
+ try:
48
+ example_configs[example["name"]] = example
49
+ except KeyError:
50
+ print(f"Example {example_file} is missing a name field. Skipping.")
51
+ return example_configs
52
+
53
+
54
+ EXAMPLE_REGISTRY = load_examples()
55
+
56
+ DB = SupabaseDB() if supabase_available else None
57
+
58
+
59
+ def get_moderator_components(visible=True):
60
+ name = "Moderator"
61
+ with gr.Row():
62
+ with gr.Column():
63
+ role_desc = gr.Textbox(
64
+ label="Moderator role",
65
+ lines=1,
66
+ visible=visible,
67
+ interactive=True,
68
+ placeholder=f"Enter the role description for {name}",
69
+ )
70
+ terminal_condition = gr.Textbox(
71
+ show_label=False,
72
+ lines=1,
73
+ visible=visible,
74
+ interactive=True,
75
+ placeholder="Enter the termination criteria",
76
+ )
77
+ with gr.Column():
78
+ backend_type = gr.Dropdown(
79
+ show_label=False,
80
+ visible=visible,
81
+ interactive=True,
82
+ choices=list(BACKEND_REGISTRY.keys()),
83
+ value=DEFAULT_BACKEND,
84
+ )
85
+ with gr.Accordion(
86
+ f"{name} Parameters", open=False, visible=visible
87
+ ) as accordion:
88
+ temperature = gr.Slider(
89
+ minimum=0,
90
+ maximum=2.0,
91
+ step=0.1,
92
+ interactive=True,
93
+ visible=visible,
94
+ label="temperature",
95
+ value=0.7,
96
+ )
97
+ max_tokens = gr.Slider(
98
+ minimum=10,
99
+ maximum=500,
100
+ step=10,
101
+ interactive=True,
102
+ visible=visible,
103
+ label="max tokens",
104
+ value=200,
105
+ )
106
+
107
+ return [
108
+ role_desc,
109
+ terminal_condition,
110
+ backend_type,
111
+ accordion,
112
+ temperature,
113
+ max_tokens,
114
+ ]
115
+
116
+
117
+ def get_player_components(name, visible):
118
+ with gr.Row():
119
+ with gr.Column():
120
+ role_name = gr.Textbox(
121
+ line=1,
122
+ show_label=False,
123
+ interactive=True,
124
+ visible=visible,
125
+ placeholder=f"Player name for {name}",
126
+ )
127
+ role_desc = gr.Textbox(
128
+ lines=3,
129
+ show_label=False,
130
+ interactive=True,
131
+ visible=visible,
132
+ placeholder=f"Enter the role description for {name}",
133
+ )
134
+ with gr.Column():
135
+ backend_type = gr.Dropdown(
136
+ show_label=False,
137
+ choices=list(BACKEND_REGISTRY.keys()),
138
+ interactive=True,
139
+ visible=visible,
140
+ value=DEFAULT_BACKEND,
141
+ )
142
+ with gr.Accordion(
143
+ f"{name} Parameters", open=False, visible=visible
144
+ ) as accordion:
145
+ temperature = gr.Slider(
146
+ minimum=0,
147
+ maximum=2.0,
148
+ step=0.1,
149
+ interactive=True,
150
+ visible=visible,
151
+ label="temperature",
152
+ value=0.7,
153
+ )
154
+ max_tokens = gr.Slider(
155
+ minimum=10,
156
+ maximum=500,
157
+ step=10,
158
+ interactive=True,
159
+ visible=visible,
160
+ label="max tokens",
161
+ value=200,
162
+ )
163
+
164
+ return [role_name, role_desc, backend_type, accordion, temperature, max_tokens]
165
+
166
+
167
+ def get_empty_state():
168
+ return gr.State({"arena": None})
169
+
170
+
171
+ with gr.Blocks(css=css) as demo:
172
+ state = get_empty_state()
173
+ all_components = []
174
+
175
+ with gr.Column(elem_id="col-container"):
176
+ gr.Markdown(
177
+ """# 🏟 ChatArena️<br>
178
+ Prompting multiple AI agents to play games in a language-driven environment.
179
+ **[Project Homepage](https://github.com/chatarena/chatarena)**""",
180
+ elem_id="header",
181
+ )
182
+
183
+ with gr.Row():
184
+ env_selector = gr.Dropdown(
185
+ choices=list(ENV_REGISTRY.keys()),
186
+ value=DEFAULT_ENV,
187
+ interactive=True,
188
+ label="Environment Type",
189
+ show_label=True,
190
+ )
191
+ example_selector = gr.Dropdown(
192
+ choices=list(EXAMPLE_REGISTRY.keys()),
193
+ interactive=True,
194
+ label="Select Example",
195
+ show_label=True,
196
+ )
197
+
198
+ # Environment configuration
199
+ env_desc_textbox = gr.Textbox(
200
+ show_label=True,
201
+ lines=2,
202
+ visible=True,
203
+ label="Environment Description",
204
+ placeholder="Enter a description of a scenario or the game rules.",
205
+ )
206
+
207
+ all_components += [env_selector, example_selector, env_desc_textbox]
208
+
209
+ with gr.Row():
210
+ with gr.Column(elem_id="col-chatbox"):
211
+ with gr.Tab("All", visible=True):
212
+ chatbot = gr.Chatbot(
213
+ elem_id="chatbox", visible=True, show_label=False
214
+ )
215
+
216
+ player_chatbots = []
217
+ for i in range(MAX_NUM_PLAYERS):
218
+ player_name = f"Player {i + 1}"
219
+ with gr.Tab(player_name, visible=(i < DEFAULT_NUM_PLAYERS)):
220
+ player_chatbot = gr.Chatbot(
221
+ elem_id=f"chatbox-{i}",
222
+ visible=i < DEFAULT_NUM_PLAYERS,
223
+ label=player_name,
224
+ show_label=False,
225
+ )
226
+ player_chatbots.append(player_chatbot)
227
+
228
+ all_components += [chatbot, *player_chatbots]
229
+
230
+ with gr.Column(elem_id="col-config"): # Player Configuration
231
+ # gr.Markdown("Player Configuration")
232
+ parallel_checkbox = gr.Checkbox(
233
+ label="Parallel Actions", value=False, visible=True
234
+ )
235
+ with gr.Accordion("Moderator", open=False, visible=True):
236
+ moderator_components = get_moderator_components(True)
237
+ all_components += [parallel_checkbox, *moderator_components]
238
+
239
+ all_players_components, players_idx2comp = [], {}
240
+ with gr.Blocks():
241
+ num_player_slider = gr.Slider(
242
+ 2,
243
+ MAX_NUM_PLAYERS,
244
+ value=DEFAULT_NUM_PLAYERS,
245
+ step=1,
246
+ label="Number of players:",
247
+ )
248
+ for i in range(MAX_NUM_PLAYERS):
249
+ player_name = f"Player {i + 1}"
250
+ with gr.Tab(
251
+ player_name, visible=(i < DEFAULT_NUM_PLAYERS)
252
+ ) as tab:
253
+ player_comps = get_player_components(
254
+ player_name, visible=(i < DEFAULT_NUM_PLAYERS)
255
+ )
256
+
257
+ players_idx2comp[i] = player_comps + [tab]
258
+ all_players_components += player_comps + [tab]
259
+
260
+ all_components += [num_player_slider] + all_players_components
261
+
262
+ def variable_players(k):
263
+ k = int(k)
264
+ update_dict = {}
265
+ for i in range(MAX_NUM_PLAYERS):
266
+ if i < k:
267
+ for comp in players_idx2comp[i]:
268
+ update_dict[comp] = gr.update(visible=True)
269
+ update_dict[player_chatbots[i]] = gr.update(visible=True)
270
+ else:
271
+ for comp in players_idx2comp[i]:
272
+ update_dict[comp] = gr.update(visible=False)
273
+ update_dict[player_chatbots[i]] = gr.update(visible=False)
274
+ return update_dict
275
+
276
+ num_player_slider.change(
277
+ variable_players,
278
+ num_player_slider,
279
+ all_players_components + player_chatbots,
280
+ )
281
+
282
+ human_input_textbox = gr.Textbox(
283
+ show_label=True,
284
+ label="Human Input",
285
+ lines=1,
286
+ visible=True,
287
+ interactive=True,
288
+ placeholder="Enter your input here",
289
+ )
290
+ with gr.Row():
291
+ btn_step = gr.Button("Start")
292
+ btn_restart = gr.Button("Clear")
293
+
294
+ all_components += [human_input_textbox, btn_step, btn_restart]
295
+
296
+ def _convert_to_chatbot_output(all_messages, display_recv=False):
297
+ chatbot_output = []
298
+ for i, message in enumerate(all_messages):
299
+ agent_name, msg, recv = (
300
+ message.agent_name,
301
+ message.content,
302
+ str(message.visible_to),
303
+ )
304
+ new_msg = re.sub(
305
+ r"\n+", "<br>", msg.strip()
306
+ ) # Preprocess message for chatbot output
307
+ if display_recv:
308
+ new_msg = f"**{agent_name} (-> {recv})**: {new_msg}" # Add role to the message
309
+ else:
310
+ new_msg = f"**{agent_name}**: {new_msg}"
311
+
312
+ if agent_name == "Moderator":
313
+ chatbot_output.append((new_msg, None))
314
+ else:
315
+ chatbot_output.append((None, new_msg))
316
+ return chatbot_output
317
+
318
+ def _create_arena_config_from_components(all_comps: dict) -> ArenaConfig:
319
+ env_desc = all_comps[env_desc_textbox]
320
+
321
+ # Initialize the players
322
+ num_players = all_comps[num_player_slider]
323
+ player_configs = []
324
+ for i in range(num_players):
325
+ role_name, role_desc, backend_type, temperature, max_tokens = (
326
+ all_comps[c]
327
+ for c in players_idx2comp[i]
328
+ if not isinstance(c, (gr.Accordion, gr.Tab))
329
+ )
330
+ player_config = {
331
+ "name": role_name,
332
+ "role_desc": role_desc,
333
+ "global_prompt": env_desc,
334
+ "backend": {
335
+ "backend_type": backend_type,
336
+ "temperature": temperature,
337
+ "max_tokens": max_tokens,
338
+ },
339
+ }
340
+ player_configs.append(player_config)
341
+
342
+ # Initialize the environment
343
+ env_type = all_comps[env_selector]
344
+ # Get moderator config
345
+ (
346
+ mod_role_desc,
347
+ mod_terminal_condition,
348
+ moderator_backend_type,
349
+ mod_temp,
350
+ mod_max_tokens,
351
+ ) = (
352
+ all_comps[c]
353
+ for c in moderator_components
354
+ if not isinstance(c, (gr.Accordion, gr.Tab))
355
+ )
356
+ moderator_config = {
357
+ "role_desc": mod_role_desc,
358
+ "global_prompt": env_desc,
359
+ "terminal_condition": mod_terminal_condition,
360
+ "backend": {
361
+ "backend_type": moderator_backend_type,
362
+ "temperature": mod_temp,
363
+ "max_tokens": mod_max_tokens,
364
+ },
365
+ }
366
+ env_config = {
367
+ "env_type": env_type,
368
+ "parallel": all_comps[parallel_checkbox],
369
+ "moderator": moderator_config,
370
+ "moderator_visibility": "all",
371
+ "moderator_period": None,
372
+ }
373
+
374
+ # arena_config = {"players": player_configs, "environment": env_config}
375
+ arena_config = ArenaConfig(players=player_configs, environment=env_config)
376
+ return arena_config
377
+
378
+ def step_game(all_comps: dict):
379
+ yield {
380
+ btn_step: gr.update(value="Running...", interactive=False),
381
+ btn_restart: gr.update(interactive=False),
382
+ }
383
+
384
+ cur_state = all_comps[state]
385
+
386
+ # If arena is not yet created, create it
387
+ if cur_state["arena"] is None:
388
+ # Create the Arena
389
+ arena_config = _create_arena_config_from_components(all_comps)
390
+ arena = Arena.from_config(arena_config)
391
+ log_arena(arena, database=DB)
392
+ cur_state["arena"] = arena
393
+ else:
394
+ arena = cur_state["arena"]
395
+
396
+ try:
397
+ timestep = arena.step()
398
+ except HumanBackendError as e:
399
+ # Handle human input and recover with the game update
400
+ human_input = all_comps[human_input_textbox]
401
+ if human_input == "":
402
+ timestep = None # Failed to get human input
403
+ else:
404
+ timestep = arena.environment.step(e.agent_name, human_input)
405
+ except TooManyInvalidActions:
406
+ timestep = arena.current_timestep
407
+ timestep.observation.append(
408
+ Message(
409
+ "System",
410
+ "Too many invalid actions. Game over.",
411
+ turn=-1,
412
+ visible_to="all",
413
+ )
414
+ )
415
+ timestep.terminal = True
416
+
417
+ if timestep is None:
418
+ yield {
419
+ human_input_textbox: gr.update(
420
+ value="", placeholder="Please enter a valid input"
421
+ ),
422
+ btn_step: gr.update(value="Next Step", interactive=True),
423
+ btn_restart: gr.update(interactive=True),
424
+ }
425
+ else:
426
+ all_messages = timestep.observation # user sees what the moderator sees
427
+ log_messages(arena, all_messages, database=DB)
428
+
429
+ chatbot_output = _convert_to_chatbot_output(all_messages, display_recv=True)
430
+ update_dict = {
431
+ human_input_textbox: gr.Textbox.update(value=""),
432
+ chatbot: chatbot_output,
433
+ btn_step: gr.update(
434
+ value="Next Step", interactive=not timestep.terminal
435
+ ),
436
+ btn_restart: gr.update(interactive=True),
437
+ state: cur_state,
438
+ }
439
+ # Get the visible messages for each player
440
+ for i, player in enumerate(arena.players):
441
+ player_messages = arena.environment.get_observation(player.name)
442
+ player_output = _convert_to_chatbot_output(player_messages)
443
+ # Update the player's chatbot output
444
+ update_dict[player_chatbots[i]] = player_output
445
+
446
+ if DEBUG:
447
+ arena.environment.print()
448
+
449
+ yield update_dict
450
+
451
+ def restart_game(all_comps: dict):
452
+ cur_state = all_comps[state]
453
+ cur_state["arena"] = None
454
+ yield {
455
+ chatbot: [],
456
+ btn_restart: gr.update(interactive=False),
457
+ btn_step: gr.update(interactive=False),
458
+ state: cur_state,
459
+ }
460
+
461
+ arena_config = _create_arena_config_from_components(all_comps)
462
+ arena = Arena.from_config(arena_config)
463
+ log_arena(arena, database=DB)
464
+ cur_state["arena"] = arena
465
+
466
+ yield {
467
+ btn_step: gr.update(value="Start", interactive=True),
468
+ btn_restart: gr.update(interactive=True),
469
+ state: cur_state,
470
+ }
471
+
472
+ # Remove Accordion and Tab from the list of components
473
+ all_components = [
474
+ comp for comp in all_components if not isinstance(comp, (gr.Accordion, gr.Tab))
475
+ ]
476
+
477
+ # If any of the Textbox, Slider, Checkbox, Dropdown, RadioButtons is changed, the Step button is disabled
478
+ for comp in all_components:
479
+
480
+ def _disable_step_button(state):
481
+ if state["arena"] is not None:
482
+ return gr.update(interactive=False)
483
+ else:
484
+ return gr.update()
485
+
486
+ if (
487
+ isinstance(
488
+ comp, (gr.Textbox, gr.Slider, gr.Checkbox, gr.Dropdown, gr.Radio)
489
+ )
490
+ and comp is not human_input_textbox
491
+ ):
492
+ comp.change(_disable_step_button, state, btn_step)
493
+
494
+ btn_step.click(
495
+ step_game,
496
+ set(all_components + [state]),
497
+ [chatbot, *player_chatbots, btn_step, btn_restart, state, human_input_textbox],
498
+ )
499
+ btn_restart.click(
500
+ restart_game,
501
+ set(all_components + [state]),
502
+ [chatbot, *player_chatbots, btn_step, btn_restart, state, human_input_textbox],
503
+ )
504
+
505
+ # If an example is selected, update the components
506
+ def update_components_from_example(all_comps: dict):
507
+ example_name = all_comps[example_selector]
508
+ example_config = EXAMPLE_REGISTRY[example_name]
509
+ update_dict = {}
510
+
511
+ # Update the environment components
512
+ env_config = example_config["environment"]
513
+ update_dict[env_desc_textbox] = gr.update(value=example_config["global_prompt"])
514
+ update_dict[env_selector] = gr.update(value=env_config["env_type"])
515
+ update_dict[parallel_checkbox] = gr.update(value=env_config["parallel"])
516
+
517
+ # Update the moderator components
518
+ if "moderator" in env_config:
519
+ (
520
+ mod_role_desc,
521
+ mod_terminal_condition,
522
+ moderator_backend_type,
523
+ mod_temp,
524
+ mod_max_tokens,
525
+ ) = (
526
+ c
527
+ for c in moderator_components
528
+ if not isinstance(c, (gr.Accordion, gr.Tab))
529
+ )
530
+ update_dict[mod_role_desc] = gr.update(
531
+ value=env_config["moderator"]["role_desc"]
532
+ )
533
+ update_dict[mod_terminal_condition] = gr.update(
534
+ value=env_config["moderator"]["terminal_condition"]
535
+ )
536
+ update_dict[moderator_backend_type] = gr.update(
537
+ value=env_config["moderator"]["backend"]["backend_type"]
538
+ )
539
+ update_dict[mod_temp] = gr.update(
540
+ value=env_config["moderator"]["backend"]["temperature"]
541
+ )
542
+ update_dict[mod_max_tokens] = gr.update(
543
+ value=env_config["moderator"]["backend"]["max_tokens"]
544
+ )
545
+
546
+ # Update the player components
547
+ update_dict[num_player_slider] = gr.update(value=len(example_config["players"]))
548
+ for i, player_config in enumerate(example_config["players"]):
549
+ role_name, role_desc, backend_type, temperature, max_tokens = (
550
+ c
551
+ for c in players_idx2comp[i]
552
+ if not isinstance(c, (gr.Accordion, gr.Tab))
553
+ )
554
+
555
+ update_dict[role_name] = gr.update(value=player_config["name"])
556
+ update_dict[role_desc] = gr.update(value=player_config["role_desc"])
557
+ update_dict[backend_type] = gr.update(
558
+ value=player_config["backend"]["backend_type"]
559
+ )
560
+ update_dict[temperature] = gr.update(
561
+ value=player_config["backend"]["temperature"]
562
+ )
563
+ update_dict[max_tokens] = gr.update(
564
+ value=player_config["backend"]["max_tokens"]
565
+ )
566
+
567
+ return update_dict
568
+
569
+ example_selector.change(
570
+ update_components_from_example,
571
+ set(all_components + [state]),
572
+ all_components + [state],
573
+ )
574
+
575
+ demo.queue()
576
+ demo.launch(debug=DEBUG, server_port=8080)