Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
app.py ββββββββββββββββββββββββββββββββββββββββββββββββ
|
3 |
β Preview Β· Save Β· Load κΈ°λ₯ ν¬ν¨
|
4 |
β Load μ λ
Έλ/μ’ν/μ£μ§ μλ 볡μ
|
|
|
5 |
"""
|
6 |
|
7 |
import os, json, typing, tempfile
|
@@ -22,16 +23,32 @@ def export_file(data: typing.Dict[str, typing.Any]) -> typing.Optional[str]:
|
|
22 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
23 |
return path
|
24 |
|
25 |
-
def import_workflow(file_obj: gr.File) -> typing.Tuple[typing.Any, str]:
|
26 |
"""
|
27 |
μ
λ‘λλ JSON β WorkflowBuilder κ° & μ½λλ·° κ°±μ .
|
28 |
-
|
29 |
"""
|
30 |
if file_obj is None:
|
31 |
-
return
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
# -------------------------------------------------------------------
|
37 |
# π¨ CSS
|
@@ -79,6 +96,7 @@ with gr.Blocks(title="π¨ Visual Workflow Builder", theme=gr.themes.Soft(), css
|
|
79 |
workflow_builder = WorkflowBuilder(
|
80 |
label="π¨ Visual Workflow Designer",
|
81 |
info="Drag from output β input β’ Click nodes to edit properties",
|
|
|
82 |
)
|
83 |
|
84 |
# βββ Export / Import βββ
|
@@ -91,10 +109,28 @@ with gr.Blocks(title="π¨ Visual Workflow Builder", theme=gr.themes.Soft(), css
|
|
91 |
|
92 |
code_view = gr.Code(language="json", label="Workflow Configuration", lines=14)
|
93 |
|
|
|
|
|
|
|
94 |
# μ΄λ²€νΈ μ°κ²°
|
95 |
btn_preview.click(fn=export_pretty, inputs=workflow_builder, outputs=code_view)
|
96 |
btn_download.click(fn=export_file, inputs=workflow_builder)
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
with gr.Accordion("π How to Use", open=False):
|
100 |
gr.Markdown(
|
@@ -104,11 +140,82 @@ with gr.Blocks(title="π¨ Visual Workflow Builder", theme=gr.themes.Soft(), css
|
|
104 |
3. **Edit** β click any node to change its properties
|
105 |
4. **Save** β *Save JSON file* to download your workflow
|
106 |
5. **Load** β *Load JSON* to restore (nodes auto-position)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
"""
|
108 |
)
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
# -------------------------------------------------------------------
|
111 |
# π μ€ν
|
112 |
# -------------------------------------------------------------------
|
113 |
if __name__ == "__main__":
|
114 |
-
demo.launch(server_name="0.0.0.0", show_error=True)
|
|
|
2 |
app.py ββββββββββββββββββββββββββββββββββββββββββββββββ
|
3 |
β Preview Β· Save Β· Load κΈ°λ₯ ν¬ν¨
|
4 |
β Load μ λ
Έλ/μ’ν/μ£μ§ μλ 볡μ
|
5 |
+
β JSON λ‘λ μ μκ°μ λ
Έλ μλ λ°°μΉ μμ
|
6 |
"""
|
7 |
|
8 |
import os, json, typing, tempfile
|
|
|
23 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
24 |
return path
|
25 |
|
26 |
+
def import_workflow(file_obj: gr.File) -> typing.Tuple[typing.Dict[str, typing.Any], str]:
|
27 |
"""
|
28 |
μ
λ‘λλ JSON β WorkflowBuilder κ° & μ½λλ·° κ°±μ .
|
29 |
+
WorkflowBuilderκ° μ§μ dictλ₯Ό λ°λλ‘ μμ
|
30 |
"""
|
31 |
if file_obj is None:
|
32 |
+
return {}, "No workflow loaded"
|
33 |
+
|
34 |
+
try:
|
35 |
+
with open(file_obj.name, "r", encoding="utf-8") as f:
|
36 |
+
data = json.load(f)
|
37 |
+
|
38 |
+
# WorkflowBuilderκ° κΈ°λνλ νμμΈμ§ νμΈ
|
39 |
+
if not isinstance(data, dict):
|
40 |
+
return {}, "Invalid workflow format: expected dictionary"
|
41 |
+
|
42 |
+
# νμ ν€λ€μ΄ μλμ§ νμΈ (nodes, edges λ±)
|
43 |
+
if 'nodes' not in data:
|
44 |
+
data['nodes'] = []
|
45 |
+
if 'edges' not in data:
|
46 |
+
data['edges'] = []
|
47 |
+
|
48 |
+
return data, json.dumps(data, indent=2, ensure_ascii=False)
|
49 |
+
|
50 |
+
except Exception as e:
|
51 |
+
return {}, f"Error loading workflow: {str(e)}"
|
52 |
|
53 |
# -------------------------------------------------------------------
|
54 |
# π¨ CSS
|
|
|
96 |
workflow_builder = WorkflowBuilder(
|
97 |
label="π¨ Visual Workflow Designer",
|
98 |
info="Drag from output β input β’ Click nodes to edit properties",
|
99 |
+
value={"nodes": [], "edges": []} # μ΄κΈ°κ° μ€μ
|
100 |
)
|
101 |
|
102 |
# βββ Export / Import βββ
|
|
|
109 |
|
110 |
code_view = gr.Code(language="json", label="Workflow Configuration", lines=14)
|
111 |
|
112 |
+
# μν λ©μμ§ νμμ©
|
113 |
+
status_msg = gr.Textbox(label="Status", visible=False)
|
114 |
+
|
115 |
# μ΄λ²€νΈ μ°κ²°
|
116 |
btn_preview.click(fn=export_pretty, inputs=workflow_builder, outputs=code_view)
|
117 |
btn_download.click(fn=export_file, inputs=workflow_builder)
|
118 |
+
|
119 |
+
# file_upload μ΄λ²€νΈ μμ - WorkflowBuilderλ₯Ό μ§μ μ
λ°μ΄νΈ
|
120 |
+
file_upload.change(
|
121 |
+
fn=import_workflow,
|
122 |
+
inputs=file_upload,
|
123 |
+
outputs=[workflow_builder, code_view]
|
124 |
+
)
|
125 |
+
|
126 |
+
# λλ²κΉ
μ© - νμ¬ workflow μν νμΈ
|
127 |
+
def debug_workflow(data):
|
128 |
+
print("Current workflow data:", json.dumps(data, indent=2))
|
129 |
+
return f"Nodes: {len(data.get('nodes', []))}, Edges: {len(data.get('edges', []))}"
|
130 |
+
|
131 |
+
btn_debug = gr.Button("π Debug Current State", variant="secondary")
|
132 |
+
debug_output = gr.Textbox(label="Debug Info", lines=1)
|
133 |
+
btn_debug.click(fn=debug_workflow, inputs=workflow_builder, outputs=debug_output)
|
134 |
|
135 |
with gr.Accordion("π How to Use", open=False):
|
136 |
gr.Markdown(
|
|
|
140 |
3. **Edit** β click any node to change its properties
|
141 |
4. **Save** β *Save JSON file* to download your workflow
|
142 |
5. **Load** β *Load JSON* to restore (nodes auto-position)
|
143 |
+
|
144 |
+
### π Expected JSON Format:
|
145 |
+
```json
|
146 |
+
{
|
147 |
+
"nodes": [
|
148 |
+
{
|
149 |
+
"id": "node_1",
|
150 |
+
"type": "custom",
|
151 |
+
"position": {"x": 100, "y": 100},
|
152 |
+
"data": {"label": "Node 1"}
|
153 |
+
}
|
154 |
+
],
|
155 |
+
"edges": [
|
156 |
+
{
|
157 |
+
"id": "edge_1",
|
158 |
+
"source": "node_1",
|
159 |
+
"target": "node_2"
|
160 |
+
}
|
161 |
+
]
|
162 |
+
}
|
163 |
+
```
|
164 |
"""
|
165 |
)
|
166 |
|
167 |
+
# μν μν¬νλ‘μ° λ‘λ λ²νΌ
|
168 |
+
gr.Markdown("### π― Sample Workflows")
|
169 |
+
|
170 |
+
def load_sample_workflow():
|
171 |
+
sample = {
|
172 |
+
"nodes": [
|
173 |
+
{
|
174 |
+
"id": "input_1",
|
175 |
+
"type": "input",
|
176 |
+
"position": {"x": 100, "y": 100},
|
177 |
+
"data": {"label": "Input Node", "value": "Sample Input"}
|
178 |
+
},
|
179 |
+
{
|
180 |
+
"id": "process_1",
|
181 |
+
"type": "default",
|
182 |
+
"position": {"x": 300, "y": 100},
|
183 |
+
"data": {"label": "Process Node"}
|
184 |
+
},
|
185 |
+
{
|
186 |
+
"id": "output_1",
|
187 |
+
"type": "output",
|
188 |
+
"position": {"x": 500, "y": 100},
|
189 |
+
"data": {"label": "Output Node"}
|
190 |
+
}
|
191 |
+
],
|
192 |
+
"edges": [
|
193 |
+
{
|
194 |
+
"id": "edge_1",
|
195 |
+
"source": "input_1",
|
196 |
+
"target": "process_1",
|
197 |
+
"sourceHandle": "output",
|
198 |
+
"targetHandle": "input"
|
199 |
+
},
|
200 |
+
{
|
201 |
+
"id": "edge_2",
|
202 |
+
"source": "process_1",
|
203 |
+
"target": "output_1",
|
204 |
+
"sourceHandle": "output",
|
205 |
+
"targetHandle": "input"
|
206 |
+
}
|
207 |
+
]
|
208 |
+
}
|
209 |
+
return sample, json.dumps(sample, indent=2, ensure_ascii=False)
|
210 |
+
|
211 |
+
btn_sample = gr.Button("π₯ Load Sample Workflow", variant="primary")
|
212 |
+
btn_sample.click(
|
213 |
+
fn=load_sample_workflow,
|
214 |
+
outputs=[workflow_builder, code_view]
|
215 |
+
)
|
216 |
+
|
217 |
# -------------------------------------------------------------------
|
218 |
# π μ€ν
|
219 |
# -------------------------------------------------------------------
|
220 |
if __name__ == "__main__":
|
221 |
+
demo.launch(server_name="0.0.0.0", show_error=True)
|