Ngadou commited on
Commit
f53d329
·
verified ·
1 Parent(s): e29a427

Create web_ui.py

Browse files
Files changed (1) hide show
  1. web_ui.py +366 -0
web_ui.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pprint
3
+ import re
4
+ from typing import List, Optional, Union
5
+
6
+ from qwen_agent import Agent, MultiAgentHub
7
+ from qwen_agent.agents.user_agent import PENDING_USER_INPUT
8
+ from qwen_agent.gui.gradio_utils import format_cover_html
9
+ from qwen_agent.gui.utils import convert_fncall_to_text, convert_history_to_chatbot, get_avatar_image
10
+ from qwen_agent.llm.schema import CONTENT, FILE, IMAGE, NAME, ROLE, USER, Message
11
+ from qwen_agent.log import logger
12
+ from qwen_agent.utils.utils import print_traceback
13
+
14
+ class WebUI:
15
+ """A Common chatbot application for agent."""
16
+
17
+ def __init__(self, agent: Union[Agent, MultiAgentHub, List[Agent]], chatbot_config: Optional[dict] = None):
18
+ """
19
+ Initialization the chatbot.
20
+ Args:
21
+ agent: The agent or a list of agents,
22
+ supports various types of agents such as Assistant, GroupChat, Router, etc.
23
+ chatbot_config: The chatbot configuration.
24
+ Set the configuration as {'user.name': '', 'user.avatar': '', 'agent.avatar': '', 'input.placeholder': '', 'prompt.suggestions': []}.
25
+ """
26
+ chatbot_config = chatbot_config or {}
27
+
28
+ if isinstance(agent, MultiAgentHub):
29
+ self.agent_list = [agent for agent in agent.nonuser_agents]
30
+ self.agent_hub = agent
31
+ elif isinstance(agent, list):
32
+ self.agent_list = agent
33
+ self.agent_hub = None
34
+ else:
35
+ self.agent_list = [agent]
36
+ self.agent_hub = None
37
+
38
+ user_name = chatbot_config.get('user.name', 'user')
39
+ self.user_config = {
40
+ 'name': user_name,
41
+ 'avatar': chatbot_config.get(
42
+ 'user.avatar',
43
+ get_avatar_image(user_name),
44
+ ),
45
+ }
46
+
47
+ self.agent_config_list = [{
48
+ 'name': agent.name,
49
+ 'avatar': chatbot_config.get(
50
+ 'agent.avatar',
51
+ os.path.join(os.path.dirname(__file__), 'assets/logo.jpeg'),
52
+ ),
53
+ 'description': agent.description or "I'm a helpful assistant.",
54
+ } for agent in self.agent_list]
55
+
56
+ self.input_placeholder = chatbot_config.get('input.placeholder', '跟我聊聊吧~')
57
+ self.prompt_suggestions = chatbot_config.get('prompt.suggestions', [])
58
+ self.verbose = chatbot_config.get('verbose', False)
59
+
60
+ """
61
+ Run the chatbot.
62
+ Args:
63
+ messages: The chat history.
64
+ """
65
+
66
+ def run(self,
67
+ messages: List[Message] = None,
68
+ share: bool = False,
69
+ server_name: str = None,
70
+ server_port: int = None,
71
+ concurrency_limit: int = 80,
72
+ enable_mention: bool = False,
73
+ **kwargs):
74
+ self.run_kwargs = kwargs
75
+
76
+ from qwen_agent.gui.gradio import gr, mgr
77
+
78
+ customTheme = gr.themes.Default(
79
+ primary_hue=gr.themes.utils.colors.blue,
80
+ radius_size=gr.themes.utils.sizes.radius_none,
81
+ )
82
+
83
+ with gr.Blocks(
84
+ css=os.path.join(os.path.dirname(__file__), 'assets/appBot.css'),
85
+ theme=customTheme,
86
+ ) as demo:
87
+ history = gr.State([])
88
+
89
+ with gr.Row(elem_classes='container'):
90
+ with gr.Column(scale=4):
91
+ chatbot = mgr.Chatbot(value=convert_history_to_chatbot(messages=messages),
92
+ avatar_images=[
93
+ self.user_config,
94
+ self.agent_config_list,
95
+ ],
96
+ height=850,
97
+ avatar_image_width=80,
98
+ flushing=False,
99
+ show_copy_button=True,
100
+ latex_delimiters=[{
101
+ 'left': '\\(',
102
+ 'right': '\\)',
103
+ 'display': True
104
+ }, {
105
+ 'left': '\\begin{equation}',
106
+ 'right': '\\end{equation}',
107
+ 'display': True
108
+ }, {
109
+ 'left': '\\begin{align}',
110
+ 'right': '\\end{align}',
111
+ 'display': True
112
+ }, {
113
+ 'left': '\\begin{alignat}',
114
+ 'right': '\\end{alignat}',
115
+ 'display': True
116
+ }, {
117
+ 'left': '\\begin{gather}',
118
+ 'right': '\\end{gather}',
119
+ 'display': True
120
+ }, {
121
+ 'left': '\\begin{CD}',
122
+ 'right': '\\end{CD}',
123
+ 'display': True
124
+ }, {
125
+ 'left': '\\[',
126
+ 'right': '\\]',
127
+ 'display': True
128
+ }])
129
+
130
+ input = mgr.MultimodalInput(placeholder=self.input_placeholder, upload_button_props=dict(visible=False))
131
+
132
+ with gr.Column(scale=1):
133
+ if len(self.agent_list) > 1:
134
+ agent_selector = gr.Dropdown(
135
+ [(agent.name, i) for i, agent in enumerate(self.agent_list)],
136
+ label='Agents',
137
+ info='选择一个Agent',
138
+ value=0,
139
+ interactive=True,
140
+ )
141
+
142
+ agent_info_block = self._create_agent_info_block()
143
+
144
+ # agent_plugins_block = self._create_agent_plugins_block()
145
+
146
+ if self.prompt_suggestions:
147
+ gr.Examples(
148
+ label='推荐对话',
149
+ examples=self.prompt_suggestions,
150
+ inputs=[input],
151
+ )
152
+
153
+ if len(self.agent_list) > 1:
154
+ agent_selector.change(
155
+ fn=self.change_agent,
156
+ inputs=[agent_selector],
157
+ outputs=[agent_selector, agent_info_block, agent_plugins_block],
158
+ queue=False,
159
+ )
160
+
161
+ input_promise = input.submit(
162
+ fn=self.add_text,
163
+ inputs=[input, chatbot, history],
164
+ outputs=[input, chatbot, history],
165
+ queue=False,
166
+ )
167
+
168
+ if len(self.agent_list) > 1 and enable_mention:
169
+ input_promise = input_promise.then(
170
+ self.add_mention,
171
+ [chatbot, agent_selector],
172
+ [chatbot, agent_selector],
173
+ ).then(
174
+ self.agent_run,
175
+ [chatbot, history, agent_selector],
176
+ [chatbot, history, agent_selector],
177
+ )
178
+ else:
179
+ input_promise = input_promise.then(
180
+ self.agent_run,
181
+ [chatbot, history],
182
+ [chatbot, history],
183
+ )
184
+
185
+ input_promise.then(self.flushed, None, [input])
186
+
187
+ demo.load(None)
188
+
189
+ demo.queue(default_concurrency_limit=concurrency_limit).launch(share=share,
190
+ server_name=server_name,
191
+ server_port=server_port)
192
+
193
+ def change_agent(self, agent_selector):
194
+ yield agent_selector, self._create_agent_info_block(agent_selector), self._create_agent_plugins_block(
195
+ agent_selector)
196
+
197
+ def add_text(self, _input, _chatbot, _history):
198
+ from qwen_agent.gui.gradio import gr
199
+ if _input.text == "/clear":
200
+ _chatbot = []
201
+ _history.clear()
202
+ yield gr.update(interactive=False, value=""), _chatbot, _history
203
+ return
204
+
205
+ if _history:
206
+ gr.Warning("Only the most recent query is retained because multi-turn conversations are not currently supported.", duration=5)
207
+ _chatbot = []
208
+ _history.clear()
209
+
210
+ _history.append({
211
+ ROLE: USER,
212
+ CONTENT: [{
213
+ 'text': _input.text
214
+ }],
215
+ })
216
+
217
+ if self.user_config[NAME]:
218
+ _history[-1][NAME] = self.user_config[NAME]
219
+
220
+ if _input.files:
221
+ for file in _input.files:
222
+ if file.mime_type.startswith('image/'):
223
+ _history[-1][CONTENT].append({IMAGE: 'file://' + file.path})
224
+ else:
225
+ _history[-1][CONTENT].append({FILE: file.path})
226
+
227
+ _chatbot.append([_input, None])
228
+
229
+ yield gr.update(interactive=False, value=None), _chatbot, _history
230
+
231
+ def add_mention(self, _chatbot, _agent_selector):
232
+ if len(self.agent_list) == 1:
233
+ yield _chatbot, _agent_selector
234
+
235
+ query = _chatbot[-1][0].text
236
+ match = re.search(r'@\w+\b', query)
237
+ if match:
238
+ _agent_selector = self._get_agent_index_by_name(match.group()[1:])
239
+
240
+ agent_name = self.agent_list[_agent_selector].name
241
+
242
+ if ('@' + agent_name) not in query and self.agent_hub is None:
243
+ _chatbot[-1][0].text = '@' + agent_name + ' ' + query
244
+
245
+ yield _chatbot, _agent_selector
246
+
247
+ def agent_run(self, _chatbot, _history, _agent_selector=None):
248
+ if not _history:
249
+ if _agent_selector is not None:
250
+ yield _chatbot, _history, _agent_selector
251
+ else:
252
+ yield _chatbot, _history
253
+ return
254
+
255
+
256
+ if self.verbose:
257
+ logger.info('agent_run input:\n' + pprint.pformat(_history, indent=2))
258
+
259
+ num_input_bubbles = len(_chatbot) - 1
260
+ num_output_bubbles = 1
261
+ _chatbot[-1][1] = [None for _ in range(len(self.agent_list))]
262
+
263
+ agent_runner = self.agent_list[_agent_selector or 0]
264
+ if self.agent_hub:
265
+ agent_runner = self.agent_hub
266
+ responses = []
267
+ for responses in agent_runner.run(_history, **self.run_kwargs):
268
+ # usage = responses.usage
269
+ # responses = [Message(ASSISTANT, responses.output.choices[0].message.content)]
270
+
271
+ if not responses:
272
+ continue
273
+ if responses[-1][CONTENT] == PENDING_USER_INPUT:
274
+ logger.info('Interrupted. Waiting for user input!')
275
+ break
276
+
277
+ display_responses = convert_fncall_to_text(responses)
278
+ if not display_responses:
279
+ continue
280
+ if display_responses[-1][CONTENT] is None:
281
+ continue
282
+
283
+ while len(display_responses) > num_output_bubbles:
284
+ # Create a new chat bubble
285
+ _chatbot.append([None, None])
286
+ _chatbot[-1][1] = [None for _ in range(len(self.agent_list))]
287
+ num_output_bubbles += 1
288
+
289
+ assert num_output_bubbles == len(display_responses)
290
+ assert num_input_bubbles + num_output_bubbles == len(_chatbot)
291
+
292
+ for i, rsp in enumerate(display_responses):
293
+ agent_index = self._get_agent_index_by_name(rsp[NAME])
294
+ _chatbot[num_input_bubbles + i][1][agent_index] = rsp[CONTENT]
295
+
296
+ if len(self.agent_list) > 1:
297
+ _agent_selector = agent_index
298
+
299
+ if _agent_selector is not None:
300
+ yield _chatbot, _history, _agent_selector
301
+ else:
302
+ yield _chatbot, _history
303
+
304
+ if responses:
305
+ _history.extend([res for res in responses if res[CONTENT] != PENDING_USER_INPUT])
306
+
307
+ if _agent_selector is not None:
308
+ yield _chatbot, _history, _agent_selector
309
+ else:
310
+ yield _chatbot, _history
311
+
312
+ if self.verbose:
313
+ logger.info('agent_run response:\n' + pprint.pformat(responses, indent=2))
314
+
315
+ def flushed(self):
316
+ from qwen_agent.gui.gradio import gr
317
+
318
+ return gr.update(interactive=True)
319
+
320
+ def _get_agent_index_by_name(self, agent_name):
321
+ if agent_name is None:
322
+ return 0
323
+
324
+ try:
325
+ agent_name = agent_name.strip()
326
+ for i, agent in enumerate(self.agent_list):
327
+ if agent.name == agent_name:
328
+ return i
329
+ return 0
330
+ except Exception:
331
+ print_traceback()
332
+ return 0
333
+
334
+ def _create_agent_info_block(self, agent_index=0):
335
+ from qwen_agent.gui.gradio import gr
336
+
337
+ agent_config_interactive = self.agent_config_list[agent_index]
338
+
339
+ return gr.HTML(
340
+ format_cover_html(
341
+ bot_name=agent_config_interactive['name'],
342
+ bot_description=agent_config_interactive['description'],
343
+ bot_avatar=agent_config_interactive['avatar'],
344
+ ))
345
+
346
+ def _create_agent_plugins_block(self, agent_index=0):
347
+ from qwen_agent.gui.gradio import gr
348
+
349
+ agent_interactive = self.agent_list[agent_index]
350
+
351
+ if agent_interactive.function_map:
352
+ capabilities = [key for key in agent_interactive.function_map.keys()]
353
+ return gr.CheckboxGroup(
354
+ label='插件',
355
+ value=capabilities,
356
+ choices=capabilities,
357
+ interactive=False,
358
+ )
359
+
360
+ else:
361
+ return gr.CheckboxGroup(
362
+ label='插件',
363
+ value=[],
364
+ choices=[],
365
+ interactive=False,
366
+ )