justmywyw commited on
Commit
630cbf4
1 Parent(s): f3adb56

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +11 -11
  2. .gitignore +60 -0
  3. Dockerfile +16 -0
  4. README.md +6 -6
  5. app.py +22 -0
  6. components/Chatbot/README.md +129 -0
  7. components/Chatbot/app.py +6 -0
  8. components/Chatbot/demos/accordion.py +49 -0
  9. components/Chatbot/demos/basic.py +55 -0
  10. components/Chatbot/demos/message_config.py +34 -0
  11. components/Chatbot/demos/multi_bots.py +91 -0
  12. components/Chatbot/demos/multimodal.py +43 -0
  13. components/Chatbot/demos/select-box.py +48 -0
  14. components/Chatbot/resources/audio.wav +0 -0
  15. components/Chatbot/resources/bot.jpeg +0 -0
  16. components/Chatbot/resources/custom_components/custom_select.js +26 -0
  17. components/Chatbot/resources/dog.mp4 +3 -0
  18. components/Chatbot/resources/image-bot.jpeg +0 -0
  19. components/Chatbot/resources/music-bot.jpeg +0 -0
  20. components/Chatbot/resources/screen.jpeg +0 -0
  21. components/Chatbot/resources/user.jpeg +0 -0
  22. components/Docs.py +147 -0
  23. components/Markdown/README.md +87 -0
  24. components/Markdown/app.py +20 -0
  25. components/Markdown/custom_tags/accordion.md +23 -0
  26. components/Markdown/custom_tags/select-box.md +45 -0
  27. components/Markdown/demos/accordion.py +30 -0
  28. components/Markdown/demos/basic.py +11 -0
  29. components/Markdown/demos/custom-tag.py +21 -0
  30. components/Markdown/demos/custom-tag2.py +38 -0
  31. components/Markdown/demos/custom-tag3.py +31 -0
  32. components/Markdown/demos/custom-tag4.py +39 -0
  33. components/Markdown/demos/custom_tags/accordion/accordion-title.py +19 -0
  34. components/Markdown/demos/custom_tags/accordion/basic.py +17 -0
  35. components/Markdown/demos/custom_tags/select-box/basic.py +21 -0
  36. components/Markdown/demos/custom_tags/select-box/card_shape.py +32 -0
  37. components/Markdown/demos/custom_tags/select-box/card_shape_width_auto.py +26 -0
  38. components/Markdown/demos/custom_tags/select-box/python_events.py +23 -0
  39. components/Markdown/demos/multimodal.py +31 -0
  40. components/Markdown/demos/select-box.py +32 -0
  41. components/Markdown/resources/audio.wav +0 -0
  42. components/Markdown/resources/bot.jpeg +0 -0
  43. components/Markdown/resources/custom_components/custom_select.js +26 -0
  44. components/Markdown/resources/dog.mp4 +3 -0
  45. components/Markdown/resources/screen.jpeg +0 -0
  46. components/Markdown/resources/user.jpeg +0 -0
  47. components/MultimodalInput/README.md +50 -0
  48. components/MultimodalInput/app.py +6 -0
  49. components/MultimodalInput/demos/basic.py +16 -0
  50. components/MultimodalInput/demos/config_buttons.py +16 -0
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
 
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
  *.model filter=lfs diff=lfs merge=lfs -text
13
  *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
  *.onnx filter=lfs diff=lfs merge=lfs -text
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
 
11
  *.model filter=lfs diff=lfs merge=lfs -text
12
  *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
13
  *.onnx filter=lfs diff=lfs merge=lfs -text
14
  *.ot filter=lfs diff=lfs merge=lfs -text
15
  *.parquet filter=lfs diff=lfs merge=lfs -text
16
  *.pb filter=lfs diff=lfs merge=lfs -text
 
 
17
  *.pt filter=lfs diff=lfs merge=lfs -text
18
  *.pth filter=lfs diff=lfs merge=lfs -text
19
  *.rar filter=lfs diff=lfs merge=lfs -text
 
20
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
22
  *.tflite filter=lfs diff=lfs merge=lfs -text
23
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
24
  *.xz filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+ *.ckpt filter=lfs diff=lfs merge=lfs -textcomponents/Chatbot/resources/dog.mp4 filter=lfs diff=lfs merge=lfs -text
35
+ components/Markdown/resources/dog.mp4 filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .eggs/
2
+ dist/
3
+ *.pyc
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ __tmp/*
8
+ *.pyi
9
+ templates
10
+
11
+ # common
12
+
13
+ !.*ignore
14
+ !.*rc
15
+ !.gitattributes
16
+ !.aoneci.yml
17
+ !.editorconfig
18
+
19
+ # Logs
20
+ logs
21
+ *.log*
22
+
23
+ # Runtime data
24
+ pids
25
+ *.pid
26
+ *.seed
27
+ *.pid.lock
28
+
29
+ # Directory for instrumented libs generated by jscoverage/JSCover
30
+ lib-cov
31
+
32
+ # Coverage directory used by tools like istanbul
33
+ coverage
34
+
35
+ # Dependency directories
36
+ bower_components
37
+ node_modules/
38
+ jspm_packages/
39
+
40
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
41
+ build/Release
42
+ lib
43
+ dist
44
+
45
+ # TypeScript v1 declaration files
46
+ typings/
47
+
48
+ # Output of 'npm pack'
49
+ *.tgz
50
+
51
+ # xconsole
52
+ src/.xconsole
53
+ build
54
+ .faas_debug_tmp
55
+ .yarn
56
+ .yalc
57
+ yalc.lock
58
+ .eslintcache
59
+ .stylelintcache
60
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.9
3
+
4
+ WORKDIR /code
5
+
6
+ COPY --link --chown=1000 . .
7
+
8
+ RUN mkdir -p /tmp/cache/
9
+ RUN chmod a+rwx -R /tmp/cache/
10
+ ENV TRANSFORMERS_CACHE=/tmp/cache/
11
+
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ ENV PYTHONUNBUFFERED=1 GRADIO_ALLOW_FLAGGING=never GRADIO_NUM_PORTS=1 GRADIO_SERVER_NAME=0.0.0.0 GRADIO_SERVER_PORT=7860 SYSTEM=spaces
15
+
16
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,10 +1,10 @@
 
1
  ---
2
- title: Modelscope Gradio Components
3
- emoji: 🌍
4
- colorFrom: blue
5
- colorTo: yellow
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+
2
  ---
3
+ tags: [gradio-custom-component]
4
+ title: modelscope_gradio_components V0.0.1b8
5
+ colorFrom: red
6
+ colorTo: red
7
  sdk: docker
8
  pinned: false
9
+ license: apache-2.0
10
  ---
 
 
app.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from components.Chatbot.app import docs as chatbot_docs
3
+ from components.Docs import Docs
4
+ from components.Markdown.app import docs as markdown_docs
5
+ from components.MultimodalInput.app import docs as multimodel_input_docs
6
+
7
+ readme_docs = Docs(__file__)
8
+
9
+ docs = [
10
+ ["开始使用", readme_docs],
11
+ ["Chatbot", chatbot_docs],
12
+ ["Markdown", markdown_docs],
13
+ ["MultimodalInput", multimodel_input_docs],
14
+ ]
15
+
16
+ with gr.Blocks() as demo:
17
+ with gr.Tabs() as components_tabs:
18
+ for doc in docs:
19
+ with gr.TabItem(doc[0], id=doc[0]):
20
+ doc[1].render(components_tabs)
21
+
22
+ demo.queue().launch()
components/Chatbot/README.md ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Chatbot
2
+
3
+ 升级版的 gradio Chatbot。
4
+
5
+ - 支持前端匀速流式输出 message
6
+ - 支持输出多模态内容(音频、视频、语音、文件、文本)
7
+ - 支持多 agent 场景
8
+ - 支持自定义渲染组件,并与 Python 侧事件交互
9
+
10
+ ## 如何使用
11
+
12
+ ### 基本使用
13
+
14
+ <demo name="basic"></demo>
15
+
16
+ ### 多模态 & 支持本地文件的展示
17
+
18
+ <demo name="multimodal"></demo>
19
+
20
+ ### 控制打字机单句 message 开关
21
+
22
+ <demo name="message_config"></demo>
23
+
24
+ ### 支持手风琴内容展示
25
+
26
+ 在返回的内容中加入 `accordion` 标签,可以在内容中加入手风琴,更多用法详见 <tab-link component-tab="Markdown">Markdown 内置自定义标签</tab-link>
27
+
28
+ 同时为了适配大模型的工具调用链路,额外对某些大模型的格式做了预设配置,支持下述格式的预设处理(会将下面的格式转换成上方`accordion`标签包裹形式)
29
+
30
+ ```python
31
+ import modelscope_gradio_components as mgr
32
+ from modelscope_gradio_components.components.Chatbot.llm_thinking_presets import qwen
33
+
34
+ # 添加 qwen 解析预设
35
+ mgr.Chatbot(llm_thinking_presets=[qwen()])
36
+ ```
37
+
38
+ ```text
39
+ Action: image_gen
40
+ Action Input: {"text": "风和日丽", "resolution": "1024*1024"}
41
+ Observation: <result>![IMAGEGEN](https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1d/a2/20231213/723609ee/1926736d-7c6e-4d2f-b438-b7746b3d89f5-1.png?Expires=1702537773&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=H%2B0rIn6BMfE%2BOr1uPb7%2Br9G3%2B5w%3D)</result> 根据您的描述"风和日丽",我生成了一张图片。![](https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1d/a2/20231213/723609ee/1926736d-7c6e-4d2f-b438-b7746b3d89f5-1.png?Expires=1702537773&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=H%2B0rIn6BMfE%2BOr1uPb7%2Br9G3%2B5w%3D)
42
+
43
+ Action: 「任意文本表示,将展示为思考链调用的名称」
44
+ Action Input: 「任意json or md 内容,将展示到调用过程的下拉框」
45
+ Observation: <result>「任意 md 内容,将作为完成调用的展示的下拉框内」</result>
46
+ ```
47
+
48
+ <demo name="accordion"></demo>
49
+
50
+ ### 支持用户选择交互
51
+
52
+ 在返回的内容中加入 `select-box` 标签,更多用法详见 <tab-link component-tab="Markdown">Markdown 内置自定义标签</tab-link>
53
+
54
+ <demo name="select-box"></demo>
55
+
56
+ ### 多 bot 场景
57
+
58
+ <demo name="multi_bots"></demo>
59
+
60
+ ### 自定义标签(高阶用法,需要了解前端知识)
61
+
62
+ 详见 <tab-link component-tab="Markdown">Markdown</tab-link> 组件
63
+
64
+ ## API 及参数列表
65
+
66
+ 以下 API 均为在原有 gradio Chatbot 外的额外拓展参数。
67
+
68
+ ### value
69
+
70
+ 接口定义:
71
+
72
+ ```python
73
+
74
+ class FileMessage(GradioModel):
75
+ file: FileData
76
+ alt_text: Optional[str] = None
77
+
78
+
79
+ class MultimodalMessage(GradioModel):
80
+ name: Optional[str] = None
81
+ text: Optional[str] = None
82
+ flushing: Optional[bool] = None
83
+ avatar: Optional[Union[str, FileData]] = ''
84
+ files: Optional[List[Union[FileMessage, dict, FileData, str]]] = None
85
+
86
+ # 支持多 bot 场景
87
+ MultimodalMessageItem = Optional[Union[MultimodalMessage, MultimodalInputData,
88
+ dict, str]]
89
+
90
+
91
+ class ChatbotData(GradioRootModel):
92
+ root: List[Tuple[Union[MultimodalMessageItem, List[MultimodalMessageItem]],
93
+ Union[MultimodalMessageItem,
94
+ List[MultimodalMessageItem]]]]
95
+ ```
96
+
97
+ ### props
98
+
99
+ | 属性 | 类型 | 默认值 | 描述 |
100
+ | -------------------- | -------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | flushing | bool | True | 是否开启打字机效果。默认只有 bot 的 message 会开启,可以通过单独修改 message 的 flushing 属性精确控制每一条 message 的显示效果 |
102
+ | enable_base64 | bool | False | 是否支持渲染的内容为 base64,因为直接渲染 base64 有安全问题,默认为 False。 |
103
+ | preview | bool | True | 是否开启图片预览功能 |
104
+ | avatar_images | tuple\[str \| Path \| None \| dict \| list, str \| Path \| None \| dict\| list\] | None | 拓展gr.Chatbot的参数值,除了接收 url 外还可以接收 dict 和 list,dict 可以传入avatar和name字段,name字段在渲染时会显示在头像下方。 <br/> - 当传入 dict 时,必须包含有avatar字段。<br/> - 当传入 list 时,一般对应多 bot 模式,每一项可以接收前面所有的值,每个 bot 的头像与 message 中 bot 的位置一一对应 |
105
+ | avatar_image_align | Literal['top', 'middle', 'bottom'] | 'bottom' | 控制头像与 message 的对齐方式,默认为下对齐 |
106
+ | avatar_image_width | int | 45 | 头像与名称的宽度 |
107
+ | flushing_speed | int | 3 | 打字机速度,值为 1 - 10,值越大速度越快 |
108
+ | llm_thinking_presets | list\[dict\] | \[\] | llm 思考链路解析预设,可以将 llm 调用工具的输出格式转为固定的前端展示格式,需要从modelscope_gradio_components.Chatbot.llm_thinking_presets引入,目前支持:qwen |
109
+ | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
110
+
111
+ **CustomComponent 定义如下**
112
+
113
+ ```python
114
+ class CustomComponentDict(TypedDict):
115
+ props: Optional[List[str]]
116
+ template: Optional[str]
117
+ js: Optional[str]
118
+ ```
119
+
120
+ ### 内置的自定义标签
121
+
122
+ 见 <tab-link component-tab="Markdown">Markdown 内置自定义标签</tab-link>
123
+
124
+ ### event listeners
125
+
126
+ | 事件 | 描述 |
127
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
128
+ | `mgr.Chatbot.flushed(fn, ···)` | 当打字机效果结束时触发。EventData 为:<br/> - index:当前 message 的 index tuple。<br/> - value:当前 message value。 |
129
+ | `mgr.Chatbot.custom(fn, ···)` | 自定义标签触发事件时触发,EventData 为:<br/> - index:前 message 的 index tuple。<br/> - tag:当前触发的标签。<br/> - tag_index:当前触发标签的 index,此 index 在 mesage 的 index tuple 基础上重新计算。<br/> - value:自定义传入的值。 |
components/Chatbot/app.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from components.Docs import Docs
2
+
3
+ docs = Docs(__file__)
4
+
5
+ if __name__ == "__main__":
6
+ docs.render().queue().launch()
components/Chatbot/demos/accordion.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+ from modelscope_gradio_components.components.Chatbot.llm_thinking_presets import \
7
+ qwen
8
+
9
+
10
+ def resolve_assets(relative_path):
11
+ return os.path.join(os.path.dirname(__file__), "../resources",
12
+ relative_path)
13
+
14
+
15
+ conversation = [
16
+ [
17
+ None, {
18
+ "text": f"""
19
+ 标签语法:
20
+ <accordion title="调用 tool">
21
+
22
+ ```json
23
+ {{"text": "风和日丽", "resolution": "1024*1024"}}
24
+ ```
25
+ </accordion>
26
+
27
+ qwen preset:
28
+ Action: image_gen
29
+ Action Input: {{"text": "风和日丽", "resolution": "1024*1024"}}
30
+ Observation: <result>![IMAGEGEN]({resolve_assets("screen.jpeg")})</result> 根据您的描述"风和日丽",我生成了一张图片。![]({resolve_assets("screen.jpeg")})
31
+
32
+ Action: 「任意文本表示,将展示为思考链调用的名称」
33
+ Action Input: 「任意json or md 内容,将展示到调用过程的下拉框」
34
+ Observation: <result>「任意 md 内容,将作为完成调用的展示的下拉框内」</result>
35
+ """,
36
+ "flushing": False
37
+ }
38
+ ],
39
+ ]
40
+
41
+ with gr.Blocks() as demo:
42
+ mgr.Chatbot(
43
+ value=conversation,
44
+ llm_thinking_presets=[qwen()],
45
+ height=600,
46
+ )
47
+
48
+ if __name__ == "__main__":
49
+ demo.queue().launch()
components/Chatbot/demos/basic.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+ conversation = [
9
+ [
10
+ None,
11
+ {
12
+ # bot 第一句话关闭打字机效果,直接输入内容
13
+ "text": "Hello I'm a chatbot",
14
+ "flushing": False
15
+ }
16
+ ],
17
+ ]
18
+
19
+
20
+ def submit(_input, _chatbot):
21
+ _chatbot.append([_input, None])
22
+ yield gr.update(interactive=False, value=None), _chatbot
23
+ time.sleep(2)
24
+ _chatbot[-1][1] = {"text": _input.text + '!'}
25
+ yield {
26
+ chatbot: _chatbot,
27
+ }
28
+
29
+
30
+ def flushed():
31
+ return gr.update(interactive=True)
32
+
33
+
34
+ with gr.Blocks() as demo:
35
+ chatbot = mgr.Chatbot(
36
+ value=conversation,
37
+ avatar_images=[
38
+ os.path.join(os.path.dirname(__file__), "../resources/user.jpeg"),
39
+ {
40
+ "name":
41
+ "bot",
42
+ "avatar":
43
+ os.path.join(os.path.dirname(__file__),
44
+ "../resources/bot.jpeg")
45
+ }
46
+ ],
47
+ height=600,
48
+ )
49
+
50
+ input = mgr.MultimodalInput()
51
+ input.submit(fn=submit, inputs=[input, chatbot], outputs=[input, chatbot])
52
+ chatbot.flushed(fn=flushed, outputs=[input])
53
+
54
+ if __name__ == "__main__":
55
+ demo.queue().launch()
components/Chatbot/demos/message_config.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+
8
+ def submit(_chatbot):
9
+ _chatbot.append(["test user", "test bot"]) # 此时只有 bot 会开启打字机效果
10
+ yield _chatbot
11
+ time.sleep(2)
12
+ _chatbot.append(["test user", {
13
+ "text": "test bot",
14
+ "flushing": False
15
+ }]) # 两者都没有打字机效果
16
+ yield _chatbot
17
+ time.sleep(2)
18
+ _chatbot.append([{
19
+ "text": "test user",
20
+ "flushing": True
21
+ }, {
22
+ "text": "test bot",
23
+ "flushing": False
24
+ }]) # user 会开启打字机效果
25
+ yield _chatbot
26
+
27
+
28
+ with gr.Blocks() as demo:
29
+ chatbot = mgr.Chatbot(height=600, )
30
+ button = gr.Button("Submit")
31
+ button.click(fn=submit, inputs=[chatbot], outputs=[chatbot])
32
+
33
+ if __name__ == "__main__":
34
+ demo.queue().launch()
components/Chatbot/demos/multi_bots.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+
9
+ def resolve_assets(relative_path):
10
+ return os.path.join(os.path.dirname(__file__), "../resources",
11
+ relative_path)
12
+
13
+
14
+ conversation = [
15
+ [
16
+ None,
17
+ {
18
+ # bot 第一句话关闭打字机效果,直接输入内容
19
+ "text": "Hello I'm a chatbot",
20
+ "flushing": False
21
+ }
22
+ ],
23
+ ]
24
+
25
+
26
+ def get_last_bot_message(chatbot):
27
+ return chatbot[-1][1]
28
+
29
+
30
+ def create_music_bot_message(text: str):
31
+ return {
32
+ "text": text,
33
+ }
34
+
35
+
36
+ def create_image_bot_message(text: str):
37
+ return {
38
+ "text": text,
39
+ }
40
+
41
+
42
+ def submit(_input, _chatbot):
43
+ _chatbot.append([_input, None])
44
+ yield gr.update(interactive=False, value=None), _chatbot
45
+ _chatbot[-1][1] = [
46
+ "Hello",
47
+ create_image_bot_message("Hello"),
48
+ create_music_bot_message("Hello")
49
+ ]
50
+
51
+ time.sleep(2)
52
+ get_last_bot_message(_chatbot)[1][
53
+ "text"] = f"""Hello, I\'m a image bot\n![image]({resolve_assets("user.jpeg")})"""
54
+ get_last_bot_message(_chatbot)[2][
55
+ "text"] = f"""Hello, I\'m a music bot <audio src="{resolve_assets("audio.wav")}"></audio>"""
56
+ yield {
57
+ chatbot: _chatbot,
58
+ }
59
+
60
+
61
+ def flushed():
62
+ return gr.update(interactive=True)
63
+
64
+
65
+ with gr.Blocks() as demo:
66
+ chatbot = mgr.Chatbot(
67
+ value=conversation,
68
+ avatar_image_width=40,
69
+ avatar_images=[
70
+ resolve_assets('user.jpeg'),
71
+ # default bot avatar and name
72
+ [{
73
+ "name": "bot",
74
+ "avatar": resolve_assets('bot.jpeg')
75
+ }, {
76
+ "name": "image bot",
77
+ "avatar": resolve_assets('image-bot.jpeg')
78
+ }, {
79
+ "name": "music bot",
80
+ "avatar": resolve_assets('music-bot.jpeg')
81
+ }]
82
+ ],
83
+ height=600,
84
+ )
85
+
86
+ input = mgr.MultimodalInput()
87
+ input.submit(fn=submit, inputs=[input, chatbot], outputs=[input, chatbot])
88
+ chatbot.flushed(fn=flushed, outputs=[input])
89
+
90
+ if __name__ == "__main__":
91
+ demo.queue().launch()
components/Chatbot/demos/multimodal.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+
8
+ def resolve_assets(relative_path):
9
+ return os.path.join(os.path.dirname(__file__), "../resources",
10
+ relative_path)
11
+
12
+
13
+ conversation = [
14
+ [
15
+ None, {
16
+ "text": f"""
17
+ 图片
18
+
19
+ ![image]({resolve_assets("bot.jpeg")})
20
+
21
+ <img src="{resolve_assets("user.jpeg")}" />
22
+
23
+ 视频
24
+
25
+ <video src="{resolve_assets("dog.mp4")}"></video>
26
+
27
+ 音频
28
+
29
+ <audio src="{resolve_assets("audio.wav")}"></audio>
30
+ """,
31
+ "flushing": False
32
+ }
33
+ ],
34
+ ]
35
+
36
+ with gr.Blocks() as demo:
37
+ mgr.Chatbot(
38
+ value=conversation,
39
+ height=600,
40
+ )
41
+
42
+ if __name__ == "__main__":
43
+ demo.queue().launch()
components/Chatbot/demos/select-box.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+ # label 为对用户展示值,value 为实际选择值
8
+ options = [{"label": "A", "value": "a"}, "b", "c"]
9
+
10
+ conversation = [[
11
+ None, f"""
12
+ Single Select: <select-box options='{json.dumps(options)}' select-once></select-box>
13
+
14
+ Multiple Select:<select-box type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
15
+
16
+ Vertical Direction:
17
+
18
+ <select-box direction="vertical" type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
19
+
20
+ Card Shape:
21
+
22
+ <select-box shape="card" options='{json.dumps(options)}' select-once equal-height></select-box>
23
+
24
+
25
+ <select-box shape="card" columns="2" options='{json.dumps(options)}' select-once equal-height></select-box>
26
+
27
+
28
+ <select-box shape="card" direction="vertical" options='{json.dumps(options)}' select-once equal-height></select-box>
29
+ """
30
+ ]]
31
+
32
+
33
+ # 必须使用 gr.EventData 显示标注
34
+ def fn(data: gr.EventData):
35
+ print(data._data)
36
+
37
+
38
+ with gr.Blocks() as demo:
39
+ chatbot = mgr.Chatbot(
40
+ value=conversation,
41
+ flushing=False,
42
+ height=600,
43
+ )
44
+ # 所有自定义标签都会触发 custom 事件
45
+ chatbot.custom(fn=fn)
46
+
47
+ if __name__ == "__main__":
48
+ demo.queue().launch()
components/Chatbot/resources/audio.wav ADDED
Binary file (44.8 kB). View file
 
components/Chatbot/resources/bot.jpeg ADDED
components/Chatbot/resources/custom_components/custom_select.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (props, cc, { el, onMount }) => {
2
+ const options = JSON.parse(props.options);
3
+ el.innerHTML = `
4
+ ${options
5
+ .map((option) => {
6
+ return `<div>
7
+ <label>${option} <input type="radio"/></label>
8
+ <div>`;
9
+ })
10
+ .join('')}
11
+ `;
12
+ onMount(() => {
13
+ const inputs = Array.from(el.getElementsByTagName('input'));
14
+ Array.from(el.getElementsByTagName('label')).forEach((label, i) => {
15
+ label.addEventListener('click', () => {
16
+ inputs.forEach((input) => {
17
+ input.checked = false;
18
+ });
19
+ const input = label.getElementsByTagName('input')[0];
20
+ input.checked = true;
21
+ // 通过 cc.dispatch 向 python 侧发送通知
22
+ cc.dispatch(options[i]);
23
+ });
24
+ });
25
+ });
26
+ };
components/Chatbot/resources/dog.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39d086ce29e48cf76e5042d2f3f0611ee46575f70fa3dc0c40dd4cfffde3d933
3
+ size 8626383
components/Chatbot/resources/image-bot.jpeg ADDED
components/Chatbot/resources/music-bot.jpeg ADDED
components/Chatbot/resources/screen.jpeg ADDED
components/Chatbot/resources/user.jpeg ADDED
components/Docs.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from typing import Callable
4
+
5
+ import gradio as gr
6
+
7
+ import modelscope_gradio_components as mgr
8
+
9
+ from .parse_markdown import parse_markdown
10
+
11
+ with open(os.path.join(os.path.dirname(__file__), "tab-link.js")) as f:
12
+ tab_link_js = f.read()
13
+
14
+ custom_components = {
15
+ "tab-link": {
16
+ "props": ["tab", "component-tab"],
17
+ "js": tab_link_js
18
+ }
19
+ }
20
+
21
+
22
+ def remove_formatter(markdown_text):
23
+ pattern = r"^---[\s\S]*?---"
24
+
25
+ replaced_text = re.sub(pattern, "", markdown_text)
26
+
27
+ return replaced_text
28
+
29
+
30
+ def list_demos(dir_path: str, prefix=''):
31
+ result = []
32
+ if (not os.path.isdir(dir_path)):
33
+ return result
34
+ for name in os.listdir(dir_path):
35
+ path = os.path.join(dir_path, name)
36
+
37
+ if os.path.isfile(path):
38
+ result.append(prefix + name)
39
+ elif os.path.isdir(path):
40
+ sub_prefix = prefix + name + '/'
41
+ result.extend(list_demos(path, sub_prefix))
42
+
43
+ return result
44
+
45
+
46
+ def get_demo_modules(file_path: str):
47
+ import importlib.util
48
+
49
+ demos = [
50
+ demo for demo in list_demos(
51
+ os.path.join(os.path.dirname(file_path), "demos"))
52
+ if demo.endswith(".py")
53
+ ]
54
+ demo_modules = {}
55
+ for demo in demos:
56
+ demo_name = demo.split(".")[0]
57
+ spec = importlib.util.spec_from_file_location(
58
+ "demo", os.path.join(os.path.dirname(file_path), "demos", demo))
59
+ module = importlib.util.module_from_spec(spec)
60
+ spec.loader.exec_module(module)
61
+ demo_modules[demo_name] = module
62
+ return demo_modules
63
+
64
+
65
+ class Docs:
66
+
67
+ def __init__(self, file_path: str, markdown_files: list = None):
68
+ self.file_path = file_path
69
+ self.demo_modules = get_demo_modules(file_path)
70
+ # default current directory
71
+ self.markdown_files = markdown_files if markdown_files else [
72
+ file_name for file_name in os.listdir(os.path.dirname(file_path))
73
+ if file_name.endswith(".md")
74
+ ]
75
+ self.tabs = None
76
+
77
+ def read_file(self, relative_path: str):
78
+ with open(os.path.join(os.path.dirname(self.file_path), relative_path),
79
+ "r") as f:
80
+ return f.read()
81
+
82
+ def render_demo(self, demo_name, prefix='', suffix=''):
83
+ content = self.read_file(f"./demos/{demo_name}.py")
84
+ module = self.demo_modules[demo_name]
85
+ with gr.Accordion("Show Demo", open=False):
86
+ with gr.Row():
87
+ with gr.Column():
88
+ mgr.Markdown(f"""
89
+ {prefix}
90
+ ````python
91
+ {content}
92
+ ````
93
+ {suffix}
94
+ """,
95
+ header_links=True,
96
+ custom_components=custom_components)
97
+ with gr.Column():
98
+ module.demo.render()
99
+
100
+ def render_markdown(self,
101
+ markdown_file,
102
+ on_tab_link_click: Callable = None,
103
+ components_tabs=None):
104
+ items = parse_markdown(remove_formatter(self.read_file(markdown_file)),
105
+ read_file=self.read_file)
106
+ for item in items:
107
+ if item["type"] == "text":
108
+ md = mgr.Markdown(item["value"],
109
+ header_links=True,
110
+ custom_components=custom_components)
111
+ # 过滤
112
+ deps = [dep for dep in [components_tabs, self.tabs] if dep]
113
+ if len(deps) > 0:
114
+ md.custom(fn=on_tab_link_click, outputs=deps)
115
+ elif item["type"] == "demo":
116
+ self.render_demo(item["name"],
117
+ prefix=item["prefix"],
118
+ suffix=item["suffix"])
119
+
120
+ def render(self, components_tabs=None):
121
+
122
+ def tab_link_click(data: gr.EventData):
123
+ tab: str = data._data["value"].get("tab", '')
124
+ component_tab: str = data._data["value"].get("component_tab", '')
125
+ if tab and tabs:
126
+ return {tabs: gr.update(selected=tab)}
127
+ elif components_tabs and component_tab:
128
+ return {components_tabs: gr.update(selected=component_tab)}
129
+
130
+ with gr.Blocks() as demo:
131
+
132
+ if len(self.markdown_files) > 1:
133
+ with gr.Tabs() as tabs:
134
+ self.tabs = tabs
135
+
136
+ for markdown_file in self.markdown_files:
137
+ tab_name = ".".join(markdown_file.split(".")[:-1])
138
+ with gr.TabItem(tab_name, id=tab_name):
139
+ self.render_markdown(
140
+ markdown_file,
141
+ on_tab_link_click=tab_link_click,
142
+ components_tabs=components_tabs)
143
+ else:
144
+ self.render_markdown(self.markdown_files[0],
145
+ on_tab_link_click=tab_link_click,
146
+ components_tabs=components_tabs)
147
+ return demo
components/Markdown/README.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Markdown
2
+
3
+ 升级版的 gradio Markdown。
4
+
5
+ - 支持输出多模态内容(音频、视频、语音、文件、文本)
6
+ - 支持自定义渲染组件,并与 Python 侧事件交互
7
+
8
+ ## 如何使用
9
+
10
+ ### 基本使用
11
+
12
+ <demo name="basic"></demo>
13
+
14
+ ### 多模态 & 支持本地文件的展示
15
+
16
+ <demo name="multimodal"></demo>
17
+
18
+ ### 支持手风琴内容展示
19
+
20
+ 在返回的内容中加入 `accordion` 标签,更多用法详见 <tab-link tab="custom_tags/accordion">accordion</tab-link>
21
+
22
+ <demo name="accordion"></demo>
23
+
24
+ ### 支持用户选择交互
25
+
26
+ 在返回的内容中加入 `select-box` 标签,更多用法详见 <tab-link tab="custom_tags/select-box">select-box</tab-link>
27
+
28
+ <demo name="select-box"></demo>
29
+
30
+ ### 自定义标签(高阶用法,需要了解前端知识)
31
+
32
+ <demo name="custom-tag"></demo>
33
+
34
+ #### 引入 js
35
+
36
+ <demo name="custom-tag2"></demo>
37
+
38
+ template只能做简单的变量替换,如果想要引入更多自定义的行为,如条件判断、循环渲染等,请使用 js 控制 el 自行处理,下面是简单的示例:
39
+
40
+ <demo name="custom-tag3">
41
+ <demo-suffix>
42
+ custom_select.js
43
+
44
+ ```js
45
+ <file src="./resources/custom_components/custom_select.js"></file>
46
+ ```
47
+
48
+ </demo-suffix>
49
+ </demo>
50
+
51
+ #### 与 Python 侧交互
52
+
53
+ 在 js 中可以使用`cc.dispatch`触发 Python 侧监听的`custom`事件,以前面的custom_select.js为例,我们在前端调用了`cc.dispatch(options[i])`,则会向 Python 侧同时发送通知。
54
+
55
+ <demo name="custom-tag4"></demo>
56
+
57
+ ## API 及参数列表
58
+
59
+ 以下 API 均为在原有 gradio Markdown 外的额外拓展参数。
60
+
61
+ ### props
62
+
63
+ | 属性 | 类型 | 默认值 | 描述 |
64
+ | ----------------- | --------------------------------------------------------------- | ------ | --------------------------------------------------------------------------- |
65
+ | enable_base64 | bool | False | 是否支持渲染的内容为 base64,因为直接渲染 base64 有安全问题,默认为 False。 |
66
+ | preview | bool | True | 是否开启图片预览功能 |
67
+ | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
68
+
69
+ **CustomComponent 定义如下**
70
+
71
+ ```python
72
+ class CustomComponentDict(TypedDict):
73
+ props: Optional[List[str]]
74
+ template: Optional[str]
75
+ js: Optional[str]
76
+ ```
77
+
78
+ ### 内置的自定义标签
79
+
80
+ - <tab-link tab="custom_tags/select-box">select-box</tab-link>
81
+ - <tab-link tab="custom_tags/accordion">accordion</tab-link>
82
+
83
+ ### event listeners
84
+
85
+ | 事件 | 描述 |
86
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
87
+ | `mgr.Markdown.custom(fn, ···)` | 自定义标签触发事件时触发,EventData 为:<br/> - index:当前 message 的 index tuple ([message index, user group(index 0) or bot group(index 1), user/bot group index])。<br/> - tag:当前触发的标签。<br/> - tag_index:当前触发标签的 index,此 index 在 mesage 的 index tuple 基础上重新计算。<br/> - value:自定义传入的值。 |
components/Markdown/app.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from components.Docs import Docs
4
+
5
+
6
+ def resolve(relative_path: str):
7
+ return os.path.join(os.path.dirname(__file__), relative_path)
8
+
9
+
10
+ docs = Docs(
11
+ __file__,
12
+ markdown_files=(["README.md"] + [
13
+ f"custom_tags/{file_name}"
14
+ for file_name in os.listdir(resolve('custom_tags'))
15
+ if file_name.endswith(".md")
16
+ ]),
17
+ )
18
+
19
+ if __name__ == "__main__":
20
+ docs.render().queue().launch()
components/Markdown/custom_tags/accordion.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # accordion
2
+
3
+ 在 markdown 文本中添加手风琴效果。
4
+
5
+ ## 如何使用
6
+
7
+ ### 基本使用
8
+
9
+ <demo name="custom_tags/accordion/basic"></demo>
10
+
11
+ ### 使用 accordion-title 标记
12
+
13
+ 使用`::accordion-title[content]`的形式可以在标题输入 markdown 文本。
14
+
15
+ <demo name="custom_tags/accordion/accordion-title"></demo>
16
+
17
+ ## API 及参数列表
18
+
19
+ ### props
20
+
21
+ | 属性 | 类型 | 默认值 | 描述 |
22
+ | ----- | ------ | ------ | ------------ |
23
+ | title | string | | 手风琴的标题 |
components/Markdown/custom_tags/select-box.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # select-box
2
+
3
+ 在 markdown 文本中添加选择交互框。
4
+
5
+ ## 如何使用
6
+
7
+ ### 基本使用
8
+
9
+ <demo name="custom_tags/select-box/basic"></demo>
10
+
11
+ ### Card 样式
12
+
13
+ <demo name="custom_tags/select-box/card_shape"></demo>
14
+
15
+ ### Card 自适应内部元素宽度
16
+
17
+ <demo name="custom_tags/select-box/card_shape_width_auto"></demo>
18
+
19
+ ### 监听 Python 事件
20
+
21
+ <demo name="custom_tags/select-box/python_events"></demo>
22
+
23
+ ## API 及参数列表
24
+
25
+ ### value
26
+
27
+ custom 事件中 custom_data value 对应值, 返回值为用户 options 传入的对应 value ,如果type="checkbox",则返回一个 list。
28
+
29
+ ### props
30
+
31
+ | 属性 | 类型 | 默认值 | 描述 |
32
+ | ------------ | ------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33
+ | type | 'checkbox' \| 'radio' | 'radio' | 选择框类型,'radio' 为单选框、'checkbox'为多选框。 |
34
+ | disabled | boolean | | 禁用选择,通常在需要读取历史信息二次渲染时会用到。 |
35
+ | value | string | | 默认选中值,通常适用于`type="checkbox"`时提前为用户选择部分选项和设置`disabled`后的默认值渲染。 |
36
+ | direction |  'horizontal' \| 'vertical' | 'horizontal' | 横向或竖向排列选择框 |
37
+ | shape | 'card' \| 'default' | 'default' | 选择框样式 |
38
+ | options | (string\| { label?: string, value?: string, imgSrc?: string})\[\] | | 为用户提供的选项值,每一项可以为 string 或 object。 当值为 object 时可以接收更多自定义值,其中imgSrc只有当shape="card"时才生效。 |
39
+ | select-once | boolean | false | 是否只允许用户选择一次 |
40
+ | submit-text | string | | 提交按钮的展示值,当该属性有值时,会展示提交按钮,此时用户只有点击提交按钮后才会触发选择事件。 |
41
+ | columns | number \| { xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?: number } | { xs: 1, sm:  2, md: 2, lg:  4} | 当shape="card"时才生效。每一行选项占用列数,值的范围为1 - 24,建议此项取值可以被 24 整除,否则可能列数会不符合预期。 当此项传入值为对象时,可以响应式控制每一行渲染列数,响应阈值如下:<br/> - xs:屏幕 < 576px <br/> - sm:屏幕 ≥ 576px <br/> - md:屏幕 ≥ 768px <br/> - lg:屏幕 ≥ 992px <br/> - xl:屏幕 ≥ 1200px <br/> - xxl:屏幕 ≥ 1600px 当direction为vertical时此配置不生效。 |
42
+ | item-width | string | | 当shape="card"时才生效。每个选项的宽度,如:'auto'、'100px',默认使用 columns 自动分配的宽度。 |
43
+ | item-height | string | | 当shape="card"时才生效。每个选项的高度,默认自适应元素高度。 |
44
+ | img-height | string | '160px' | 当shape="card"时才生效。每个选项中图片的高度。 |
45
+ | equal-height | boolean | false | 当shape="card"时才生效。是否每一行的选项高度都相等,会使用高度最高的选项。 |
components/Markdown/demos/accordion.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(f"""
7
+ 普通调用:
8
+
9
+ <accordion title="调用 tool">
10
+
11
+ ```json
12
+ {{"text": "风和日丽", "resolution": "1024*1024"}}
13
+ ```
14
+
15
+ </accordion>
16
+
17
+ 使用 ::accordion-title 标记支持 markdown 语法:
18
+
19
+ <accordion>
20
+
21
+ ::accordion-title[调用 `tool`]
22
+
23
+ ```json
24
+ {{"text": "风和日丽", "resolution": "1024*1024"}}
25
+ ```
26
+ </accordion>
27
+ """)
28
+
29
+ if __name__ == "__main__":
30
+ demo.queue().launch()
components/Markdown/demos/basic.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(
7
+ "This _example_ was **written** in [Markdown](https://en.wikipedia.org/wiki/Markdown)\n"
8
+ )
9
+
10
+ if __name__ == "__main__":
11
+ demo.queue().launch()
components/Markdown/demos/custom-tag.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(
7
+ f"""
8
+ custom tag:<custom-tag value="aaa"></custom-tag>
9
+ """,
10
+ custom_components={
11
+ # key 为标签名
12
+ "custom-tag": {
13
+ # 自定义标签允许接收的值,可在调用标签时由用户传入
14
+ "props": ["value"],
15
+ # 实际渲染时的 template, 可以使用 {} 将用户传入的 props 替换。
16
+ "template": "<div>{value}</div>"
17
+ }
18
+ })
19
+
20
+ if __name__ == "__main__":
21
+ demo.queue().launch()
components/Markdown/demos/custom-tag2.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(
7
+ f"""
8
+ custom tag:<custom-tag value="aaa"></custom-tag>
9
+ """,
10
+ custom_components={
11
+ # key 为标签名
12
+ "custom-tag": {
13
+ "props": ["value"],
14
+ "template":
15
+ "<button onclick='{onClick}'>{value}</button>",
16
+ # js 接收一个 function
17
+ "js":
18
+ """
19
+ (props, cc, { el, onMount }) => {
20
+ // onMount 会在 template 渲染完成后调用
21
+ onMount(() => {
22
+ // el 是当前自定义标签挂载的 container
23
+ console.log(el)
24
+ })
25
+ console.log(props.children) // 默认会包含 children,可以拿到 <tag>xx</tag> 标签内的内容 xx
26
+ // 可以返回一个对象,对象里的值会与 props 做并集最后渲染到模板中
27
+ return {
28
+ value: 'Click Me: ' + props.value,
29
+ onClick: () => {
30
+ alert('hello')
31
+ }
32
+ }
33
+ }"""
34
+ }
35
+ })
36
+
37
+ if __name__ == "__main__":
38
+ demo.queue().launch()
components/Markdown/demos/custom-tag3.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+ options = ["a", "b", "c"]
9
+
10
+
11
+ def resolve_assets(relative_path):
12
+ return os.path.join(os.path.dirname(__file__), "../resources",
13
+ relative_path)
14
+
15
+
16
+ with open(resolve_assets("./custom_components/custom_select.js"), 'r') as f:
17
+ custom_select_js = f.read()
18
+
19
+ with gr.Blocks() as demo:
20
+ mgr.Markdown(value=f"""
21
+ custom tag: <custom-select options='{json.dumps(options)}'></custom-select>
22
+ """,
23
+ custom_components={
24
+ "custom-select": {
25
+ "props": ["options"],
26
+ "js": custom_select_js,
27
+ }
28
+ })
29
+
30
+ if __name__ == "__main__":
31
+ demo.queue().launch()
components/Markdown/demos/custom-tag4.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+ options = ["a", "b", "c"]
9
+
10
+
11
+ def resolve_assets(relative_path):
12
+ return os.path.join(os.path.dirname(__file__), "../resources",
13
+ relative_path)
14
+
15
+
16
+ with open(resolve_assets("./custom_components/custom_select.js"), 'r') as f:
17
+ custom_select_js = f.read()
18
+
19
+
20
+ # 注意一定要显示指明类型,gradio 使用 ioc 机制注入值
21
+ def fn(data: gr.EventData):
22
+ # custom {'index': [0, 1, 0], 'tag': 'custom-select', 'tag_index': 0, 'value': 'option A'}
23
+ print("custom value", data._data)
24
+
25
+
26
+ with gr.Blocks() as demo:
27
+ md = mgr.Markdown(value=f"""
28
+ custom tag: <custom-select options='{json.dumps(options)}'></custom-select>
29
+ """,
30
+ custom_components={
31
+ "custom-select": {
32
+ "props": ["options"],
33
+ "js": custom_select_js,
34
+ }
35
+ })
36
+ md.custom(fn=fn)
37
+
38
+ if __name__ == "__main__":
39
+ demo.queue().launch()
components/Markdown/demos/custom_tags/accordion/accordion-title.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(f"""
7
+ <accordion>
8
+
9
+ ::accordion-title[调用 `tool`]
10
+
11
+ ```json
12
+ {{"text": "风和日丽", "resolution": "1024*1024"}}
13
+ ```
14
+
15
+ </accordion>
16
+ """)
17
+
18
+ if __name__ == "__main__":
19
+ demo.queue().launch()
components/Markdown/demos/custom_tags/accordion/basic.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+ with gr.Blocks() as demo:
6
+ mgr.Markdown(f"""
7
+ <accordion title="调用 tool">
8
+
9
+ ```json
10
+ {{"text": "风和日丽", "resolution": "1024*1024"}}
11
+ ```
12
+
13
+ </accordion>
14
+ """)
15
+
16
+ if __name__ == "__main__":
17
+ demo.queue().launch()
components/Markdown/demos/custom_tags/select-box/basic.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+ options = [{"label": "A", "value": "a"}, "b", "c"]
8
+
9
+ with gr.Blocks() as demo:
10
+ mgr.Markdown(
11
+ f"""Single Select: <select-box options='{json.dumps(options)}' select-once></select-box>
12
+
13
+ Multiple Select:<select-box type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
14
+
15
+ Vertical Direction:
16
+
17
+ <select-box direction="vertical" type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
18
+ """, )
19
+
20
+ if __name__ == "__main__":
21
+ demo.queue().launch()
components/Markdown/demos/custom_tags/select-box/card_shape.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+ # card 支持额外传入 imgSrc 属性作为样式封面
9
+ options = [{
10
+ "label":
11
+ "A",
12
+ "imgSrc":
13
+ os.path.join(os.path.dirname(__file__), '../../../resources/screen.jpeg'),
14
+ "value":
15
+ "a"
16
+ }, "b", "c", "d"]
17
+
18
+ with gr.Blocks() as demo:
19
+ mgr.Markdown(
20
+ f"""<select-box shape="card" options='{json.dumps(options)}' select-once equal-height></select-box>
21
+
22
+ Custom Columns:
23
+
24
+ <select-box shape="card" columns="2" options='{json.dumps(options)}' select-once equal-height></select-box>
25
+
26
+ Vertical Direction:
27
+
28
+ <select-box shape="card" direction="vertical" options='{json.dumps(options)}' select-once equal-height></select-box>
29
+ """)
30
+
31
+ if __name__ == "__main__":
32
+ demo.queue().launch()
components/Markdown/demos/custom_tags/select-box/card_shape_width_auto.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_gradio_components as mgr
7
+
8
+ # card 支持额外传入 imgSrc 属性作为样式封面
9
+ options = [{
10
+ "label":
11
+ "A",
12
+ "imgSrc":
13
+ os.path.join(os.path.dirname(__file__), '../../../resources/screen.jpeg'),
14
+ "value":
15
+ "a"
16
+ }, "b", "c", "d"]
17
+
18
+ with gr.Blocks() as demo:
19
+ mgr.Markdown(
20
+ # 填写 item-width="auto"
21
+ f"""
22
+ <select-box shape="card" direction="vertical" options='{json.dumps(options)}' select-once item-width="auto"></select-box>
23
+ """, )
24
+
25
+ if __name__ == "__main__":
26
+ demo.queue().launch()
components/Markdown/demos/custom_tags/select-box/python_events.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+ options = [{"label": "A", "value": "a"}, "b", "c"]
8
+
9
+
10
+ def fn(data: gr.EventData):
11
+ custom_data = data._data
12
+ if (custom_data["tag"] == "select-box"):
13
+ print(custom_data["value"]) # 用户选择的值,与 options 中的 value 对应
14
+
15
+
16
+ with gr.Blocks() as demo:
17
+ md = mgr.Markdown(
18
+ f"<select-box options='{json.dumps(options)}' select-once></select-box>"
19
+ )
20
+ md.custom(fn=fn)
21
+
22
+ if __name__ == "__main__":
23
+ demo.queue().launch()
components/Markdown/demos/multimodal.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+
8
+ def resolve_assets(relative_path):
9
+ return os.path.join(os.path.dirname(__file__), "../resources",
10
+ relative_path)
11
+
12
+
13
+ with gr.Blocks() as demo:
14
+ mgr.Markdown(f"""
15
+ 图片
16
+
17
+ ![image]({resolve_assets("bot.jpeg")})
18
+
19
+ <img src="{resolve_assets("user.jpeg")}" />
20
+
21
+ 视频
22
+
23
+ <video src="{resolve_assets("dog.mp4")}"></video>
24
+
25
+ 音频
26
+
27
+ <audio src="{resolve_assets("audio.wav")}"></audio>
28
+ """)
29
+
30
+ if __name__ == "__main__":
31
+ demo.queue().launch()
components/Markdown/demos/select-box.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+
5
+ import modelscope_gradio_components as mgr
6
+
7
+ # label 为对用户展示值,value 为实际选择值
8
+ options = [{"label": "A", "value": "a"}, "b", "c"]
9
+
10
+ with gr.Blocks() as demo:
11
+ mgr.Markdown(f"""
12
+ Single Select: <select-box options='{json.dumps(options)}' select-once></select-box>
13
+
14
+ Multiple Select:<select-box type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
15
+
16
+ Vertical Direction:
17
+
18
+ <select-box direction="vertical" type="checkbox" options='{json.dumps(options)}' select-once submit-text="Submit"></select-box>
19
+
20
+ Card Shape:
21
+
22
+ <select-box shape="card" options='{json.dumps(options)}' select-once equal-height></select-box>
23
+
24
+
25
+ <select-box shape="card" columns="2" options='{json.dumps(options)}' select-once equal-height></select-box>
26
+
27
+
28
+ <select-box shape="card" direction="vertical" options='{json.dumps(options)}' select-once equal-height></select-box>
29
+ """)
30
+
31
+ if __name__ == "__main__":
32
+ demo.queue().launch()
components/Markdown/resources/audio.wav ADDED
Binary file (44.8 kB). View file
 
components/Markdown/resources/bot.jpeg ADDED
components/Markdown/resources/custom_components/custom_select.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (props, cc, { el, onMount }) => {
2
+ const options = JSON.parse(props.options);
3
+ el.innerHTML = `
4
+ ${options
5
+ .map((option) => {
6
+ return `<div>
7
+ <label>${option} <input type="radio"/></label>
8
+ <div>`;
9
+ })
10
+ .join('')}
11
+ `;
12
+ onMount(() => {
13
+ const inputs = Array.from(el.getElementsByTagName('input'));
14
+ Array.from(el.getElementsByTagName('label')).forEach((label, i) => {
15
+ label.addEventListener('click', () => {
16
+ inputs.forEach((input) => {
17
+ input.checked = false;
18
+ });
19
+ const input = label.getElementsByTagName('input')[0];
20
+ input.checked = true;
21
+ // 通过 cc.dispatch 向 python 侧发送通知
22
+ cc.dispatch(options[i]);
23
+ });
24
+ });
25
+ });
26
+ };
components/Markdown/resources/dog.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39d086ce29e48cf76e5042d2f3f0611ee46575f70fa3dc0c40dd4cfffde3d933
3
+ size 8626383
components/Markdown/resources/screen.jpeg ADDED
components/Markdown/resources/user.jpeg ADDED
components/MultimodalInput/README.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MutilmodalInput 
2
+
3
+ 多模态输入框,支持上传文件、录音、照相等功能。
4
+
5
+ - 支持文本输入+文件上传共同提交
6
+ - 支持文件上传时的图片、音频预览
7
+ - 提交内容作为 Chatbot 输入多模态内容作为用户输入问题自动匹配
8
+ - 支持用户录音和拍照
9
+
10
+ ## 如何使用
11
+
12
+ ### 基本使用
13
+
14
+ <demo name="basic"></demo>
15
+
16
+ ### 与 Chatbot 配合使用
17
+
18
+ <demo name="with_chatbot"></demo>
19
+
20
+ ### 配置上传/提交按钮
21
+
22
+ <demo name="config_buttons"></demo>
23
+
24
+ ### 允许用户录音或拍照
25
+
26
+ <demo name="upload_sources"></demo>
27
+
28
+ ## API 及参数列表
29
+
30
+ 以下 API 均为在原有 gradio Textbox 外的额外拓展参数。
31
+
32
+ ### value
33
+
34
+ 接口定义:
35
+
36
+ ```python
37
+ class MultimodalInputData(GradioModel):
38
+ files: List[Union[FileData, str]] = []
39
+ text: str
40
+ ```
41
+
42
+ ### props
43
+
44
+ | 属性 | 类型 | 默认值 | 描述 |
45
+ | ------------------- | -------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------- |
46
+ | sources | list\[Literal\['upload', 'microphone','webcam'\]\] | \['upload'\] | 上传文件的类型列表。 "upload"会提供上文文件按钮。 "microphone"支持用户录音输入。 "webcam"支持用户照相生成图片或视频 |
47
+ | webcam_props | dict | None | webcam 组件属性,目前支持传入mirror_webcam(bool)、include_audio(bool) |
48
+ | upload_button_props | dict | None | 上传文件按钮属性,同 gradio UploadButton |
49
+ | submit_button_props | dict | None | 提交按钮属性,同 gradio Button |
50
+ | file_preview_props | dict | None | 文件预览组件属性,目前支持传入 height (int) |
components/MultimodalInput/app.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from components.Docs import Docs
2
+
3
+ docs = Docs(__file__)
4
+
5
+ if __name__ == "__main__":
6
+ docs.render().queue().launch()
components/MultimodalInput/demos/basic.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+
6
+ def fn(value):
7
+ # value 包含 text 与 files
8
+ print(value.text, value.files)
9
+
10
+
11
+ with gr.Blocks() as demo:
12
+ input = mgr.MultimodalInput()
13
+ input.change(fn=fn, inputs=[input])
14
+
15
+ if __name__ == "__main__":
16
+ demo.queue().launch()
components/MultimodalInput/demos/config_buttons.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_gradio_components as mgr
4
+
5
+
6
+ def fn(value):
7
+ print(value.text, value.files)
8
+
9
+
10
+ with gr.Blocks() as demo:
11
+ input = mgr.MultimodalInput(upload_button_props=dict(variant="primary"),
12
+ submit_button_props=dict(visible=False))
13
+ input.change(fn=fn, inputs=[input])
14
+
15
+ if __name__ == "__main__":
16
+ demo.queue().launch()