cherrytest commited on
Commit
ce8b42c
1 Parent(s): 685153b
Files changed (8) hide show
  1. app.py +85 -0
  2. assets/app.css +147 -0
  3. assets/appBot.css +129 -0
  4. assets/logo.jpeg +0 -0
  5. assets/user.jpeg +0 -0
  6. patching.py +276 -0
  7. requirements.txt +3 -0
  8. web_ui.py +366 -0
app.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.system("pip install 'https://modelscope-studios.oss-cn-zhangjiakou.aliyuncs.com/SDK/gradio/gradio-4.44.0-py3-none-any.whl?OSSAccessKeyId=LTAI5tCGZWFdkWKivGKCtvTD&Expires=361727611665&Signature=iynlOFVFiaF3OmxatNMHUBPfb3o%3D'")
3
+ os.system("pip install starlette==0.38.6 fastapi==0.112.4")
4
+
5
+ from typing import List, Tuple, Union
6
+ from web_ui import WebUI
7
+ import math
8
+
9
+ from qwen_agent.agents import Assistant
10
+ from qwen_agent.tools.base import register_tool
11
+ from qwen_agent.tools.doc_parser import Record
12
+ from qwen_agent.tools.search_tools.base_search import RefMaterialOutput, BaseSearch
13
+ from qwen_agent.log import logger
14
+ from qwen_agent.gui.gradio import gr
15
+
16
+ POSITIVE_INFINITY = math.inf
17
+
18
+ @register_tool('no_search')
19
+ class NoSearch(BaseSearch):
20
+ def call(self, params: Union[str, dict], docs: List[Union[Record, str, List[str]]] = None, **kwargs) -> list:
21
+ """The basic search algorithm
22
+
23
+ Args:
24
+ params: The dict parameters.
25
+ docs: The list of parsed doc, each doc has unique url.
26
+
27
+ Returns:
28
+ The list of retrieved chunks from each doc.
29
+
30
+ """
31
+ params = self._verify_json_format_args(params)
32
+ # Compatible with the parameter passing of the qwen-agent version <= 0.0.3
33
+ max_ref_token = kwargs.get('max_ref_token', self.max_ref_token)
34
+
35
+ # The query is a string that may contain only the original question,
36
+ # or it may be a json string containing the generated keywords and the original question
37
+ if not docs:
38
+ return []
39
+ return self._get_the_front_part(docs, max_ref_token)
40
+
41
+ @staticmethod
42
+ def _get_the_front_part(docs: List[Record], max_ref_token: int) -> list:
43
+ all_tokens = 0
44
+ _ref_list = []
45
+ for doc in docs:
46
+ text = []
47
+ for page in doc.raw:
48
+ text.append(page.content)
49
+ all_tokens += page.token
50
+ now_ref_list = RefMaterialOutput(url=doc.url, text=text).to_dict()
51
+ _ref_list.append(now_ref_list)
52
+
53
+ logger.info(f'Using tokens: {all_tokens}')
54
+ if all_tokens > max_ref_token:
55
+ raise gr.Error(f"Your document files (around {all_tokens} tokens) exceed the maximum context length ({max_ref_token} tokens).")
56
+ return _ref_list
57
+
58
+ def sort_by_scores(self,
59
+ query: str,
60
+ docs: List[Record],
61
+ max_ref_token: int,
62
+ **kwargs) -> List[Tuple[str, int, float]]:
63
+ raise NotImplementedError
64
+
65
+ def app_gui():
66
+ # Define the agent
67
+ bot = Assistant(llm={
68
+ 'model': 'pre-qwen-turbo-1m-1101-blade-opt',
69
+ 'generate_cfg': {
70
+ 'max_input_tokens': 1000000,
71
+ 'max_retries': 10,
72
+ }},
73
+ name='Qwen-Turbo-1M',
74
+ description='Qwen-Turbo natively supports input length of up to 1M tokens. You can upload documents for Q&A, supporting file types: PDF/Word/PPT/TXT/HTML.',
75
+ rag_cfg={'max_ref_token': 1000000, 'rag_searchers': ['no_search']},
76
+ )
77
+ chatbot_config = {
78
+ 'input.placeholder': "Type \"/clear\" to clear the history",
79
+ 'verbose': True,
80
+ }
81
+ WebUI(bot, chatbot_config=chatbot_config).run()
82
+
83
+ if __name__ == '__main__':
84
+ import patching # patch qwen-agent to accelerate 1M processing
85
+ app_gui()
assets/app.css ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* code highlight: https://python-markdown.github.io/extensions/code_hilite/ */
2
+ .codehilite .hll { background-color: #ffffcc }
3
+ .codehilite { background: #f8f8f8; }
4
+ .codehilite .c { color: #408080; font-style: italic } /* Comment */
5
+ .codehilite .err { border: 1px solid #FF0000 } /* Error */
6
+ .codehilite .k { color: #008000; font-weight: bold } /* Keyword */
7
+ .codehilite .o { color: #666666 } /* Operator */
8
+ .codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
9
+ .codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
10
+ .codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
11
+ .codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
12
+ .codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
13
+ .codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
14
+ .codehilite .gd { color: #A00000 } /* Generic.Deleted */
15
+ .codehilite .ge { font-style: italic } /* Generic.Emph */
16
+ .codehilite .gr { color: #FF0000 } /* Generic.Error */
17
+ .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
18
+ .codehilite .gi { color: #00A000 } /* Generic.Inserted */
19
+ .codehilite .go { color: #888888 } /* Generic.Output */
20
+ .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
21
+ .codehilite .gs { font-weight: bold } /* Generic.Strong */
22
+ .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
23
+ .codehilite .gt { color: #0044DD } /* Generic.Traceback */
24
+ .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
25
+ .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
26
+ .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
27
+ .codehilite .kp { color: #008000 } /* Keyword.Pseudo */
28
+ .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
29
+ .codehilite .kt { color: #B00040 } /* Keyword.Type */
30
+ .codehilite .m { color: #666666 } /* Literal.Number */
31
+ .codehilite .s { color: #BA2121 } /* Literal.String */
32
+ .codehilite .na { color: #7D9029 } /* Name.Attribute */
33
+ .codehilite .nb { color: #008000 } /* Name.Builtin */
34
+ .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
35
+ .codehilite .no { color: #880000 } /* Name.Constant */
36
+ .codehilite .nd { color: #AA22FF } /* Name.Decorator */
37
+ .codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
38
+ .codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
39
+ .codehilite .nf { color: #0000FF } /* Name.Function */
40
+ .codehilite .nl { color: #A0A000 } /* Name.Label */
41
+ .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
42
+ .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
43
+ .codehilite .nv { color: #19177C } /* Name.Variable */
44
+ .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
45
+ .codehilite .w { color: #bbbbbb } /* Text.Whitespace */
46
+ .codehilite .mb { color: #666666 } /* Literal.Number.Bin */
47
+ .codehilite .mf { color: #666666 } /* Literal.Number.Float */
48
+ .codehilite .mh { color: #666666 } /* Literal.Number.Hex */
49
+ .codehilite .mi { color: #666666 } /* Literal.Number.Integer */
50
+ .codehilite .mo { color: #666666 } /* Literal.Number.Oct */
51
+ .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
52
+ .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
53
+ .codehilite .sc { color: #BA2121 } /* Literal.String.Char */
54
+ .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
55
+ .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
56
+ .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
57
+ .codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
58
+ .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
59
+ .codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
60
+ .codehilite .sx { color: #008000 } /* Literal.String.Other */
61
+ .codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
62
+ .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
63
+ .codehilite .ss { color: #19177C } /* Literal.String.Symbol */
64
+ .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
65
+ .codehilite .fm { color: #0000FF } /* Name.Function.Magic */
66
+ .codehilite .vc { color: #19177C } /* Name.Variable.Class */
67
+ .codehilite .vg { color: #19177C } /* Name.Variable.Global */
68
+ .codehilite .vi { color: #19177C } /* Name.Variable.Instance */
69
+ .codehilite .vm { color: #19177C } /* Name.Variable.Magic */
70
+ .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
71
+
72
+ .preview_header {
73
+ font-size: 18px;
74
+ font-weight: 500;
75
+ text-align: center;
76
+ margin-bottom: -12px;
77
+ }
78
+
79
+ .bot_cover {
80
+ display: flex;
81
+ flex-direction: column;
82
+ justify-content: center;
83
+ align-items: center;
84
+ min-height: 650px;
85
+ border: 1px solid rgb(229, 231, 235);
86
+ border-radius: 8px;
87
+ padding: 20px 40px;
88
+ }
89
+
90
+ .bot_avatar {
91
+ width: 100px;
92
+ height: 100px;
93
+ border-radius: 50%;
94
+ overflow: hidden;
95
+ }
96
+
97
+ .bot_avatar img {
98
+ width: 100px;
99
+ height: 100px;
100
+ }
101
+
102
+ .bot_name {
103
+ font-size: 36px;
104
+ margin-top: 10px;
105
+ }
106
+
107
+ .bot_desp {
108
+ color: #ddd;
109
+ }
110
+
111
+ .publish_link_container > a {
112
+ display: block;
113
+ border-radius: var(--button-large-radius);
114
+ padding: var(--button-large-padding);
115
+ font-weight: var(--button-large-text-weight);
116
+ font-size: var(--button-large-text-size);
117
+ border: var(--button-border-width) solid var(--button-secondary-border-color);
118
+ background: var(--button-secondary-background-fill);
119
+ color: var(--button-secondary-text-color) !important;
120
+ cursor: pointer;
121
+ text-decoration: none !important;
122
+ text-align: center;
123
+ }
124
+
125
+ .publish_link_container > .disabled {
126
+ cursor: not-allowed;
127
+ opacity: .5;
128
+ filter: grayscale(30%);
129
+ }
130
+
131
+ .markdown-body .message {
132
+ white-space: pre-wrap;
133
+ }
134
+
135
+ .markdown-body details {
136
+ white-space: nowrap;
137
+ }
138
+ .markdown-body .bot details:not(:last-child) {
139
+ margin-bottom: 1px;
140
+ }
141
+ .markdown-body summary {
142
+ background-color: #4b5563;
143
+ color: #eee;
144
+ padding: 0 4px;
145
+ border-radius: 4px;
146
+ font-size: 0.9em;
147
+ }
assets/appBot.css ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* code highlight: https://python-markdown.github.io/extensions/code_hilite/ */
2
+ .codehilite .hll { background-color: #ffffcc }
3
+ .codehilite { background: #f8f8f8; }
4
+ .codehilite .c { color: #408080; font-style: italic } /* Comment */
5
+ .codehilite .err { border: 1px solid #FF0000 } /* Error */
6
+ .codehilite .k { color: #008000; font-weight: bold } /* Keyword */
7
+ .codehilite .o { color: #666666 } /* Operator */
8
+ .codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
9
+ .codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
10
+ .codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
11
+ .codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
12
+ .codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
13
+ .codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
14
+ .codehilite .gd { color: #A00000 } /* Generic.Deleted */
15
+ .codehilite .ge { font-style: italic } /* Generic.Emph */
16
+ .codehilite .gr { color: #FF0000 } /* Generic.Error */
17
+ .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
18
+ .codehilite .gi { color: #00A000 } /* Generic.Inserted */
19
+ .codehilite .go { color: #888888 } /* Generic.Output */
20
+ .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
21
+ .codehilite .gs { font-weight: bold } /* Generic.Strong */
22
+ .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
23
+ .codehilite .gt { color: #0044DD } /* Generic.Traceback */
24
+ .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
25
+ .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
26
+ .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
27
+ .codehilite .kp { color: #008000 } /* Keyword.Pseudo */
28
+ .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
29
+ .codehilite .kt { color: #B00040 } /* Keyword.Type */
30
+ .codehilite .m { color: #666666 } /* Literal.Number */
31
+ .codehilite .s { color: #BA2121 } /* Literal.String */
32
+ .codehilite .na { color: #7D9029 } /* Name.Attribute */
33
+ .codehilite .nb { color: #008000 } /* Name.Builtin */
34
+ .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
35
+ .codehilite .no { color: #880000 } /* Name.Constant */
36
+ .codehilite .nd { color: #AA22FF } /* Name.Decorator */
37
+ .codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
38
+ .codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
39
+ .codehilite .nf { color: #0000FF } /* Name.Function */
40
+ .codehilite .nl { color: #A0A000 } /* Name.Label */
41
+ .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
42
+ .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
43
+ .codehilite .nv { color: #19177C } /* Name.Variable */
44
+ .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
45
+ .codehilite .w { color: #bbbbbb } /* Text.Whitespace */
46
+ .codehilite .mb { color: #666666 } /* Literal.Number.Bin */
47
+ .codehilite .mf { color: #666666 } /* Literal.Number.Float */
48
+ .codehilite .mh { color: #666666 } /* Literal.Number.Hex */
49
+ .codehilite .mi { color: #666666 } /* Literal.Number.Integer */
50
+ .codehilite .mo { color: #666666 } /* Literal.Number.Oct */
51
+ .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
52
+ .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
53
+ .codehilite .sc { color: #BA2121 } /* Literal.String.Char */
54
+ .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
55
+ .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
56
+ .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
57
+ .codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
58
+ .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
59
+ .codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
60
+ .codehilite .sx { color: #008000 } /* Literal.String.Other */
61
+ .codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
62
+ .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
63
+ .codehilite .ss { color: #19177C } /* Literal.String.Symbol */
64
+ .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
65
+ .codehilite .fm { color: #0000FF } /* Name.Function.Magic */
66
+ .codehilite .vc { color: #19177C } /* Name.Variable.Class */
67
+ .codehilite .vg { color: #19177C } /* Name.Variable.Global */
68
+ .codehilite .vi { color: #19177C } /* Name.Variable.Instance */
69
+ .codehilite .vm { color: #19177C } /* Name.Variable.Magic */
70
+ .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
71
+
72
+ .preview_header {
73
+ font-size: 24px;
74
+ font-weight: 500;
75
+ text-align: center;
76
+ }
77
+
78
+ .bot_cover {
79
+ display: flex;
80
+ flex-direction: column;
81
+ justify-content: center;
82
+ align-items: center;
83
+ min-height: 300px;
84
+ border: 1px solid rgb(229, 231, 235);
85
+ padding: 20px 20px;
86
+ }
87
+
88
+ .bot_avatar {
89
+ width: 100px;
90
+ height: 100px;
91
+ border-radius: 50%;
92
+ overflow: hidden;
93
+ }
94
+
95
+ .bot_avatar img {
96
+ width: 100px;
97
+ height: 100px;
98
+ }
99
+
100
+ .bot_name {
101
+ font-size: 36px;
102
+ margin-top: 10px;
103
+ }
104
+
105
+ /* .bot_desp {
106
+ color: #ddd;
107
+ } */
108
+
109
+ .container {
110
+ /* flex-direction: row-reverse; */
111
+ }
112
+
113
+ .markdown-body .message {
114
+ white-space: pre-wrap;
115
+ }
116
+
117
+ .markdown-body details {
118
+ white-space: nowrap;
119
+ }
120
+ .markdown-body .bot details:not(:last-child) {
121
+ margin-bottom: 1px;
122
+ }
123
+ .markdown-body summary {
124
+ background-color: #4b5563;
125
+ color: #eee;
126
+ padding: 0 4px;
127
+ border-radius: 4px;
128
+ font-size: 0.9em;
129
+ }
assets/logo.jpeg ADDED
assets/user.jpeg ADDED
patching.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from typing import List, Union, Iterator
4
+ from http import HTTPStatus
5
+ from time import time
6
+ import time
7
+ import json
8
+
9
+
10
+ from qwen_agent.agents import Assistant
11
+ from qwen_agent.agents import assistant
12
+ from qwen_agent.agents.assistant import Assistant, get_basename_from_url
13
+ from qwen_agent.memory.memory import Memory
14
+ from qwen_agent.llm.schema import ASSISTANT, USER, Message, SYSTEM, CONTENT
15
+ from qwen_agent.llm.qwen_dashscope import QwenChatAtDS
16
+ import qwen_agent.llm.base
17
+ from qwen_agent.llm.base import ModelServiceError
18
+ from qwen_agent.utils.utils import extract_text_from_message, print_traceback
19
+ from qwen_agent.utils.tokenization_qwen import count_tokens, tokenizer
20
+ from qwen_agent.utils.utils import (get_file_type, hash_sha256, is_http_url,
21
+ sanitize_chrome_file_path, save_url_to_local_work_dir)
22
+ from qwen_agent.log import logger
23
+ from qwen_agent.gui.gradio import gr
24
+ from qwen_agent.tools.storage import KeyNotExistsError
25
+ from qwen_agent.tools.simple_doc_parser import (SimpleDocParser, PARSER_SUPPORTED_FILE_TYPES, parse_pdf,
26
+ parse_word, parse_ppt, parse_txt, parse_html_bs, parse_csv,
27
+ parse_tsv, parse_excel, get_plain_doc)
28
+
29
+
30
+
31
+
32
+ def memory_run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]:
33
+ """This agent is responsible for processing the input files in the message.
34
+
35
+ This method stores the files in the knowledge base, and retrievals the relevant parts
36
+ based on the query and returning them.
37
+ The currently supported file types include: .pdf, .docx, .pptx, .txt, .csv, .tsv, .xlsx, .xls and html.
38
+
39
+ Args:
40
+ messages: A list of messages.
41
+ lang: Language.
42
+
43
+ Yields:
44
+ The message of retrieved documents.
45
+ """
46
+ # process files in messages
47
+ rag_files = self.get_rag_files(messages)
48
+
49
+ if not rag_files:
50
+ yield [Message(role=ASSISTANT, content='', name='memory')]
51
+ else:
52
+ query = ''
53
+ # Only retrieval content according to the last user query if exists
54
+ if messages and messages[-1].role == USER:
55
+ query = extract_text_from_message(messages[-1], add_upload_info=False)
56
+
57
+ content = self.function_map['retrieval'].call(
58
+ {
59
+ 'query': query,
60
+ 'files': rag_files
61
+ },
62
+ **kwargs,
63
+ )
64
+ if not isinstance(content, str):
65
+ content = json.dumps(content, ensure_ascii=False, indent=4)
66
+
67
+ yield [Message(role=ASSISTANT, content=content, name='memory')]
68
+
69
+ Memory._run = memory_run
70
+
71
+
72
+ def SimpleDocParser_call(self, params: Union[str, dict], **kwargs) -> Union[str, list]:
73
+ params = self._verify_json_format_args(params)
74
+ path = params['url']
75
+ cached_name_ori = f'{hash_sha256(path)}_ori'
76
+ try:
77
+ # Directly load the parsed doc
78
+ parsed_file = self.db.get(cached_name_ori)
79
+ # [PATCH]: disable json5 for faster processing
80
+ # try:
81
+ # parsed_file = json5.loads(parsed_file)
82
+ # except ValueError:
83
+ # logger.warning(f'Encountered ValueError raised by json5. Fall back to json. File: {cached_name_ori}')
84
+ parsed_file = json.loads(parsed_file)
85
+ logger.info(f'Read parsed {path} from cache.')
86
+ except KeyNotExistsError:
87
+ logger.info(f'Start parsing {path}...')
88
+ time1 = time.time()
89
+
90
+ f_type = get_file_type(path)
91
+ if f_type in PARSER_SUPPORTED_FILE_TYPES:
92
+ if path.startswith('https://') or path.startswith('http://') or re.match(
93
+ r'^[A-Za-z]:\\', path) or re.match(r'^[A-Za-z]:/', path):
94
+ path = path
95
+ else:
96
+ path = sanitize_chrome_file_path(path)
97
+
98
+ os.makedirs(self.data_root, exist_ok=True)
99
+ if is_http_url(path):
100
+ # download online url
101
+ tmp_file_root = os.path.join(self.data_root, hash_sha256(path))
102
+ os.makedirs(tmp_file_root, exist_ok=True)
103
+ path = save_url_to_local_work_dir(path, tmp_file_root)
104
+
105
+ if f_type == 'pdf':
106
+ parsed_file = parse_pdf(path, self.extract_image)
107
+ elif f_type == 'docx':
108
+ parsed_file = parse_word(path, self.extract_image)
109
+ elif f_type == 'pptx':
110
+ parsed_file = parse_ppt(path, self.extract_image)
111
+ elif f_type == 'txt':
112
+ parsed_file = parse_txt(path)
113
+ elif f_type == 'html':
114
+ parsed_file = parse_html_bs(path, self.extract_image)
115
+ elif f_type == 'csv':
116
+ parsed_file = parse_csv(path, self.extract_image)
117
+ elif f_type == 'tsv':
118
+ parsed_file = parse_tsv(path, self.extract_image)
119
+ elif f_type in ['xlsx', 'xls']:
120
+ parsed_file = parse_excel(path, self.extract_image)
121
+ else:
122
+ raise ValueError(
123
+ f'Failed: The current parser does not support this file type! Supported types: {"/".join(PARSER_SUPPORTED_FILE_TYPES)}'
124
+ )
125
+ for page in parsed_file:
126
+ for para in page['content']:
127
+ # Todo: More attribute types
128
+ para['token'] = count_tokens(para.get('text', para.get('table')))
129
+ time2 = time.time()
130
+ logger.info(f'Finished parsing {path}. Time spent: {time2 - time1} seconds.')
131
+ # Cache the parsing doc
132
+ self.db.put(cached_name_ori, json.dumps(parsed_file, ensure_ascii=False, indent=2))
133
+
134
+ if not self.structured_doc:
135
+ return get_plain_doc(parsed_file)
136
+ else:
137
+ return parsed_file
138
+
139
+ SimpleDocParser.call = SimpleDocParser_call
140
+
141
+
142
+ def _truncate_input_messages_roughly(messages: List[Message], max_tokens: int) -> List[Message]:
143
+ sys_msg = messages[0]
144
+ assert sys_msg.role == SYSTEM # The default system is prepended if none exists
145
+ if len([m for m in messages if m.role == SYSTEM]) >= 2:
146
+ raise gr.Error(
147
+ 'The input messages must contain no more than one system message. '
148
+ ' And the system message, if exists, must be the first message.',
149
+ )
150
+
151
+ turns = []
152
+ for m in messages[1:]:
153
+ if m.role == USER:
154
+ turns.append([m])
155
+ else:
156
+ if turns:
157
+ turns[-1].append(m)
158
+ else:
159
+ raise gr.Error(
160
+ 'The input messages (excluding the system message) must start with a user message.',
161
+ )
162
+
163
+ def _count_tokens(msg: Message) -> int:
164
+ return tokenizer.count_tokens(extract_text_from_message(msg, add_upload_info=True))
165
+
166
+ token_cnt = _count_tokens(sys_msg)
167
+ truncated = []
168
+ for i, turn in enumerate(reversed(turns)):
169
+ cur_turn_msgs = []
170
+ cur_token_cnt = 0
171
+ for m in reversed(turn):
172
+ cur_turn_msgs.append(m)
173
+ cur_token_cnt += _count_tokens(m)
174
+ # Check "i == 0" so that at least one user message is included
175
+ # [PATCH] Do not do truncate for this demo
176
+ # if (i == 0) or (token_cnt + cur_token_cnt <= max_tokens):
177
+ truncated.extend(cur_turn_msgs)
178
+ token_cnt += cur_token_cnt
179
+ # else:
180
+ # break
181
+ # Always include the system message
182
+ truncated.append(sys_msg)
183
+ truncated.reverse()
184
+
185
+ if len(truncated) < 2: # one system message + one or more user messages
186
+ raise gr.Error(
187
+ code='400',
188
+ message='At least one user message should be provided.',
189
+ )
190
+ if token_cnt > max_tokens:
191
+ raise gr.Error(
192
+ f'The input messages (around {token_cnt} tokens) exceed the maximum context length ({max_tokens} tokens).'
193
+ )
194
+ return truncated
195
+
196
+ qwen_agent.llm.base._truncate_input_messages_roughly = _truncate_input_messages_roughly
197
+
198
+
199
+
200
+ def format_knowledge_to_source_and_content(result: Union[str, List[dict]]) -> List[dict]:
201
+ knowledge = []
202
+ if isinstance(result, str):
203
+ result = f'{result}'.strip()
204
+ try:
205
+ # [PATCH]: disable json5 for faster processing
206
+ docs = json.loads(result)
207
+ except Exception:
208
+ print_traceback()
209
+ knowledge.append({'source': '上传的文档', 'content': result})
210
+ return knowledge
211
+ else:
212
+ docs = result
213
+ try:
214
+ _tmp_knowledge = []
215
+ assert isinstance(docs, list)
216
+ for doc in docs:
217
+ url, snippets = doc['url'], doc['text']
218
+ assert isinstance(snippets, list)
219
+ _tmp_knowledge.append({
220
+ 'source': f'[文件]({get_basename_from_url(url)})',
221
+ 'content': '\n\n...\n\n'.join(snippets)
222
+ })
223
+ knowledge.extend(_tmp_knowledge)
224
+ except Exception:
225
+ print_traceback()
226
+ knowledge.append({'source': '上传的文档', 'content': result})
227
+ return knowledge
228
+
229
+ assistant.format_knowledge_to_source_and_content = format_knowledge_to_source_and_content
230
+
231
+
232
+ HINT_PATTERN = "\n<summary>input tokens: {input_tokens}, prefill time: [[<PrefillCost>]]s, output tokens: {output_tokens}, decode speed: [[<DecodeSpeed>]] tokens/s</summary>"
233
+
234
+ @staticmethod
235
+ def _full_stream_output(response):
236
+ for chunk in response:
237
+ if chunk.status_code == HTTPStatus.OK:
238
+ # [PATCH]: add speed statistics
239
+ yield [Message(ASSISTANT, chunk.output.choices[0].message.content + HINT_PATTERN.format(
240
+ input_tokens=chunk.usage.input_tokens,
241
+ output_tokens=chunk.usage.output_tokens,)
242
+ )]
243
+ else:
244
+ raise ModelServiceError(code=chunk.code, message=chunk.message)
245
+
246
+ QwenChatAtDS._full_stream_output = _full_stream_output
247
+
248
+ def assistant_run(self,
249
+ messages,
250
+ lang="en",
251
+ knowledge="",
252
+ **kwargs):
253
+
254
+ if any([len(message[CONTENT]) > 1 for message in messages]):
255
+ yield [Message(ASSISTANT, "Uploading and Parsing Files...")]
256
+ new_messages = self._prepend_knowledge_prompt(messages=messages, lang=lang, knowledge=knowledge, **kwargs)
257
+ start_prefill_time = time.time()
258
+
259
+ yield [Message(ASSISTANT, "Qwen-Turbo is thinking...")]
260
+
261
+ start_decode_time = None
262
+ for chunk in super(Assistant, self)._run(messages=new_messages, lang=lang, **kwargs):
263
+
264
+ if start_decode_time is None:
265
+ end_prefill_time = time.time()
266
+ start_decode_time = time.time() - 0.5
267
+
268
+ # [PATCH]: compute speed statstics
269
+ pattern = re.search(HINT_PATTERN.format(input_tokens="\d+", output_tokens="(\d+)").replace("[", "\[").replace("]", "\]"), chunk[0][CONTENT])
270
+ if pattern:
271
+ output_tokens = int(pattern.group(1))
272
+ chunk[0][CONTENT] = chunk[0][CONTENT].replace("[[<PrefillCost>]]", "%.2f" % (end_prefill_time - start_prefill_time)).replace("[[<DecodeSpeed>]]", "%.2f" % (output_tokens/(time.time() - start_decode_time)))
273
+
274
+ yield chunk
275
+
276
+ Assistant._run = assistant_run
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ qwen-agent==0.0.10
2
+ qwen-agent[gui]
3
+ qwen-agent[rag]
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
+
21
+ Args:
22
+ agent: The agent or a list of agents,
23
+ supports various types of agents such as Assistant, GroupChat, Router, etc.
24
+ chatbot_config: The chatbot configuration.
25
+ Set the configuration as {'user.name': '', 'user.avatar': '', 'agent.avatar': '', 'input.placeholder': '', 'prompt.suggestions': []}.
26
+ """
27
+ chatbot_config = chatbot_config or {}
28
+
29
+ if isinstance(agent, MultiAgentHub):
30
+ self.agent_list = [agent for agent in agent.nonuser_agents]
31
+ self.agent_hub = agent
32
+ elif isinstance(agent, list):
33
+ self.agent_list = agent
34
+ self.agent_hub = None
35
+ else:
36
+ self.agent_list = [agent]
37
+ self.agent_hub = None
38
+
39
+ user_name = chatbot_config.get('user.name', 'user')
40
+ self.user_config = {
41
+ 'name': user_name,
42
+ 'avatar': chatbot_config.get(
43
+ 'user.avatar',
44
+ get_avatar_image(user_name),
45
+ ),
46
+ }
47
+
48
+ self.agent_config_list = [{
49
+ 'name': agent.name,
50
+ 'avatar': chatbot_config.get(
51
+ 'agent.avatar',
52
+ get_avatar_image(agent.name),
53
+ ),
54
+ 'description': agent.description or "I'm a helpful assistant.",
55
+ } for agent in self.agent_list]
56
+
57
+ self.input_placeholder = chatbot_config.get('input.placeholder', '跟我聊聊吧~')
58
+ self.prompt_suggestions = chatbot_config.get('prompt.suggestions', [])
59
+ self.verbose = chatbot_config.get('verbose', False)
60
+
61
+ """
62
+ Run the chatbot.
63
+
64
+ Args:
65
+ messages: The chat history.
66
+ """
67
+
68
+ def run(self,
69
+ messages: List[Message] = None,
70
+ share: bool = False,
71
+ server_name: str = None,
72
+ server_port: int = None,
73
+ concurrency_limit: int = 10,
74
+ enable_mention: bool = False,
75
+ **kwargs):
76
+ self.run_kwargs = kwargs
77
+
78
+ from qwen_agent.gui.gradio import gr, mgr
79
+
80
+ customTheme = gr.themes.Default(
81
+ primary_hue=gr.themes.utils.colors.blue,
82
+ radius_size=gr.themes.utils.sizes.radius_none,
83
+ )
84
+
85
+ with gr.Blocks(
86
+ css=os.path.join(os.path.dirname(__file__), 'assets/appBot.css'),
87
+ theme=customTheme,
88
+ ) as demo:
89
+ history = gr.State([])
90
+
91
+ with gr.Row(elem_classes='container'):
92
+ with gr.Column(scale=4):
93
+ chatbot = mgr.Chatbot(value=convert_history_to_chatbot(messages=messages),
94
+ avatar_images=[
95
+ self.user_config,
96
+ self.agent_config_list,
97
+ ],
98
+ height=650,
99
+ avatar_image_width=80,
100
+ flushing=False,
101
+ show_copy_button=False,
102
+ latex_delimiters=[{
103
+ 'left': '\\(',
104
+ 'right': '\\)',
105
+ 'display': True
106
+ }, {
107
+ 'left': '\\begin{equation}',
108
+ 'right': '\\end{equation}',
109
+ 'display': True
110
+ }, {
111
+ 'left': '\\begin{align}',
112
+ 'right': '\\end{align}',
113
+ 'display': True
114
+ }, {
115
+ 'left': '\\begin{alignat}',
116
+ 'right': '\\end{alignat}',
117
+ 'display': True
118
+ }, {
119
+ 'left': '\\begin{gather}',
120
+ 'right': '\\end{gather}',
121
+ 'display': True
122
+ }, {
123
+ 'left': '\\begin{CD}',
124
+ 'right': '\\end{CD}',
125
+ 'display': True
126
+ }, {
127
+ 'left': '\\[',
128
+ 'right': '\\]',
129
+ 'display': True
130
+ }])
131
+
132
+ input = mgr.MultimodalInput(placeholder=self.input_placeholder,)
133
+
134
+ with gr.Column(scale=1):
135
+ if len(self.agent_list) > 1:
136
+ agent_selector = gr.Dropdown(
137
+ [(agent.name, i) for i, agent in enumerate(self.agent_list)],
138
+ label='Agents',
139
+ info='选择一个Agent',
140
+ value=0,
141
+ interactive=True,
142
+ )
143
+
144
+ agent_info_block = self._create_agent_info_block()
145
+
146
+ # agent_plugins_block = self._create_agent_plugins_block()
147
+
148
+ if self.prompt_suggestions:
149
+ gr.Examples(
150
+ label='推荐对话',
151
+ examples=self.prompt_suggestions,
152
+ inputs=[input],
153
+ )
154
+
155
+ if len(self.agent_list) > 1:
156
+ agent_selector.change(
157
+ fn=self.change_agent,
158
+ inputs=[agent_selector],
159
+ outputs=[agent_selector, agent_info_block, agent_plugins_block],
160
+ queue=False,
161
+ )
162
+
163
+ input_promise = input.submit(
164
+ fn=self.add_text,
165
+ inputs=[input, chatbot, history],
166
+ outputs=[input, chatbot, history],
167
+ queue=False,
168
+ )
169
+
170
+ if len(self.agent_list) > 1 and enable_mention:
171
+ input_promise = input_promise.then(
172
+ self.add_mention,
173
+ [chatbot, agent_selector],
174
+ [chatbot, agent_selector],
175
+ ).then(
176
+ self.agent_run,
177
+ [chatbot, history, agent_selector],
178
+ [chatbot, history, agent_selector],
179
+ )
180
+ else:
181
+ input_promise = input_promise.then(
182
+ self.agent_run,
183
+ [chatbot, history],
184
+ [chatbot, history],
185
+ )
186
+
187
+ input_promise.then(self.flushed, None, [input])
188
+
189
+ demo.load(None)
190
+
191
+ demo.queue(default_concurrency_limit=concurrency_limit).launch(share=share,
192
+ server_name=server_name,
193
+ server_port=server_port)
194
+
195
+ def change_agent(self, agent_selector):
196
+ yield agent_selector, self._create_agent_info_block(agent_selector), self._create_agent_plugins_block(
197
+ agent_selector)
198
+
199
+ def add_text(self, _input, _chatbot, _history):
200
+ from qwen_agent.gui.gradio import gr
201
+ if _input.text == "/clear":
202
+ _chatbot = []
203
+ _history.clear()
204
+ yield gr.update(interactive=False, value=""), _chatbot, _history
205
+ return
206
+
207
+ _history.append({
208
+ ROLE: USER,
209
+ CONTENT: [{
210
+ 'text': _input.text
211
+ }],
212
+ })
213
+
214
+ if self.user_config[NAME]:
215
+ _history[-1][NAME] = self.user_config[NAME]
216
+
217
+ if _input.files:
218
+ for file in _input.files:
219
+ if file.mime_type.startswith('image/'):
220
+ _history[-1][CONTENT].append({IMAGE: 'file://' + file.path})
221
+ else:
222
+ _history[-1][CONTENT].append({FILE: file.path})
223
+
224
+ _chatbot.append([_input, None])
225
+
226
+ yield gr.update(interactive=False, value=None), _chatbot, _history
227
+
228
+ def add_mention(self, _chatbot, _agent_selector):
229
+ if len(self.agent_list) == 1:
230
+ yield _chatbot, _agent_selector
231
+
232
+ query = _chatbot[-1][0].text
233
+ match = re.search(r'@\w+\b', query)
234
+ if match:
235
+ _agent_selector = self._get_agent_index_by_name(match.group()[1:])
236
+
237
+ agent_name = self.agent_list[_agent_selector].name
238
+
239
+ if ('@' + agent_name) not in query and self.agent_hub is None:
240
+ _chatbot[-1][0].text = '@' + agent_name + ' ' + query
241
+
242
+ yield _chatbot, _agent_selector
243
+
244
+ def agent_run(self, _chatbot, _history, _agent_selector=None):
245
+ if not _history:
246
+ if _agent_selector is not None:
247
+ yield _chatbot, _history, _agent_selector
248
+ else:
249
+ yield _chatbot, _history
250
+ return
251
+
252
+
253
+ if self.verbose:
254
+ logger.info('agent_run input:\n' + pprint.pformat(_history, indent=2))
255
+
256
+ num_input_bubbles = len(_chatbot) - 1
257
+ num_output_bubbles = 1
258
+ _chatbot[-1][1] = [None for _ in range(len(self.agent_list))]
259
+
260
+ agent_runner = self.agent_list[_agent_selector or 0]
261
+ if self.agent_hub:
262
+ agent_runner = self.agent_hub
263
+ responses = []
264
+ for responses in agent_runner.run(_history, **self.run_kwargs):
265
+ # usage = responses.usage
266
+ # responses = [Message(ASSISTANT, responses.output.choices[0].message.content)]
267
+
268
+ if not responses:
269
+ continue
270
+ if responses[-1][CONTENT] == PENDING_USER_INPUT:
271
+ logger.info('Interrupted. Waiting for user input!')
272
+ break
273
+
274
+ display_responses = convert_fncall_to_text(responses)
275
+ # display_responses[-1][CONTENT] += "\n<summary>" + repr({"usage": usage}) + "</summary>"
276
+ if not display_responses:
277
+ continue
278
+ if display_responses[-1][CONTENT] is None:
279
+ continue
280
+
281
+ while len(display_responses) > num_output_bubbles:
282
+ # Create a new chat bubble
283
+ _chatbot.append([None, None])
284
+ _chatbot[-1][1] = [None for _ in range(len(self.agent_list))]
285
+ num_output_bubbles += 1
286
+
287
+ assert num_output_bubbles == len(display_responses)
288
+ assert num_input_bubbles + num_output_bubbles == len(_chatbot)
289
+
290
+ for i, rsp in enumerate(display_responses):
291
+ agent_index = self._get_agent_index_by_name(rsp[NAME])
292
+ _chatbot[num_input_bubbles + i][1][agent_index] = rsp[CONTENT]
293
+
294
+ if len(self.agent_list) > 1:
295
+ _agent_selector = agent_index
296
+
297
+ if _agent_selector is not None:
298
+ yield _chatbot, _history, _agent_selector
299
+ else:
300
+ yield _chatbot, _history
301
+
302
+ if responses:
303
+ for res in responses:
304
+ res['content'] = re.sub(r"\n<summary>input tokens.*</summary>", "", res['content'])
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
+ )