Aabbhishekk commited on
Commit
e37ee12
·
verified ·
1 Parent(s): 3310c6d

Upload 3 files

Browse files
ui_components/add_mcp_server_button.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modelscope_studio.components.antd as antd
2
+ import modelscope_studio.components.base as ms
3
+ import gradio as gr
4
+ import json
5
+
6
+
7
+ def AddMcpServerButton():
8
+ with antd.Button("添加 MCP Server", type="primary",
9
+ size="small") as add_mcp_server_btn:
10
+ with ms.Slot("icon"):
11
+ antd.Icon("PlusOutlined")
12
+ with antd.Modal(
13
+ title="添加 MCP Server",
14
+ footer=False,
15
+ styles=dict(footer=dict(display="none"))) as add_mcp_server_modal:
16
+ with antd.Tabs():
17
+ with antd.Tabs.Item(label="表单添加"):
18
+ with antd.Form(layout="vertical") as add_mcp_server_form:
19
+ with antd.Form.Item(form_name="name",
20
+ label="名称",
21
+ rules=[{
22
+ "required": True
23
+ }]):
24
+ antd.Input(placeholder="MCP Server 名称,如 fetch、time 等")
25
+ with antd.Form.Item(form_name="url",
26
+ label="SSE 链接",
27
+ rules=[{
28
+ "required": True
29
+ }]):
30
+ antd.Input(placeholder="MCP Server SSE 链接")
31
+ with antd.Flex(gap="small", justify="end"):
32
+ add_mcp_server_modal_cancel_btn = antd.Button("取消")
33
+ antd.Button("确定", html_type="submit", type="primary")
34
+ with antd.Tabs.Item(label="JSON 添加"):
35
+ with antd.Form(layout="vertical") as add_mcp_server_json_form:
36
+ with antd.Form.Item(form_name="json",
37
+ label="JSON",
38
+ rules=[{
39
+ "required":
40
+ True,
41
+ "validator":
42
+ """(_, value) => {
43
+ if (!value) {
44
+ return Promise.reject('请输入 JSON 值');
45
+ }
46
+ try {
47
+ const parsedValue = JSON.parse(value);
48
+ if (!parsedValue.mcpServers || typeof parsedValue.mcpServers !== 'object') {
49
+ return Promise.reject('配置必须包含正确的 mcpServers 字段');
50
+ }
51
+ return Promise.resolve()
52
+ } catch {
53
+ return Promise.reject('请输入有效的 JSON 值');
54
+ }
55
+ } """
56
+ }]):
57
+ antd.Input.Textarea(
58
+ auto_size=dict(minRows=4, maxRows=8),
59
+ placeholder=json.dumps(
60
+ {
61
+ "mcpServers": {
62
+ "fetch": {
63
+ "type": "sse",
64
+ "url": "mcp server sse url"
65
+ }
66
+ }
67
+ },
68
+ indent=4))
69
+ with antd.Flex(gap="small", justify="end"):
70
+ add_mcp_server_modal_json_cancel_btn = antd.Button(
71
+ "取消")
72
+ antd.Button("确定", html_type="submit", type="primary")
73
+ add_mcp_server_btn.click(fn=lambda: gr.update(open=True),
74
+ outputs=[add_mcp_server_modal],
75
+ queue=False)
76
+ gr.on(triggers=[
77
+ add_mcp_server_modal_cancel_btn.click,
78
+ add_mcp_server_modal_json_cancel_btn.click, add_mcp_server_modal.cancel
79
+ ],
80
+ queue=False,
81
+ fn=lambda: gr.update(open=False),
82
+ outputs=[add_mcp_server_modal])
83
+ add_mcp_server_form.finish(
84
+ lambda: (gr.update(value={
85
+ "name": "",
86
+ "url": "",
87
+ }), gr.update(open=False)),
88
+ outputs=[add_mcp_server_form, add_mcp_server_modal])
89
+ add_mcp_server_json_form.finish(
90
+ lambda: (gr.update(value={"json": ""}), gr.update(open=False)),
91
+ outputs=[add_mcp_server_json_form, add_mcp_server_modal])
92
+ return add_mcp_server_form, add_mcp_server_json_form
ui_components/config_form.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import gradio as gr
3
+ import modelscope_studio.components.antd as antd
4
+ import modelscope_studio.components.base as ms
5
+
6
+ from .add_mcp_server_button import AddMcpServerButton
7
+ from config import default_mcp_config, model_options, default_sys_prompt
8
+
9
+
10
+ def ConfigForm():
11
+ with antd.Form(layout="vertical",
12
+ value={
13
+ "sys_prompt": default_sys_prompt,
14
+ "model": model_options[0]["value"]
15
+ }) as config_form:
16
+ with antd.Form.Item(label_col=24):
17
+ with ms.Slot("extra"):
18
+ with antd.Flex(justify="end", elem_style=dict(marginTop=10)):
19
+ mcp_config_confirm_btn = antd.Button("保存配置",
20
+ type="primary")
21
+ with antd.Flex(wrap=True,
22
+ justify="space-between",
23
+ gap="middle",
24
+ elem_style=dict(paddingBottom=8)):
25
+ with antd.Flex(gap="middle", wrap=True):
26
+ with antd.Flex(gap="small", align="center"):
27
+ antd.Typography.Text("MCP Servers",
28
+ elem_style=dict(fontSize=14))
29
+ antd.Typography.Text("编辑以下内容以修改运行中的 MCP Servers",
30
+ elem_style=dict(fontSize=12),
31
+ type="secondary")
32
+ with antd.Tooltip(title="目前只支持 SSE 类型的 MCP Server"):
33
+ with antd.Typography.Text(type="warning"):
34
+ antd.Icon("InfoCircleOutlined")
35
+ add_mcp_server_form, add_mcp_server_json_form = AddMcpServerButton(
36
+ )
37
+
38
+ with antd.Button("重置默认配置",
39
+ size="small") as reset_mcp_config_btn:
40
+ with ms.Slot("icon"):
41
+ antd.Icon("ReloadOutlined")
42
+ with ms.Div():
43
+ with antd.Tooltip("在 MCP Inspector 中测试待连接的 MCP Server"):
44
+ with antd.Button(
45
+ "前往 MCP Inspector 测试",
46
+ color="primary",
47
+ variant="outlined",
48
+ size="small",
49
+ href_target="_blank",
50
+ href=
51
+ "https://modelscope.cn/studios/modelscope/mcp-inspector"
52
+ ):
53
+ with ms.Slot("icon"):
54
+ antd.Icon("ExportOutlined")
55
+ mcp_config = gr.Code(default_mcp_config,
56
+ show_label=False,
57
+ container=False,
58
+ max_lines=20,
59
+ lines=3,
60
+ language="json")
61
+ with antd.Form.Item(form_name="model", label="模型"):
62
+ with ms.Slot("extra"):
63
+ with ms.Fragment(visible=False) as thought_tip:
64
+ antd.Typography.Text("Note: 推理模式在调用工具前,会有较长的思考过程,需耐心等待。",
65
+ elem_style=dict(fontSize=12),
66
+ type="warning")
67
+
68
+ with antd.Flex(align="center",
69
+ gap=4,
70
+ elem_style=dict(marginTop=4)):
71
+ ms.Text("Powered by")
72
+ with antd.Typography.Link(
73
+ href=
74
+ "https://modelscope.cn/docs/model-service/API-Inference/intro",
75
+ href_target="_blank",
76
+ elem_style=dict(display="flex",
77
+ alignItems="center")):
78
+ antd.Image(
79
+ "https://gw.alicdn.com/imgextra/i4/O1CN01dCJ2sA1OHUQJFyCRm_!!6000000001680-2-tps-200-200.png",
80
+ preview=False,
81
+ width=20,
82
+ height=20)
83
+ ms.Text("ModelScope API-Inference")
84
+ with antd.Select(options=model_options) as model_select:
85
+ with ms.Slot("labelRender",
86
+ params_mapping="""(option) => {
87
+ const tag = window.MODEL_OPTIONS_MAP[option.value].tag
88
+ return {
89
+ label: option.label,
90
+ link: { href: `https://modelscope.cn/models/${option.value.split(':')[0]}` },
91
+ tag: tag ? { value: tag.label, style: { display: 'inline-block', color: tag.color } } : undefined
92
+ }
93
+ }"""):
94
+ with antd.Flex(gap="small"):
95
+ antd.Typography.Text(as_item="label")
96
+ antd.Tag(elem_style=dict(display="none"),
97
+ as_item="tag")
98
+ antd.Typography.Link("模型链接",
99
+ href_target="_blank",
100
+ as_item="link")
101
+ with ms.Slot("optionRender",
102
+ params_mapping="""(option) => ({
103
+ label: option.data.label.split(':')[0],
104
+ tag: option.data.tag ? { value: option.data.tag.label, style: { display: 'inline-block', color: option.data.tag.color } } : undefined
105
+ })"""):
106
+
107
+ with antd.Flex(gap="small"):
108
+ antd.Typography.Text(as_item="label")
109
+ antd.Tag(elem_style=dict(display="none"),
110
+ as_item="tag")
111
+
112
+ with antd.Form.Item(form_name="sys_prompt", label="系统提示"):
113
+ antd.Input.Textarea(auto_size=dict(minRows=2, maxRows=4))
114
+
115
+ def add_mcp_server(mcp_config_value, add_mcp_server_form_value):
116
+ if not mcp_config_value:
117
+ mcp_config_value = "{}"
118
+ mcp_config = json.loads(mcp_config_value)
119
+ name = add_mcp_server_form_value["name"]
120
+ url = add_mcp_server_form_value["url"]
121
+ if "mcpServers" not in mcp_config:
122
+ mcp_config["mcpServers"] = {}
123
+ mcp_config["mcpServers"][name] = {"type": "sse", "url": url}
124
+
125
+ return gr.update(value=json.dumps(mcp_config, indent=4))
126
+
127
+ def add_mcp_server_by_json(mcp_config_value,
128
+ add_mcp_server_json_form_value):
129
+ if not mcp_config_value:
130
+ mcp_config_value = "{}"
131
+ mcp_config = json.loads(mcp_config_value)
132
+ json_value = add_mcp_server_json_form_value["json"]
133
+ json_config = json.loads(json_value)
134
+ if "mcpServers" not in mcp_config:
135
+ mcp_config["mcpServers"] = {}
136
+
137
+ mcp_config["mcpServers"].update(json_config.get("mcpServers", {}))
138
+
139
+ return gr.update(value=json.dumps(mcp_config, indent=4))
140
+
141
+ def select_model(e: gr.EventData):
142
+ return gr.update(visible=e._data["payload"][1].get("thought", False))
143
+
144
+ add_mcp_server_form.finish(fn=add_mcp_server,
145
+ inputs=[mcp_config, add_mcp_server_form],
146
+ outputs=[mcp_config],
147
+ queue=False)
148
+ add_mcp_server_json_form.finish(
149
+ fn=add_mcp_server_by_json,
150
+ inputs=[mcp_config, add_mcp_server_json_form],
151
+ outputs=[mcp_config],
152
+ queue=False)
153
+ model_select.change(fn=select_model, outputs=[thought_tip], queue=False)
154
+
155
+ return config_form, mcp_config_confirm_btn, reset_mcp_config_btn, mcp_config
ui_components/mcp_servers_button.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+ import modelscope_studio.components.antd as antd
3
+ import modelscope_studio.components.base as ms
4
+ import gradio as gr
5
+ from config import max_mcp_server_count
6
+
7
+
8
+ def McpServersButton(data_source: List[dict]):
9
+ state = gr.State({"data_source": data_source})
10
+ with antd.Button(value=None, variant="text",
11
+ color="primary") as mcp_servers_btn:
12
+ with ms.Slot("icon"):
13
+ antd.Icon("ToolOutlined")
14
+ with antd.Modal(
15
+ width=420,
16
+ footer=False,
17
+ centered=True,
18
+ styles=dict(footer=dict(display="none"))) as mcp_servers_modal:
19
+ with ms.Slot("title"):
20
+ with antd.Flex(gap="small", align="center"):
21
+ ms.Text("MCP Servers")
22
+ mcp_servers_switch = antd.Switch(True)
23
+ antd.Typography.Text(
24
+ f"最大 MCP Server 连接数:{max_mcp_server_count}",
25
+ type="secondary",
26
+ elem_style=dict(fontSize=12, fontWeight="normal"))
27
+ with antd.List(
28
+ data_source=data_source,
29
+ pagination=dict(pageSize=10,
30
+ hideOnSinglePage=True)) as mcp_servers_list:
31
+ with ms.Slot(
32
+ "renderItem",
33
+ params_mapping=
34
+ "(item) => ({ text: { value: item.name, disabled: item.disabled }, tag: { style: { display: item.internal ? undefined: 'none' } }, switch: { value: item.enabled, mcp: item.name, disabled: item.disabled }})"
35
+ ):
36
+ with antd.List.Item():
37
+ with antd.Flex(justify="space-between",
38
+ elem_style=dict(width="100%")):
39
+ with antd.Flex(gap="small"):
40
+ antd.Typography.Text(as_item="text")
41
+ antd.Tag("官方示例", color="green", as_item="tag")
42
+ mcp_server_switch = antd.Switch(as_item="switch")
43
+
44
+ def change_mcp_servers_switch(mcp_servers_switch_value, state_value):
45
+ state_value["data_source"] = [{
46
+ **item, "disabled":
47
+ not mcp_servers_switch_value
48
+ } for item in state_value["data_source"]]
49
+ return gr.update(value=state_value)
50
+
51
+ def change_mcp_server_switch(state_value, e: gr.EventData):
52
+ mcp = e._data["component"]["mcp"]
53
+
54
+ enabled = e._data["payload"][0]
55
+
56
+ state_value["data_source"] = [{
57
+ **item, "enabled": enabled
58
+ } if item["name"] == mcp else item
59
+ for item in state_value["data_source"]]
60
+ return gr.update(value=state_value)
61
+
62
+ def apply_state_change(state_value):
63
+ has_tool_use = False
64
+ disabled_tool_use = False
65
+ enabled_server_count = 0
66
+ for item in state_value["data_source"]:
67
+ if item.get("enabled"):
68
+ if enabled_server_count >= max_mcp_server_count:
69
+ item["enabled"] = False
70
+ else:
71
+ enabled_server_count += 1
72
+ if item.get("disabled"):
73
+ disabled_tool_use = True
74
+ else:
75
+ has_tool_use = True
76
+
77
+ if not disabled_tool_use:
78
+ for item in state_value["data_source"]:
79
+ if enabled_server_count >= max_mcp_server_count:
80
+ item["disabled"] = not item.get("enabled", False)
81
+ else:
82
+ item["disabled"] = False
83
+
84
+ return gr.update(
85
+ data_source=state_value["data_source"],
86
+ footer="没有可用的 MCP Server"
87
+ if len(state_value["data_source"]) == 0 else ""), gr.update(
88
+ color="primary" if has_tool_use else "default"), gr.update(
89
+ value=not disabled_tool_use), gr.update(value=state_value)
90
+
91
+ mcp_servers_btn.click(fn=lambda: gr.update(open=True),
92
+ outputs=[mcp_servers_modal],
93
+ queue=False)
94
+ mcp_servers_switch.change(fn=change_mcp_servers_switch,
95
+ inputs=[mcp_servers_switch, state],
96
+ outputs=[state])
97
+ mcp_server_switch.change(fn=change_mcp_server_switch,
98
+ inputs=[state],
99
+ outputs=[state])
100
+ state.change(
101
+ fn=apply_state_change,
102
+ inputs=[state],
103
+ outputs=[mcp_servers_list, mcp_servers_btn, mcp_servers_switch, state],
104
+ queue=False)
105
+ mcp_servers_modal.cancel(fn=lambda: gr.update(open=False),
106
+ outputs=[mcp_servers_modal],
107
+ queue=False)
108
+ return state