Coloring commited on
Commit
176823e
1 Parent(s): ad0e7fc

feat: add Flow component

Browse files
app.py CHANGED
@@ -1,6 +1,7 @@
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
  from components.WaterfallGallery.app import docs as waterfall_gallery_docs
@@ -13,6 +14,7 @@ docs = [
13
  ["Markdown", markdown_docs],
14
  ["MultimodalInput", multimodel_input_docs],
15
  ["WaterfallGallery", waterfall_gallery_docs],
 
16
  ]
17
 
18
  with gr.Blocks() as demo:
 
1
  import gradio as gr
2
  from components.Chatbot.app import docs as chatbot_docs
3
  from components.Docs import Docs
4
+ from components.Flow.app import docs as flow_docs
5
  from components.Markdown.app import docs as markdown_docs
6
  from components.MultimodalInput.app import docs as multimodel_input_docs
7
  from components.WaterfallGallery.app import docs as waterfall_gallery_docs
 
14
  ["Markdown", markdown_docs],
15
  ["MultimodalInput", multimodel_input_docs],
16
  ["WaterfallGallery", waterfall_gallery_docs],
17
+ ["Flow", flow_docs],
18
  ]
19
 
20
  with gr.Blocks() as demo:
components/Chatbot/README-zh_CN.md CHANGED
@@ -89,7 +89,7 @@ class MultimodalMessage(GradioModel):
89
  # message 容器的 elem id
90
  elem_id: Optional[str] = None
91
  # message 容器的 elem classes
92
- elem_classes: Optional[list[str] | str] = None
93
  name: Optional[str] = None
94
  text: Optional[str] = None
95
  flushing: Optional[bool] = None
@@ -123,7 +123,7 @@ class ChatbotData(GradioRootModel):
123
  | llm_thinking_presets | list\[dict\] | \[\] | llm 思考链路解析预设,可以将 llm 调用工具的输出格式转为固定的前端展示格式,需要从modelscope_studio.Chatbot.llm_thinking_presets引入,目前支持:qwen |
124
  | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
125
 
126
- **CustomComponent 定义如下**
127
 
128
  ```python
129
  class CustomComponentDict(TypedDict):
 
89
  # message 容器的 elem id
90
  elem_id: Optional[str] = None
91
  # message 容器的 elem classes
92
+ elem_classes: Optional[List[str] | str] = None
93
  name: Optional[str] = None
94
  text: Optional[str] = None
95
  flushing: Optional[bool] = None
 
123
  | llm_thinking_presets | list\[dict\] | \[\] | llm 思考链路解析预设,可以将 llm 调用工具的输出格式转为固定的前端展示格式,需要从modelscope_studio.Chatbot.llm_thinking_presets引入,目前支持:qwen |
124
  | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
125
 
126
+ **CustomComponentDict 定义如下**
127
 
128
  ```python
129
  class CustomComponentDict(TypedDict):
components/Chatbot/README.md CHANGED
@@ -88,7 +88,7 @@ class MultimodalMessage(GradioModel):
88
  # elem id of message container
89
  elem_id: Optional[str] = None
90
  # elem classes of message container
91
- elem_classes: Optional[list[str] | str] = None
92
  name: Optional[str] = None
93
  text: Optional[str] = None
94
  flushing: Optional[bool] = None
 
88
  # elem id of message container
89
  elem_id: Optional[str] = None
90
  # elem classes of message container
91
+ elem_classes: Optional[List[str] | str] = None
92
  name: Optional[str] = None
93
  text: Optional[str] = None
94
  flushing: Optional[bool] = None
components/Chatbot/demos/chart.py CHANGED
@@ -35,6 +35,7 @@ Chart:
35
  with gr.Blocks() as demo:
36
  mgr.Chatbot(
37
  value=conversation,
 
38
  height=600,
39
  )
40
 
 
35
  with gr.Blocks() as demo:
36
  mgr.Chatbot(
37
  value=conversation,
38
+ flushing=False,
39
  height=600,
40
  )
41
 
components/Docs.py CHANGED
@@ -49,7 +49,7 @@ def get_demo_modules(file_path: str):
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:
@@ -93,23 +93,41 @@ class Docs:
93
  "r") as f:
94
  return f.read()
95
 
96
- def render_demo(self, demo_name, prefix='', suffix=''):
 
 
 
 
97
  content = self.read_file(f"./demos/{demo_name}.py")
98
  module = self.demo_modules[demo_name]
99
  with gr.Accordion("Show Demo", open=False):
100
- with gr.Row():
101
- with gr.Column():
102
- mgr.Markdown(f"""
103
- {prefix}
104
  ````python
105
  {content}
106
  ````
107
- {suffix}
108
- """,
109
- header_links=True,
110
- custom_components=custom_components)
 
 
 
 
 
 
 
 
111
  with gr.Column():
112
  module.demo.render()
 
 
 
 
 
 
 
113
 
114
  def render_markdown(self,
115
  markdown_file,
@@ -129,7 +147,8 @@ class Docs:
129
  elif item["type"] == "demo":
130
  self.render_demo(item["name"],
131
  prefix=item["prefix"],
132
- suffix=item["suffix"])
 
133
 
134
  def render(self, components_tabs=None):
135
 
 
49
  demos = [
50
  demo for demo in list_demos(
51
  os.path.join(os.path.dirname(file_path), "demos"))
52
+ if demo.endswith(".py") and not demo.startswith("__")
53
  ]
54
  demo_modules = {}
55
  for demo in demos:
 
93
  "r") as f:
94
  return f.read()
95
 
96
+ def render_demo(self,
97
+ demo_name,
98
+ code_position='left',
99
+ prefix='',
100
+ suffix=''):
101
  content = self.read_file(f"./demos/{demo_name}.py")
102
  module = self.demo_modules[demo_name]
103
  with gr.Accordion("Show Demo", open=False):
104
+
105
+ def render_code():
106
+ mgr.Markdown(f"""{prefix}
 
107
  ````python
108
  {content}
109
  ````
110
+ {suffix}""",
111
+ header_links=True,
112
+ custom_components=custom_components)
113
+
114
+ if code_position == 'top':
115
+ with gr.Row():
116
+ with gr.Column():
117
+ render_code()
118
+ with gr.Row():
119
+ if code_position == 'left':
120
+ with gr.Column():
121
+ render_code()
122
  with gr.Column():
123
  module.demo.render()
124
+ if code_position == 'right':
125
+ with gr.Column():
126
+ render_code()
127
+ if code_position == 'bottom':
128
+ with gr.Row():
129
+ with gr.Column():
130
+ render_code()
131
 
132
  def render_markdown(self,
133
  markdown_file,
 
147
  elif item["type"] == "demo":
148
  self.render_demo(item["name"],
149
  prefix=item["prefix"],
150
+ suffix=item["suffix"],
151
+ code_position=item["code_position"])
152
 
153
  def render(self, components_tabs=None):
154
 
components/Flow/README-zh_CN.md ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Flow
2
+
3
+ 基于 [reactflow](https://reactflow.dev/) 实现的 Flow 组件。
4
+
5
+ - 支持通过 schema 自定义渲染节点
6
+ - 支持自定义节点的渲染组件,并与 Python 事件交互
7
+
8
+ ## 如何使用
9
+
10
+ ### 定义 schema 节点 (重要)
11
+
12
+ 详见:<tab-link tab="define_schema">Define Schema</tab-link>
13
+
14
+ ### 基本使用
15
+
16
+ <demo name="basic" code-position="bottom"></demo>
17
+
18
+ ### 组件配置项
19
+
20
+ <demo name="component_options" code-position="bottom"></demo>
21
+
22
+ ### 自定义节点类型(高阶用法,需要了解前端知识)
23
+
24
+ <demo name="custom_node_type" code-position="bottom"></demo>
25
+
26
+ ## API 及参数列表
27
+
28
+ ### value
29
+
30
+ 接口定义:
31
+
32
+ ```python
33
+ class NodePosition(GradioModel):
34
+ x: Optional[int] = 0
35
+ y: Optional[int] = 0
36
+
37
+
38
+ class Node(GradioModel):
39
+ id: Optional[str] = None
40
+ name: str
41
+ title: Optional[str] = None
42
+ position: Optional[Union[NodePosition, dict]] = None
43
+ data: Optional[dict] = None
44
+
45
+ class EdgePort(GradioModel):
46
+ attr: Optional[str] = None
47
+ attrItemIndex: Optional[int] = None
48
+ handleIndex: Optional[int] = None
49
+
50
+ class Edge(GradioModel):
51
+ id: Optional[str] = None
52
+ source: str
53
+ target: str
54
+ sourcePort: Optional[Union[EdgePort, dict]] = None
55
+ targetPort: Optional[Union[EdgePort, dict]] = None
56
+
57
+
58
+ class FlowData(GradioModel):
59
+ nodes: Optional[List[Union[Node, dict]]] = []
60
+ edges: Optional[List[Union[Edge, dict]]] = []
61
+
62
+ ```
63
+
64
+ ### props
65
+
66
+ | 属性 | 类型 | 默认值 | 描述 |
67
+ | ------------------- | --------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68
+ | height | int \| str | 600 | Flow 组件高度。 |
69
+ | sync_on_data_change | bool | None | 是否仅在数据更改时同步 Python 值(例如:节点属性、节点计数、边缘计数、连接端口等,不包括节点位置)。 如果您想要更好的页面性能而不是完整数据同步,则应将其设置为 True。 |
70
+ | schema | FlowSchemaDict \| dict | None | 定义 Flow 组件的 nodes 与 edges。 |
71
+ | show_sidebar | bool | True | 是否展示侧 Flow 组件侧边栏。 |
72
+ | show_minimap | bool | True | 是否展示侧 Flow 组件小地图。 |
73
+ | show_controls | bool | True | 是否展示侧Flow 组件控制栏。 |
74
+ | background_props | BackgroundPropsDict \| dict CustomComponentDict 定义见下方 | None | 修改 Flow组件背景,详见 BackgroundPropsDict 类型。 |
75
+ | min_zoom | int | 0.1 | Flow 组件最小缩放倍率。 |
76
+ | max_zoom | int | 2 | Flow 组件最大缩放倍率。 |
77
+ | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户自定义节点类型,并通过 js 控制渲染样式与触发 python 事件。 |
78
+
79
+ **BackgroundPropsDict 定义如下**
80
+
81
+ ```python
82
+ class BackgroundPropsDict(TypedDict):
83
+ color: Optional[str]
84
+ className: Optional[str]
85
+ # The gap between patterns. Passing in a tuple allows you to control the x and y gap independently.
86
+ gap: Optional[Union[int, Tuple[int, int]]]
87
+ # The radius of each dot or the size of each rectangle if BackgroundVariant.Dots or BackgroundVariant.Cross is used. This defaults to 1 or 6 respectively, or ignored if BackgroundVariant.Lines is used.
88
+ size: Optional[int]
89
+ offset: Optional[int]
90
+ lineWidth: Optional[int]
91
+ variant: Optional[Literal['dots', 'lines', 'cross']]
92
+ ```
93
+
94
+ **CustomComponentDict 定义如下**
95
+
96
+ ```python
97
+ class CustomComponentDict(TypedDict):
98
+ props: Optional[List[str]]
99
+ template: Optional[str]
100
+ js: Optional[str]
101
+ ```
102
+
103
+ ### event listeners
104
+
105
+ | 事件 | 描述 |
106
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
107
+ | `mgr.Flow.change(fn, ···)` | 当 value 更新时触发,如果 sync_on_data_change 值为 True 时,此时 flow 的实际数据可能并不是实时的,建议监听 data_change 事件。 |
108
+ | `mgr.Flow.data_change(fn, ···)` | 当在数据更改时触发(例如:节点属性、节点计数、边缘计数、连接端口等,不包括节点位置) |
109
+ | `mgr.Flow.custom(fn, ···)` | 自定义标签触发事件时触发,EventData 为:<br/> - id:当前触发节点 id。<br/> - node:当前触发节点类型。 <br/> - attr:当前触发节点属性。<br/> - index:当前触发节点属性索引,当节点属性为 list 时有值。<br/> - value:自定义传入的值。 |
components/Flow/README.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Flow
2
+
3
+ A Flow component implemented based on [reactflow](https://reactflow.dev/).
4
+
5
+ - Supports customization of node rendering through a schema.
6
+ - Allows for custom node render components with interaction from Python.
7
+
8
+ ## How to Use
9
+
10
+ ### Defining Schema Nodes (Important)
11
+
12
+ See: <tab-link tab="define_schema">Define Schema</tab-link>
13
+
14
+ ### Basic Usage
15
+
16
+ <demo name="basic" code-position="bottom"></demo>
17
+
18
+ ### Component Options
19
+
20
+ <demo name="component_options" code-position="bottom"></demo>
21
+
22
+ ### Custom Node Types (Advanced usage, requires frontend knowledge)
23
+
24
+ <demo name="custom_node_type" code-position="bottom"></demo>
25
+
26
+ ## API and Parameter List
27
+
28
+ ### value
29
+
30
+ Interface definition:
31
+
32
+ ```python
33
+ class NodePosition(GradioModel):
34
+ x: Optional[int] = 0
35
+ y: Optional[int] = 0
36
+
37
+
38
+ class Node(GradioModel):
39
+ id: Optional[str] = None
40
+ name: str
41
+ title: Optional[str] = None
42
+ position: Optional[Union[NodePosition, dict]] = None
43
+ data: Optional[dict] = None
44
+
45
+ class EdgePort(GradioModel):
46
+ attr: Optional[str] = None
47
+ attrItemIndex: Optional[int] = None
48
+ handleIndex: Optional[int] = None
49
+
50
+ class Edge(GradioModel):
51
+ id: Optional[str] = None
52
+ source: str
53
+ target: str
54
+ sourcePort: Optional[Union[EdgePort, dict]] = None
55
+ targetPort: Optional[Union[EdgePort, dict]] = None
56
+
57
+
58
+ class FlowData(GradioModel):
59
+ nodes: Optional[List[Union[Node, dict]]] = []
60
+ edges: Optional[List[Union[Edge, dict]]] = []
61
+ ```
62
+
63
+ ### props
64
+
65
+ | Property | Type | Default | Description |
66
+ | ------------------- | --------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
67
+ | height | int \| str | 600 | Height of the Flow component. |
68
+ | sync_on_data_change | bool | None | Whether to sync the Python value only on data change (e.g., node attributes, node count, edge count, connection ports, not including node positions). If you want better page performance without full data sync, set this to True. |
69
+ | schema | FlowSchemaDict \| dict | None | Defines the nodes and edges of the Flow component. |
70
+ | show_sidebar | bool | True | Whether to display the sidebar in the Flow component. |
71
+ | show_minimap | bool | True | Whether to display the minimap in the Flow component. |
72
+ | show_controls | bool | True | Whether to display the controls bar in the Flow component. |
73
+ | background_props | BackgroundPropsDict \| dict BackgroundPropsDict definition below | None | Modify the background of the Flow component, see the BackgroundPropsDict type. |
74
+ | min_zoom | int | 0.1 | Minimum zoom level for the Flow component. |
75
+ | max_zoom | int | 2 | Maximum zoom level for the Flow component. |
76
+ | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict definition below | None | Supports user-defined custom tags and controls tag rendering styles and triggers Python events through js. |
77
+
78
+ **BackgroundPropsDict definition:**
79
+
80
+ ```python
81
+ class BackgroundPropsDict(TypedDict):
82
+ color: Optional[str]
83
+ className: Optional[str]
84
+ # The gap between patterns. Passing in a tuple allows you to control the x and y gap independently.
85
+ gap: Optional[Union[int, Tuple[int, int]]]
86
+ # The radius of each dot or the size of each rectangle if BackgroundVariant.Dots or BackgroundVariant.Cross is used. This defaults to 1 or 6 respectively, or ignored if BackgroundVariant.Lines is used.
87
+ size: Optional[int]
88
+ offset: Optional[int]
89
+ lineWidth: Optional[int]
90
+ variant: Optional[Literal['dots', 'lines', 'cross']]
91
+ ```
92
+
93
+ **CustomComponentDict definition:**
94
+
95
+ ```python
96
+ class CustomComponentDict(TypedDict):
97
+ props: Optional[List[str]]
98
+ template: Optional[str]
99
+ js: Optional[str]
100
+ ```
101
+
102
+ ### Event Listeners
103
+
104
+ | Event | Description |
105
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
106
+ | `mgr.Flow.change(fn, ...)` | Triggers when the `value` updates. If `sync_on_data_change` is True, the actual data at this point might not be up-to-date; consider listening to the `data_change` event instead. |
107
+ | `mgr.Flow.data_change(fn, ...)` | Triggers when there's a data change (e.g., node attributes, node count, edge count, connection ports), but not node positions. |
108
+ | `mgr.Flow.custom(fn, ...)` | Triggers when a custom label event occurs. The `EventData` includes:<br/> - `id`: ID of the currently triggered node.<br/> - `node`: Type of the currently triggered node.<br/> - `attr`: Attributes of the currently triggered node.<br/> - `index`: Index of the attribute if it's a list.<br/> - `value`: Custom passed-in value. |
components/Flow/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/Flow/define_schema-zh_CN.md ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Define Schema
2
+
3
+ 在使用 Flow 组件前,需要预先创建 Schema 定义 node 节点,schema 类型定义如下:
4
+
5
+ ```ts
6
+ export interface FlowSchema {
7
+ nodes: FlowNodeSchema[];
8
+ }
9
+
10
+ export interface FlowNodeSchema {
11
+ /**
12
+ * 作为节点的唯一标识。必填。
13
+ */
14
+ name: string;
15
+
16
+ /**
17
+ * 节点显示图标。
18
+ */
19
+ icon?: string;
20
+
21
+ /**
22
+ * 节点标题,如果没有提供则默认使用 name。
23
+ */
24
+ title?: string;
25
+
26
+ /**
27
+ * 节点的简短描述。
28
+ */
29
+ description?: string;
30
+
31
+ /**
32
+ * 节点宽度。
33
+ */
34
+ width?: number;
35
+
36
+ /**
37
+ * 节点高度。
38
+ */
39
+ height?: number;
40
+
41
+ /**
42
+ * 显示/隐藏工具栏(删除、复制、重命名等)。
43
+ * @default true
44
+ */
45
+ show_toolbar?: boolean;
46
+
47
+ /**
48
+ * 启用/禁止添加更多此类节点实例。
49
+ * @default true
50
+ */
51
+ addable?: boolean;
52
+
53
+ /**
54
+ * 启用/禁止删除现有此类节点实例。
55
+ * @default true
56
+ */
57
+ deletable?: boolean;
58
+
59
+ /**
60
+ * 可以同时存在的此类节点的最大数量。
61
+ */
62
+ max?: number;
63
+
64
+ /**
65
+ * 可以同时存在的此类节点的最小数量。
66
+ */
67
+ min?: number;
68
+
69
+ /**
70
+ * 节点连接端口的配置。
71
+ */
72
+ ports?: {
73
+ /**
74
+ * 节点作为连接的源端口。
75
+ * @default ['right']
76
+ */
77
+ source?: Position[];
78
+
79
+ /**
80
+ * 允许此节点 source 端口连接到的其他节点或属性。默认为所有节点和属性。
81
+ * @default []
82
+ */
83
+ sourceConnections?: PortConnection[];
84
+
85
+ /**
86
+ * 节点作为连接的目标端口。
87
+ * @default ['left']
88
+ */
89
+ target?: Position[];
90
+
91
+ /**
92
+ * 其他允许连接到此节点 target 端口的节点或属性。默认为所有节点和属性
93
+ * @default []
94
+ */
95
+ targetConnections?: PortConnection[];
96
+ };
97
+
98
+ /**
99
+ * 节点的属性配置。
100
+ */
101
+ attrs?: FlowNodeAttrSchema[];
102
+
103
+ /**
104
+ * 创建新实例时节点属性的初始值。
105
+ */
106
+ template?: {
107
+ /**
108
+ * 在`attrs`字段中与其名称相对应的属性值,例如 { "a": 1, "b": 2 }。
109
+ */
110
+ attrs?: Attrs;
111
+ };
112
+ }
113
+
114
+ export interface FlowNodeAttrSchema {
115
+ /**
116
+ * 唯一的属性名称,在 node data 中用作 key。必填。
117
+ */
118
+ name: string;
119
+
120
+ /**
121
+ * 属性标题,如果没有提供则默认使用 name。
122
+ */
123
+ title?: string;
124
+
125
+ /**
126
+ * 属性的简短描述
127
+ */
128
+ description?: string;
129
+
130
+ /**
131
+ * 禁用用户编辑属性值。默认情况下,属性是可编辑的。
132
+ * @default false
133
+ */
134
+ disabled?: boolean;
135
+
136
+ /**
137
+ * 属性输入类型。可以是内置的 Ant Design 组件或自定义组件之一。默认为'input'。
138
+ * @default 'input'
139
+ */
140
+ type?:
141
+ | 'input'
142
+ | 'textarea'
143
+ | 'radio'
144
+ | 'checkbox'
145
+ | 'number'
146
+ | 'select'
147
+ | 'switch'
148
+ | 'upload'
149
+ // 自定义
150
+ | (string & {});
151
+
152
+ /**
153
+ * 针对所选组件类型的特定配置选项,支持 Ant Design 组件({@link https://ant.design/components/overview/})或自定义组件的属性。
154
+ */
155
+ props?: Record<string, any>;
156
+
157
+ /**
158
+ * 节点属性连接端口的配置。
159
+ */
160
+ ports?: {
161
+ /**
162
+ * 节点属性作为连接的源端口。
163
+ * @default []
164
+ */
165
+ source?: Position[];
166
+
167
+ /**
168
+ * 允许此节点属性 source 端口连接到的其他节点或属性。
169
+ * @default []
170
+ */
171
+ sourceConnections?: PortConnection[];
172
+
173
+ /**
174
+ * 节点属性作为连接的目标端口。
175
+ * @default []
176
+ */
177
+ target?: Position[];
178
+
179
+ /**
180
+ * 其他允许连接到此节点属性 target 端口的节点或属性。
181
+ * @default []
182
+ */
183
+ targetConnections?: PortConnection[];
184
+ };
185
+
186
+ /**
187
+ * 表示该属性是否为列表值。
188
+ * @default false
189
+ */
190
+ list?:
191
+ | boolean
192
+ | {
193
+ /**
194
+ * 列表中每个 item 的端口配置。
195
+ */
196
+ ports?: {
197
+ /**
198
+ * 列表 item 作为连接的源端口。
199
+ * @default []
200
+ */
201
+ source?: Position[];
202
+
203
+ /**
204
+ * 允许此列表 item source 端口连接到的其他节点或属性。
205
+ * @default []
206
+ */
207
+ sourceConnections?: PortConnection[];
208
+
209
+ /**
210
+ * 列表 item 作为连接的目标端口。
211
+ */
212
+ target?: Position[];
213
+
214
+ /**
215
+ * 其他允许连接到此列表 item target 端口的节点或属性。
216
+ */
217
+ targetConnections?: PortConnection[];
218
+ };
219
+
220
+ /**
221
+ * 列表中的最小 item 数量。
222
+ */
223
+ min?: number;
224
+
225
+ /**
226
+ * 列表中的最大 item 数量。
227
+ */
228
+ max?: number;
229
+ };
230
+
231
+ /**
232
+ * 启用/禁用手风琴 UI。
233
+ * @default true
234
+ */
235
+ accordion?: boolean;
236
+
237
+ /**
238
+ * 指定该属性值是否为必填项。默认情况为非必填项。
239
+ * @default false
240
+ */
241
+ required?:
242
+ | boolean
243
+ | {
244
+ message?: string;
245
+ };
246
+
247
+ /**
248
+ * 使用 JSON schema 验证属性值。
249
+ */
250
+ json_schema_validator?: Record<string, any>;
251
+ }
252
+ ```
253
+
254
+ 你可以通过 json 文件(推荐)或直接在 Python 端通过导出类型定义:
255
+
256
+ - 通过 json 定义:
257
+
258
+ ```json
259
+ <file src="./schema/agents_schema.json"></file>
260
+ ```
261
+
262
+ - 通过 Python 定义:
263
+
264
+ ```python
265
+ <file src="./schema/agents_schema.py"></file>
266
+ ```
components/Flow/define_schema.md ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Define Schema
2
+
3
+ Before using the Flow component, need to create a Schema definition node in advance. The schema type is defined as follows:
4
+
5
+ ```ts
6
+ export interface FlowSchema {
7
+ nodes: FlowNodeSchema[];
8
+ }
9
+
10
+ export interface FlowNodeSchema {
11
+ /**
12
+ * As a unique identifier for the node. Mandatory.
13
+ */
14
+ name: string;
15
+
16
+ /**
17
+ * Display icon for the node.
18
+ */
19
+ icon?: string;
20
+
21
+ /**
22
+ * Display title for the node, defaults to the node name if not provided.
23
+ */
24
+ title?: string;
25
+
26
+ /**
27
+ * A short description of the node's purpose.
28
+ */
29
+ description?: string;
30
+
31
+ /**
32
+ * Width of the node.
33
+ */
34
+ width?: number;
35
+
36
+ /**
37
+ * Height of the node.
38
+ */
39
+ height?: number;
40
+
41
+ /**
42
+ * Shows/hides the toolbar (delete, copy, rename, etc.).
43
+ * @default true
44
+ */
45
+ show_toolbar?: boolean;
46
+
47
+ /**
48
+ * Enables/disables adding more instances of this node.
49
+ * @default true
50
+ */
51
+ addable?: boolean;
52
+
53
+ /**
54
+ * Enables/disables deleting existing instances of this node.
55
+ * @default true
56
+ */
57
+ deletable?: boolean;
58
+
59
+ /**
60
+ * Maximum number of this node type that can exist simultaneously.
61
+ */
62
+ max?: number;
63
+
64
+ /**
65
+ * Minimum number of this node type that must exist simultaneously.
66
+ */
67
+ min?: number;
68
+
69
+ /**
70
+ * Configurations for the node's connection ports.
71
+ */
72
+ ports?: {
73
+ /**
74
+ * Source ports for the node as a connection.
75
+ * @default ['right']
76
+ */
77
+ source?: Position[];
78
+
79
+ /**
80
+ * Allowed the source ports of this node to connect to other nodes or attributes. Defaults to all nodes and attributes
81
+ */
82
+ sourceConnections?: PortConnection[];
83
+
84
+ /**
85
+ * Target ports for the node as a connection
86
+ * @default ['left']
87
+ */
88
+ target?: Position[];
89
+
90
+ /**
91
+ * Allowed other nodes or attributes allowed to connect to the target ports of this node. Defaults to all nodes and attributes
92
+ *
93
+ */
94
+ targetConnections?: PortConnection[];
95
+ };
96
+
97
+ /**
98
+ * Configuration of the node's attributes.
99
+ */
100
+ attrs?: FlowNodeAttrSchema[];
101
+
102
+ /**
103
+ * Initial values for the node's attributes when creating a new instance.
104
+ */
105
+ template?: {
106
+ /**
107
+ * Attribute values corresponding to their names in the `attrs` field, e.g., `{ "a": 1, "b": 2 }`.
108
+ */
109
+ attrs?: Attrs;
110
+ };
111
+ }
112
+
113
+ export interface FlowNodeAttrSchema {
114
+ /**
115
+ * Unique attribute name used as a key in the node data. Mandatory.
116
+ */
117
+ name: string;
118
+
119
+ /**
120
+ * Display title for the attribute, defaults to the attribute name if not provided.
121
+ */
122
+ title?: string;
123
+
124
+ /**
125
+ * A brief explanation about the attribute purpose.
126
+ */
127
+ description?: string;
128
+
129
+ /**
130
+ * Disables user editing of the attribute value. By default, attributes are editable.
131
+ * @default false
132
+ */
133
+ disabled?: boolean;
134
+
135
+ /**
136
+ * Attribute input type. Can be one of the built-in Ant Design components or a custom component. Defaults to 'input'.
137
+ * @default 'input'
138
+ */
139
+ type?:
140
+ | 'input'
141
+ | 'textarea'
142
+ | 'radio'
143
+ | 'checkbox'
144
+ | 'number'
145
+ | 'select'
146
+ | 'switch'
147
+ | 'upload'
148
+ // custom
149
+ | (string & {});
150
+
151
+ /**
152
+ * Configuration options specific to the chosen component type, supporting Ant Design ({@link https://ant.design/components/overview/}) or custom component properties.
153
+ */
154
+ props?: Record<string, any>;
155
+
156
+ /**
157
+ * Configurations for the node attribute ports.
158
+ */
159
+ ports?: {
160
+ /**
161
+ * Source ports for the attribute as a connection.
162
+ * @default []
163
+ */
164
+ source?: Position[];
165
+
166
+ /**
167
+ * Allowed the source ports of this attribute to connect to other nodes or attributes. Defaults to all nodes and attributes
168
+ */
169
+ sourceConnections?: PortConnection[];
170
+
171
+ /**
172
+ * Target ports for the attribute as a connection
173
+ * @default []
174
+ */
175
+ target?: Position[];
176
+
177
+ /**
178
+ * Allowed other nodes or attributes allowed to connect to the target ports of this attribute. Defaults to all nodes and attributes
179
+ */
180
+ targetConnections?: PortConnection[];
181
+ };
182
+
183
+ /**
184
+ * Indicates whether the attribute is a list.
185
+ * @default false
186
+ */
187
+ list?:
188
+ | boolean
189
+ | {
190
+ /**
191
+ * Port configurations for each item in the list.
192
+ */
193
+ ports?: {
194
+ /**
195
+ * Source ports for the list item as a connection.
196
+ * @default []
197
+ */
198
+ source?: Position[];
199
+
200
+ /**
201
+ * Allowed the source ports of this list item to connect to other nodes or attributes. Defaults to all nodes and attributes
202
+ */
203
+ sourceConnections?: PortConnection[];
204
+
205
+ /**
206
+ * Target ports for the list item as a connection
207
+ */
208
+ target?: Position[];
209
+
210
+ /**
211
+ * Allowed other nodes or attributes allowed to connect to the target ports of this list item. Defaults to all nodes and attributes
212
+ */
213
+ targetConnections?: PortConnection[];
214
+ };
215
+
216
+ /**
217
+ * Minimum number of items in the list.
218
+ */
219
+ min?: number;
220
+
221
+ /**
222
+ * Maximum number of items in the list.
223
+ */
224
+ max?: number;
225
+ };
226
+
227
+ /**
228
+ * Enable/disable accordion UI.
229
+ * @default true
230
+ */
231
+ accordion?: boolean;
232
+
233
+ /**
234
+ * Specifies if the attribute value is mandatory. By default, attributes are optional.
235
+ * @default false
236
+ */
237
+ required?:
238
+ | boolean
239
+ | {
240
+ message?: string;
241
+ };
242
+
243
+ /**
244
+ * Validates attribute values using JSON schema.
245
+ */
246
+ json_schema_validator?: Record<string, any>;
247
+ }
248
+ ```
249
+
250
+ You can define the schema by a json file (recommended) or directly on the Python by the exported types:
251
+
252
+ - Defined by json:
253
+
254
+ ```json
255
+ <file src="./schema/agents_schema.json"></file>
256
+ ```
257
+
258
+ - Defined by Python:
259
+
260
+ ```python
261
+ <file src="./schema/agents_schema.py"></file>
262
+ ```
components/Flow/demos/basic.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_studio as mgr
7
+ from modelscope_studio.components.Flow import Edge, Node
8
+
9
+ with open((os.path.join(os.path.dirname(__file__),
10
+ "../schema/agents_schema.json"))) as f:
11
+ schema = json.load(f)
12
+
13
+ # define the initial value of the flow
14
+ data = {
15
+ "nodes": [
16
+ Node(id="start-node", name="start", position=dict(x=0, y=0)),
17
+ Node(name="agent",
18
+ position=dict(x=200, y=0),
19
+ data=dict(condition=['']))
20
+ ],
21
+ "edges": [Edge(source='start-node', target="initial-agent-node")],
22
+ }
23
+
24
+
25
+ def on_data_change(_flow):
26
+ print(_flow)
27
+
28
+
29
+ with gr.Blocks() as demo:
30
+ flow = mgr.Flow(value=data, schema=schema, sync_on_data_change=True)
31
+ flow.data_change(fn=on_data_change, inputs=[flow])
32
+
33
+ if __name__ == "__main__":
34
+ demo.queue().launch()
components/Flow/demos/component_options.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import gradio as gr
5
+
6
+ import modelscope_studio as mgr
7
+ from modelscope_studio.components.Flow import BackgroundPropsDict, Edge, Node
8
+
9
+ with open((os.path.join(os.path.dirname(__file__),
10
+ "../schema/agents_schema.json"))) as f:
11
+ schema = json.load(f)
12
+
13
+ # define the initial value of the flow
14
+ data = {
15
+ "nodes": [
16
+ Node(id="start-node", name="start", position=dict(x=0, y=0)),
17
+ Node(name="agent",
18
+ position=dict(x=200, y=0),
19
+ data=dict(condition=['']))
20
+ ],
21
+ "edges": [Edge(source='start-node', target="initial-agent-node")],
22
+ }
23
+
24
+
25
+ def on_data_change(_flow):
26
+ print(_flow)
27
+
28
+
29
+ flow_props = ["show_sidebar", "show_minimap", "show_controls"]
30
+
31
+
32
+ def on_change(_flow_config, _bgc_variant, _bgc_color, _bgc_gap, _bgc_size,
33
+ _bgc_offset, _bgc_line_width):
34
+ new_props = {}
35
+ new_background_props = {
36
+ "variant": _bgc_variant,
37
+ "color": _bgc_color,
38
+ "gap": _bgc_gap,
39
+ "size": _bgc_size,
40
+ "offset": _bgc_offset,
41
+ 'lineWidth': _bgc_line_width
42
+ }
43
+ for choice in flow_props:
44
+ if choice in _flow_config:
45
+ new_props[choice] = True
46
+ else:
47
+ new_props[choice] = False
48
+ return gr.update(**new_props, background_props=new_background_props)
49
+
50
+
51
+ with gr.Blocks() as demo:
52
+ with gr.Accordion(label="Flow Options"):
53
+ flow_config = gr.CheckboxGroup(
54
+ container=False,
55
+ value=["show_sidebar", "show_minimap", "show_controls"],
56
+ choices=flow_props)
57
+ with gr.Accordion(label="Background Props"):
58
+ with gr.Row():
59
+ with gr.Column():
60
+ bgc_variant = gr.Radio(choices=["dots", "lines", "cross"],
61
+ label="variant",
62
+ value="dots")
63
+ with gr.Column():
64
+ bgc_color = gr.ColorPicker(label="color", value="")
65
+ with gr.Column():
66
+ bgc_gap = gr.Slider(label="gap", value=28)
67
+ with gr.Column():
68
+ bgc_size = gr.Slider(label="size",
69
+ value=1,
70
+ maximum=10,
71
+ step=1)
72
+ with gr.Column():
73
+ bgc_offset = gr.Slider(label="offset",
74
+ value=1,
75
+ step=1,
76
+ maximum=10)
77
+ with gr.Column():
78
+ bgc_line_width = gr.Slider(label="lineWidth",
79
+ value=1,
80
+ step=1,
81
+ maximum=10)
82
+
83
+ flow = mgr.Flow(value=data,
84
+ schema=schema,
85
+ show_controls=True,
86
+ show_minimap=True,
87
+ show_sidebar=True,
88
+ sync_on_data_change=True,
89
+ background_props=BackgroundPropsDict(variant='dots'))
90
+ gr.on(triggers=[
91
+ flow_config.change, bgc_variant.change, bgc_color.change,
92
+ bgc_gap.change, bgc_size.change, bgc_offset.change,
93
+ bgc_line_width.change
94
+ ],
95
+ fn=on_change,
96
+ inputs=[
97
+ flow_config, bgc_variant, bgc_color, bgc_gap, bgc_size,
98
+ bgc_offset, bgc_line_width
99
+ ],
100
+ outputs=[flow])
101
+ flow.data_change(fn=on_data_change, inputs=[flow])
102
+
103
+ if __name__ == "__main__":
104
+ demo.queue().launch()
components/Flow/demos/custom_node_type.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import modelscope_studio as mgr
4
+ from modelscope_studio.components.Flow import (FlowSchemaDict, Node,
5
+ NodeSchemaAttributeDict,
6
+ NodeSchemaDict)
7
+
8
+
9
+ def on_data_change(_flow):
10
+ print(_flow)
11
+
12
+
13
+ def on_custom(data: gr.EventData):
14
+ print(data._data)
15
+
16
+
17
+ custom_components = {
18
+ "my-input": {
19
+ "js":
20
+ """
21
+ (props, cc, { el, theme, onMount, onUpdate }) => {
22
+ onMount(() => {
23
+ el.innerHTML = `<input />`
24
+ const input = el.querySelector('input')
25
+ input.style.color = theme === 'dark' ? 'white' : 'black'
26
+ input.style.backgroundColor = theme === 'dark' ? 'black' : 'white'
27
+ input.addEventListener('change', (e) => {
28
+ cc.dispatch(e.target.value)
29
+ })
30
+ })
31
+ // props update
32
+ onUpdate(
33
+ () => {
34
+ const input = el.querySelector('input')
35
+ input.setAttribute('value', props.value || '')
36
+ },
37
+ // By default, the callback will not be called when the component is being mounted. Set `callAfterMount` to true to enable it.
38
+ { callAfterMount: true }
39
+ )
40
+ }
41
+ """
42
+ }
43
+ }
44
+
45
+ schema = FlowSchemaDict(nodes=[
46
+ NodeSchemaDict(name="my-input-node",
47
+ title="MyInputNode",
48
+ attrs=[NodeSchemaAttributeDict(name="a", type="my-input")])
49
+ ])
50
+
51
+ data = {
52
+ "nodes": [
53
+ Node(name="my-input-node",
54
+ position=dict(x=0, y=0),
55
+ data=dict(a='Hello'))
56
+ ]
57
+ }
58
+
59
+ with gr.Blocks() as demo:
60
+ flow = mgr.Flow(value=data,
61
+ schema=schema,
62
+ custom_components=custom_components,
63
+ sync_on_data_change=True)
64
+ flow.data_change(fn=on_data_change, inputs=[flow])
65
+ # called when custom component dispatch event
66
+ flow.custom(fn=on_custom)
67
+
68
+ if __name__ == "__main__":
69
+ demo.queue().launch()
components/Flow/schema/agents_schema.json ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/modelscope/modelscope-studio/main/frontend/Flow/schema.json",
3
+ "nodes": [
4
+ {
5
+ "max": 1,
6
+ "min": 1,
7
+ "addable": false,
8
+ "show_toolbar": false,
9
+ "name": "start",
10
+ "title": "Start",
11
+ "ports": {
12
+ "source": ["right"],
13
+ "target": []
14
+ }
15
+ },
16
+ {
17
+ "icon": "https://img.alicdn.com/imgextra/i4/O1CN01fvt4it25rEZU4Gjso_!!6000000007579-2-tps-128-128.png",
18
+ "name": "agent",
19
+ "title": "Agent Node",
20
+ "description": "Agent Flow Node",
21
+ "ports": {
22
+ "target": ["left"],
23
+ "source": []
24
+ },
25
+ "attrs": [
26
+ {
27
+ "name": "prompt",
28
+ "title": "Agent Prompt",
29
+ "type": "textarea",
30
+ "required": {
31
+ "message": "Agent Prompt is required"
32
+ }
33
+ },
34
+ {
35
+ "name": "tool",
36
+ "title": "Tools",
37
+ "type": "select",
38
+ "props": {
39
+ "mode": "multiple",
40
+ "options": [
41
+ { "label": "Wanx Image Generation", "value": "image_gen" },
42
+ { "label": "Code Interpreter", "value": "code_interpreter" },
43
+ { "label": "Web Browsing", "value": "web_browser" }
44
+ ]
45
+ }
46
+ },
47
+ {
48
+ "name": "condition",
49
+ "title": "Jump Condition",
50
+ "list": {
51
+ "min": 1,
52
+ "ports": {
53
+ "source": ["right"]
54
+ }
55
+ },
56
+ "accordion": false
57
+ }
58
+ ],
59
+ "template": {
60
+ "attrs": {
61
+ "condition": [""]
62
+ }
63
+ }
64
+ }
65
+ ]
66
+ }
components/Flow/schema/agents_schema.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from modelscope_studio.components.Flow import (
4
+ FlowSchemaDict, NodeSchemaAttributeDict, NodeSchemaAttributeListDict,
5
+ NodeSchemaAttributeRequiredDict, NodeSchemaDict, NodeSchemaPortsDict,
6
+ NodeSchemaTemplateDict)
7
+
8
+
9
+ def resolve_assets(relative_path):
10
+ return os.path.join(os.path.dirname(__file__), "../../resources",
11
+ relative_path)
12
+
13
+
14
+ schema = FlowSchemaDict(nodes=[
15
+ NodeSchemaDict(max=1,
16
+ min=1,
17
+ addable=False,
18
+ show_toolbar=False,
19
+ name="start",
20
+ title="Start",
21
+ ports=NodeSchemaPortsDict(source=['right'], target=[])),
22
+ NodeSchemaDict(
23
+ icon=resolve_assets('./bot.jpeg'),
24
+ name="agent",
25
+ title="Agent Node",
26
+ description="Agent Flow Node",
27
+ ports=NodeSchemaPortsDict(source=[], target=['left']),
28
+ attrs=[
29
+ NodeSchemaAttributeDict(name="prompt",
30
+ title="Agent Prompt",
31
+ type='textarea',
32
+ required=NodeSchemaAttributeRequiredDict(
33
+ message="Agent Prompt is required")),
34
+ NodeSchemaAttributeDict(name="tool",
35
+ title="Tools",
36
+ type="select",
37
+ props={
38
+ "mode":
39
+ "multiple",
40
+ "options": [{
41
+ "label": "Wanx Image Generation",
42
+ "value": "image_gen"
43
+ }, {
44
+ "label": "Code Interpreter",
45
+ "value": "code_interpreter"
46
+ }, {
47
+ "label": "Web Browsing",
48
+ "value": "web_browser"
49
+ }]
50
+ }),
51
+ NodeSchemaAttributeDict(
52
+ name="condition",
53
+ title="Jump Condition",
54
+ list=NodeSchemaAttributeListDict(
55
+ min=1, ports=NodeSchemaPortsDict(source=['right'])),
56
+ accordion=False)
57
+ ],
58
+ template=NodeSchemaTemplateDict(attrs=dict(condition=[''])))
59
+ ])
components/Markdown/README-zh_CN.md CHANGED
@@ -55,7 +55,7 @@ custom_select.js
55
  </demo-suffix>
56
  </demo>
57
 
58
- #### 与 Python 侧交互
59
 
60
  在 js 中可以使用`cc.dispatch`触发 Python 侧监听的`custom`事件,以前面的custom_select.js为例,我们在前端调用了`cc.dispatch(options[i])`,则会向 Python 侧同时发送通知。
61
 
@@ -75,7 +75,7 @@ custom_select.js
75
  | preview | bool | True | 是否开启图片预览功能 |
76
  | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
77
 
78
- **CustomComponent 定义如下**
79
 
80
  ```python
81
  class CustomComponentDict(TypedDict):
 
55
  </demo-suffix>
56
  </demo>
57
 
58
+ #### 与 Python 交互
59
 
60
  在 js 中可以使用`cc.dispatch`触发 Python 侧监听的`custom`事件,以前面的custom_select.js为例,我们在前端调用了`cc.dispatch(options[i])`,则会向 Python 侧同时发送通知。
61
 
 
75
  | preview | bool | True | 是否开启图片预览功能 |
76
  | custom_components | dict\[str, CustomComponentDict\] CustomComponentDict 定义见下方 | None | 支持用户定义自定义标签,并通过 js 控制标签渲染样式与触发 python 事件。 |
77
 
78
+ **CustomComponentDict 定义如下**
79
 
80
  ```python
81
  class CustomComponentDict(TypedDict):
components/Markdown/README.md CHANGED
@@ -50,7 +50,7 @@ custom_select.js
50
  </demo-suffix>
51
  </demo>
52
 
53
- #### Interaction with Python Side
54
 
55
  In js, you can use `cc.dispatch` to trigger the `custom` event listened to on the Python side. Taking the previous custom_select.js as an example, when we call `cc.dispatch(options[i])` on the frontend, a notification will be sent to the Python side simultaneously.
56
  <demo name="custom-tag4"></demo>
@@ -66,8 +66,8 @@ The following APIs are additional extended parameters beyond the original gradio
66
  | enable_base64 | bool | False | Whether to support rendering content as base64, since rendering base64 is unsafe, the default is False. |
67
  | preview | bool | True | Whether to enable image preview functionality. |
68
  | enable_latex | bool | True | Whether to enable LaTeX rendering. |
69
- | latex_single_dollar_delimiter | bool | True | Whether to enable single dollar delimiter `$` for LaTeX rendering. |
70
- | custom_components | dict[str, CustomComponentDict] CustomComponentDict definition below | None | Supports user-defined custom tags and controls tag rendering styles and triggers Python events through js. |
71
  | |
72
 
73
  **CustomComponent definition is as follows:**
 
50
  </demo-suffix>
51
  </demo>
52
 
53
+ #### Interaction with Python
54
 
55
  In js, you can use `cc.dispatch` to trigger the `custom` event listened to on the Python side. Taking the previous custom_select.js as an example, when we call `cc.dispatch(options[i])` on the frontend, a notification will be sent to the Python side simultaneously.
56
  <demo name="custom-tag4"></demo>
 
66
  | enable_base64 | bool | False | Whether to support rendering content as base64, since rendering base64 is unsafe, the default is False. |
67
  | preview | bool | True | Whether to enable image preview functionality. |
68
  | enable_latex | bool | True | Whether to enable LaTeX rendering. |
69
+ | latex_single_dollar_delimiter | bool | True | Whe ther to enable single dollar delimiter `$` for LaTeX rendering. |
70
+ | custom_components | Dict[str, CustomComponentDict] CustomComponentDict definition below | None | Supports user-defined custom tags and controls tag rendering styles and triggers Python events through js. |
71
  | |
72
 
73
  **CustomComponent definition is as follows:**
components/Markdown/demos/custom-tag2.py CHANGED
@@ -15,12 +15,29 @@ custom tag:<custom-tag value="aaa"></custom-tag>
15
  # The `js` property should be a string containing a JavaScript Function.
16
  "js":
17
  """
18
- (props, cc, { el, onMount }) => {
19
- // `onMount` will be called after the template rendered
20
  onMount(() => {
21
  // `el` is the container element
22
  console.log(el)
 
 
 
 
 
 
 
 
 
 
23
  })
 
 
 
 
 
 
 
24
  console.log(props.children) // By default, `props` will be passed a property named `children`, which can get the content in the tag, such as 'xx' in '<tag>xx</tag>'.
25
 
26
  // The return value will be merged with `props` and passed to the template.
 
15
  # The `js` property should be a string containing a JavaScript Function.
16
  "js":
17
  """
18
+ (props, cc, { el, onMount, onUpdate }) => {
19
+ // `onMount` will be called after the template first rendered
20
  onMount(() => {
21
  // `el` is the container element
22
  console.log(el)
23
+
24
+ // the return function will be called when the component is being unmounted
25
+ return () => {
26
+ console.log('unmount')
27
+ }
28
+ })
29
+
30
+ // `onUpdate` will be called when the props changed
31
+ onUpdate(() => {
32
+ console.log(props)
33
  })
34
+ onUpdate(
35
+ () => {
36
+ console.log(props, 'after mount')
37
+ },
38
+ // By default, the callback will not be called when the component is being mounted. Set `callAfterMount` to true to enable it.
39
+ { callAfterMount: true }
40
+ )
41
  console.log(props.children) // By default, `props` will be passed a property named `children`, which can get the content in the tag, such as 'xx' in '<tag>xx</tag>'.
42
 
43
  // The return value will be merged with `props` and passed to the template.
components/MultimodalInput/README.md CHANGED
@@ -43,7 +43,7 @@ class MultimodalInputData(GradioModel):
43
 
44
  | Attribute | Type | Default Value | Description |
45
  | ------------------- | ----------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
- | sources | list[Literal['upload', 'microphone', 'webcam']] | ['upload'] | A list of types for uploading files. "upload" provides an upload file button. "microphone" supports user audio input. "webcam" supports user photography to generate images or videos. |
47
  | webcam_props | dict | None | webcam component properties, currently supports passing mirror_webcam(bool), include_audio(bool) |
48
  | upload_button_props | dict | None | Upload file button properties, same as gradio UploadButton |
49
  | submit_button_props | dict | None | Submit button properties, same as gradio Button |
 
43
 
44
  | Attribute | Type | Default Value | Description |
45
  | ------------------- | ----------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | sources | List[Literal['upload', 'microphone', 'webcam']] | ['upload'] | A list of types for uploading files. "upload" provides an upload file button. "microphone" supports user audio input. "webcam" supports user photography to generate images or videos. |
47
  | webcam_props | dict | None | webcam component properties, currently supports passing mirror_webcam(bool), include_audio(bool) |
48
  | upload_button_props | dict | None | Upload file button properties, same as gradio UploadButton |
49
  | submit_button_props | dict | None | Submit button properties, same as gradio Button |
components/parse_markdown.py CHANGED
@@ -42,10 +42,16 @@ class MarkdownParser(HTMLParser):
42
  return
43
  if tag == "demo":
44
  self.value.append({
45
- "type": "demo",
46
- "name": dict(attrs)["name"],
47
- "prefix": "",
48
- "suffix": ""
 
 
 
 
 
 
49
  })
50
  elif tag == "file":
51
  content = self.read_file(dict(attrs)["src"])
 
42
  return
43
  if tag == "demo":
44
  self.value.append({
45
+ "type":
46
+ "demo",
47
+ "code_position":
48
+ dict(attrs).get("code-position", 'left'),
49
+ "name":
50
+ dict(attrs)["name"],
51
+ "prefix":
52
+ "",
53
+ "suffix":
54
+ ""
55
  })
56
  elif tag == "file":
57
  content = self.read_file(dict(attrs)["src"])
components/resources/custom_components/custom_select.js CHANGED
@@ -1,4 +1,4 @@
1
- (props, cc, { el, onMount }) => {
2
  const options = JSON.parse(props.options);
3
  el.innerHTML = `
4
  ${options
@@ -9,18 +9,21 @@
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
- // Use cc.dispatch to trigger events.
22
- cc.dispatch(options[i]);
23
  });
24
- });
25
- });
 
26
  };
 
1
+ (props, cc, { el, onUpdate }) => {
2
  const options = JSON.parse(props.options);
3
  el.innerHTML = `
4
  ${options
 
9
  })
10
  .join('')}
11
  `;
12
+ onUpdate(
13
+ () => {
14
+ const inputs = Array.from(el.getElementsByTagName('input'));
15
+ Array.from(el.getElementsByTagName('label')).forEach((label, i) => {
16
+ label.addEventListener('click', () => {
17
+ inputs.forEach((input) => {
18
+ input.checked = false;
19
+ });
20
+ const input = label.getElementsByTagName('input')[0];
21
+ input.checked = true;
22
+ // Use cc.dispatch to trigger events.
23
+ cc.dispatch(options[i]);
24
  });
 
 
 
 
25
  });
26
+ },
27
+ { callAfterMount: true }
28
+ );
29
  };
modelscope_studio-0.1.4-py3-none-any.whl → modelscope_studio-0.2.0-py3-none-any.whl RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:918f05ea07a5ceffe1b6e759f288c214345f4f61b12b4315d9d61544b7196a4a
3
- size 7456942
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:26497677e515ca086a7da0719cd3268516f842bab0833ebb00cd33a558b6a1bb
3
+ size 11001355
requirements.txt CHANGED
@@ -1,2 +1,2 @@
1
  modelscope_studio
2
- modelscope_studio-0.1.4-py3-none-any.whl
 
1
  modelscope_studio
2
+ modelscope_studio-0.2.0-py3-none-any.whl
src/pyproject.toml CHANGED
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
 
9
  [project]
10
  name = "modelscope_studio"
11
- version = "0.1.4"
12
  description = "A set of extension component, inluding components for conversational input and display in multimodal scenarios, as well as more components for vertical scenarios."
13
  readme = "README.md"
14
  license = "Apache-2.0"
@@ -45,10 +45,13 @@ artifacts = [
45
  "backend/modelscope_studio/components/Chatbot/templates",
46
  "backend/modelscope_studio/components/MultimodalInput/templates",
47
  "backend/modelscope_studio/components/Markdown/templates",
 
48
  ]
49
 
50
  [tool.hatch.build.targets.sdist]
 
51
  include = ["/backend/modelscope_studio"]
52
 
53
  [tool.hatch.build.targets.wheel]
54
  packages = ["/backend/modelscope_studio"]
 
 
8
 
9
  [project]
10
  name = "modelscope_studio"
11
+ version = "0.1.7"
12
  description = "A set of extension component, inluding components for conversational input and display in multimodal scenarios, as well as more components for vertical scenarios."
13
  readme = "README.md"
14
  license = "Apache-2.0"
 
45
  "backend/modelscope_studio/components/Chatbot/templates",
46
  "backend/modelscope_studio/components/MultimodalInput/templates",
47
  "backend/modelscope_studio/components/Markdown/templates",
48
+ "backend/modelscope_studio/components/WaterfallGallery/templates",
49
  ]
50
 
51
  [tool.hatch.build.targets.sdist]
52
+ exclude = ["__pycache__"]
53
  include = ["/backend/modelscope_studio"]
54
 
55
  [tool.hatch.build.targets.wheel]
56
  packages = ["/backend/modelscope_studio"]
57
+ exclude = ["__pycache__"]