gstaff commited on
Commit
4b286f3
β€’
1 Parent(s): 3bddc3f

Cleanup and move template to utility file.

Browse files
app.py CHANGED
@@ -1,13 +1,18 @@
1
  import logging
2
  import os
3
  import re
4
- from pathlib import Path
5
 
6
  import gradio as gr
7
  import requests
8
  import torch
9
  from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
10
 
 
 
 
 
 
11
  logging.basicConfig(
12
  level=logging.INFO, # Set the logging level to INFO or any other desired level
13
  format="%(asctime)s - %(message)s", # Define the log message format
@@ -48,100 +53,7 @@ def init_speech_to_text_model():
48
 
49
  whisper_pipe = init_speech_to_text_model()
50
 
51
- code_pattern = r'```python\n(.*?)```'
52
-
53
- starting_app_code = """import gradio as gr
54
-
55
- def greet(name):
56
- return "Hello " + name + "!"
57
-
58
- with gr.Blocks(theme="monochrome") as demo:
59
- name = gr.Textbox(label="Name", value="World")
60
- output = gr.Textbox(label="Output Box")
61
- greet_btn = gr.Button("Greet")
62
- greet_btn.click(fn=greet, inputs=name, outputs=output)
63
- name.submit(fn=greet, inputs=name, outputs=output)
64
-
65
- if __name__ == "__main__":
66
- demo.css = "footer {visibility: hidden}"
67
- demo.launch()
68
- """
69
-
70
- html_template = Path('gradio-lite-playground.html').read_text()
71
- pattern = r"# APP CODE START(.*?)# APP CODE END"
72
-
73
- load_js = f"""() => {{
74
- const htmlString = '<iframe class="my-frame" width="100%" height="512px" src="about:blank"></iframe>';
75
- const parser = new DOMParser();
76
- const doc = parser.parseFromString(htmlString, 'text/html');
77
- const iframe = doc.querySelector('.my-frame');
78
- const div = document.getElementById('demoDiv');
79
- div.appendChild(iframe);
80
-
81
- const frame = document.querySelector('.my-frame');
82
- frame.contentWindow.document.open('text/html', 'replace');
83
- frame.contentWindow.document.write(`{html_template}`);
84
- frame.contentWindow.document.close();
85
- }}"""
86
-
87
- # TODO: Works but is inefficient because the iframe has to be reloaded each time
88
- update_iframe_js = f"""(code) => {{
89
- const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
90
- const template = `{html_template}`;
91
- const completedTemplate = template.replace(pattern, code);
92
-
93
- const oldFrame = document.querySelector('.my-frame');
94
- oldFrame.remove();
95
-
96
- const htmlString = '<iframe class="my-frame" width="100%" height="512px" src="about:blank"></iframe>';
97
- const parser = new DOMParser();
98
- const doc = parser.parseFromString(htmlString, 'text/html');
99
- const iframe = doc.querySelector('.my-frame');
100
- const div = document.getElementById('demoDiv');
101
- div.appendChild(iframe);
102
-
103
- const frame = document.querySelector('.my-frame');
104
- frame.contentWindow.document.open('text/html', 'replace');
105
- frame.contentWindow.document.write(completedTemplate);
106
- frame.contentWindow.document.close();
107
- }}"""
108
-
109
- copy_snippet_js = f"""async (code) => {{
110
- console.log(`DOWNLOADING CODE`);
111
- const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
112
- const template = `<div id="KiteWindApp">\n<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
113
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
114
- <gradio-lite>\n# APP CODE START\n\n# APP CODE END\n</gradio-lite>\n</div>\n`;
115
- // Step 1: Generate the HTML content
116
- const completedTemplate = template.replace(pattern, code);
117
-
118
- const snippet = completedTemplate;
119
- await navigator.clipboard.writeText(snippet);
120
- }}"""
121
-
122
- download_code_js = f"""(code) => {{
123
- const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
124
- const template = `{html_template}`;
125
- // Step 1: Generate the HTML content
126
- const completedTemplate = template.replace(pattern, code);
127
-
128
- // Step 2: Create a Blob from the HTML content
129
- const blob = new Blob([completedTemplate], {{ type: "text/html" }});
130
-
131
- // Step 3: Create a URL for the Blob
132
- const url = URL.createObjectURL(blob);
133
-
134
- // Step 4: Create a download link
135
- const downloadLink = document.createElement("a");
136
- downloadLink.href = url;
137
- downloadLink.download = "gradio-lite-app.html"; // Specify the filename for the download
138
-
139
- // Step 5: Trigger a click event on the download link
140
- downloadLink.click();
141
-
142
- // Clean up by revoking the URL
143
- URL.revokeObjectURL(url);
144
- }}"""
145
 
146
 
147
  def query(payload):
@@ -162,10 +74,11 @@ def generate_text(code, prompt):
162
  raise gr.Warning(f'Language model call failed: {output["error"]}')
163
  logger.info(f'API RESPONSE\n{output[0]["generated_text"]}')
164
  assistant_reply = output[0]["generated_text"].split('<|assistant|>')[1]
165
- match = re.search(code_pattern, assistant_reply, re.DOTALL)
 
 
166
  new_code = match.group(1)
167
  logger.info(f'NEW CODE:\nnew_code')
168
- # TODO: error handling here
169
  return assistant_reply, new_code, None
170
 
171
 
@@ -182,10 +95,11 @@ with gr.Blocks() as demo:
182
  gr.Markdown("<h1 align=\"center\">KiteWind πŸͺπŸƒ</h1>")
183
  gr.Markdown(
184
  "<h4 align=\"center\">Chat-assisted web app creator by <a href=\"https://huggingface.co/gstaff\">@gstaff</a></h4>")
 
185
  with gr.Row():
186
  with gr.Column():
187
  gr.Markdown("## 1. Run your app in the browser!")
188
- html = gr.HTML(value='<div id="demoDiv"></div>')
189
  gr.Markdown("## 2. Customize using voice requests!")
190
  with gr.Row():
191
  with gr.Column():
@@ -197,9 +111,10 @@ with gr.Blocks() as demo:
197
  clear = gr.ClearButton([in_prompt, in_audio, out_text])
198
  with gr.Column():
199
  code_area = gr.Code(label="App Code - You can also edit directly and then click Update App",
200
- language='python', value=starting_app_code)
201
  update_btn = gr.Button("Update App", variant="primary")
202
- code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None, '_js': update_iframe_js}
 
203
  gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt], 'outputs': [out_text, code_area]}
204
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
205
  update_btn.click(**code_update_params)
@@ -209,9 +124,9 @@ with gr.Blocks() as demo:
209
  with gr.Column():
210
  gr.Markdown("## 3. Export your app to share!")
211
  copy_snippet_btn = gr.Button("Copy app snippet to paste in another page")
212
- copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js)
213
  download_btn = gr.Button("Download app as a standalone file")
214
- download_btn.click(None, code_area, None, _js=download_code_js)
215
  with gr.Row():
216
  with gr.Column():
217
  gr.Markdown("## Current limitations")
@@ -219,7 +134,7 @@ with gr.Blocks() as demo:
219
  gr.Markdown(
220
  "- Only gradio-lite apps using the python standard libraries and gradio are supported\n- The chat hasn't been tuned on gradio library data; it may make mistakes\n- The app needs to fully reload each time it is changed")
221
 
222
- demo.load(None, None, None, _js=load_js)
223
  demo.css = "footer {visibility: hidden}"
224
 
225
  if __name__ == "__main__":
 
1
  import logging
2
  import os
3
  import re
4
+ import warnings
5
 
6
  import gradio as gr
7
  import requests
8
  import torch
9
  from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
10
 
11
+ from templates import starting_app_code, update_iframe_js, copy_snippet_js, download_code_js, load_js
12
+
13
+ # Filter the UserWarning raised by the audio component.
14
+ warnings.filterwarnings("ignore", message='Trying to convert audio automatically from int32 to 16-bit int format')
15
+
16
  logging.basicConfig(
17
  level=logging.INFO, # Set the logging level to INFO or any other desired level
18
  format="%(asctime)s - %(message)s", # Define the log message format
 
53
 
54
  whisper_pipe = init_speech_to_text_model()
55
 
56
+ code_pattern = re.compile(r'```python\n(.*?)```', re.DOTALL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
 
59
  def query(payload):
 
74
  raise gr.Warning(f'Language model call failed: {output["error"]}')
75
  logger.info(f'API RESPONSE\n{output[0]["generated_text"]}')
76
  assistant_reply = output[0]["generated_text"].split('<|assistant|>')[1]
77
+ match = re.search(code_pattern, assistant_reply)
78
+ if not match:
79
+ return assistant_reply, code, None
80
  new_code = match.group(1)
81
  logger.info(f'NEW CODE:\nnew_code')
 
82
  return assistant_reply, new_code, None
83
 
84
 
 
95
  gr.Markdown("<h1 align=\"center\">KiteWind πŸͺπŸƒ</h1>")
96
  gr.Markdown(
97
  "<h4 align=\"center\">Chat-assisted web app creator by <a href=\"https://huggingface.co/gstaff\">@gstaff</a></h4>")
98
+
99
  with gr.Row():
100
  with gr.Column():
101
  gr.Markdown("## 1. Run your app in the browser!")
102
+ html = gr.HTML(value='<div id="gradioDemoDiv"></div>')
103
  gr.Markdown("## 2. Customize using voice requests!")
104
  with gr.Row():
105
  with gr.Column():
 
111
  clear = gr.ClearButton([in_prompt, in_audio, out_text])
112
  with gr.Column():
113
  code_area = gr.Code(label="App Code - You can also edit directly and then click Update App",
114
+ language='python', value=starting_app_code('gradio-lite'))
115
  update_btn = gr.Button("Update App", variant="primary")
116
+ code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None,
117
+ '_js': update_iframe_js('gradio-lite')}
118
  gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt], 'outputs': [out_text, code_area]}
119
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
120
  update_btn.click(**code_update_params)
 
124
  with gr.Column():
125
  gr.Markdown("## 3. Export your app to share!")
126
  copy_snippet_btn = gr.Button("Copy app snippet to paste in another page")
127
+ copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js('gradio-lite'))
128
  download_btn = gr.Button("Download app as a standalone file")
129
+ download_btn.click(None, code_area, None, _js=download_code_js('gradio-lite'))
130
  with gr.Row():
131
  with gr.Column():
132
  gr.Markdown("## Current limitations")
 
134
  gr.Markdown(
135
  "- Only gradio-lite apps using the python standard libraries and gradio are supported\n- The chat hasn't been tuned on gradio library data; it may make mistakes\n- The app needs to fully reload each time it is changed")
136
 
137
+ demo.load(None, None, None, _js=load_js('gradio-lite'))
138
  demo.css = "footer {visibility: hidden}"
139
 
140
  if __name__ == "__main__":
templates.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ gradio_lite_html_template = Path('templates/gradio-lite/gradio-lite-template.html').read_text()
4
+ stlite_html_template = Path('templates/stlite/stlite-template.html').read_text()
5
+ stlite_snippet_template = Path('templates/stlite/stlite-snippet-template.html').read_text()
6
+
7
+
8
+ def starting_app_code(demo_type):
9
+ if demo_type == 'gradio-lite':
10
+ return Path('templates/gradio-lite/gradio_lite_starting_code.py').read_text()
11
+ elif demo_type == 'stlite':
12
+ return Path('templates/stlite/stlite_starting_code.py').read_text().replace('`', r'\`')
13
+ raise NotImplementedError(f'{demo_type} is not a supported demo type')
14
+
15
+
16
+ def load_js(demo_type):
17
+ if demo_type == 'gradio-lite':
18
+ return f"""() => {{
19
+ const htmlString = '<iframe class="my-frame" width="100%" height="512px" src="about:blank"></iframe>';
20
+ const parser = new DOMParser();
21
+ const doc = parser.parseFromString(htmlString, 'text/html');
22
+ const iframe = doc.querySelector('.my-frame');
23
+ const div = document.getElementById('gradioDemoDiv');
24
+ div.appendChild(iframe);
25
+
26
+ const frame = document.querySelector('.my-frame');
27
+ frame.contentWindow.document.open('text/html', 'replace');
28
+ frame.contentWindow.document.write(`{gradio_lite_html_template}`);
29
+ frame.contentWindow.document.close();
30
+ }}"""
31
+ elif demo_type == 'stlite':
32
+ return f"""() => {{
33
+ const htmlString = '<iframe id="demo-iframe" width="100%" height="512px" src="about:blank"></iframe>';
34
+ const parser = new DOMParser();
35
+ const doc = parser.parseFromString(htmlString, 'text/html');
36
+ const iframe = doc.getElementById('demo-iframe');
37
+ const div = document.getElementById('stliteDemoDiv');
38
+ div.appendChild(iframe);
39
+
40
+ const template = `{stlite_html_template.replace('STARTING_CODE', starting_app_code(demo_type))}`;
41
+ console.log(template);
42
+
43
+ const frame = document.getElementById('demo-iframe');
44
+ frame.contentWindow.document.open();
45
+ frame.contentWindow.document.write(template);
46
+ frame.contentWindow.document.close();
47
+ }}"""
48
+ raise NotImplementedError(f'{demo_type} is not a supported demo type')
49
+
50
+
51
+ def update_iframe_js(demo_type):
52
+ if demo_type == 'gradio-lite':
53
+ # TODO: Works but is inefficient because the iframe has to be reloaded each time
54
+ return f"""(code) => {{
55
+ const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
56
+ const template = `{gradio_lite_html_template}`;
57
+ const completedTemplate = template.replace(pattern, code);
58
+
59
+ const oldFrame = document.querySelector('.my-frame');
60
+ oldFrame.remove();
61
+
62
+ const htmlString = '<iframe class="my-frame" width="100%" height="512px" src="about:blank"></iframe>';
63
+ const parser = new DOMParser();
64
+ const doc = parser.parseFromString(htmlString, 'text/html');
65
+ const iframe = doc.querySelector('.my-frame');
66
+ const div = document.getElementById('gradioDemoDiv');
67
+ div.appendChild(iframe);
68
+
69
+ const frame = document.querySelector('.my-frame');
70
+ frame.contentWindow.document.open('text/html', 'replace');
71
+ frame.contentWindow.document.write(completedTemplate);
72
+ frame.contentWindow.document.close();
73
+ }}"""
74
+ elif demo_type == 'stlite':
75
+ return f"""async (code) => {{
76
+ async function update() {{
77
+ const appController = document.getElementById('demo-iframe').contentWindow.window.appController;
78
+ const newCode = code + ` # Update tag ${{Math.random()}}`;
79
+ const entrypointFile = "streamlit_app.py";
80
+ appController.writeFile(entrypointFile, newCode);
81
+ }};
82
+ await update();
83
+ }}"""
84
+ raise NotImplementedError(f'{demo_type} is not a supported demo type')
85
+
86
+
87
+ def copy_snippet_js(demo_type):
88
+ if demo_type == 'gradio-lite':
89
+ return f"""async (code) => {{
90
+ const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
91
+ const template = `<div id="KiteWindApp">\n<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
92
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
93
+ <gradio-lite>\n# APP CODE START\n\n# APP CODE END\n</gradio-lite>\n</div>\n`;
94
+ // Step 1: Generate the HTML content
95
+ const completedTemplate = template.replace(pattern, code);
96
+
97
+ const snippet = completedTemplate;
98
+ await navigator.clipboard.writeText(snippet);
99
+ }}"""
100
+ elif demo_type == 'stlite':
101
+ return f"""async (code) => {{
102
+ const escapedCode = code.replace('`', String.fromCharCode(92) + '`');
103
+ const template = `{stlite_html_template}`;
104
+ // Step 1: Generate the HTML content
105
+ const completedTemplate = template.replace('STARTING_CODE', code);
106
+
107
+ const snippet = completedTemplate;
108
+ await navigator.clipboard.writeText(snippet);
109
+ }}"""
110
+ raise NotImplementedError(f'{demo_type} is not a supported demo type')
111
+
112
+
113
+ def download_code_js(demo_type):
114
+ if demo_type == 'gradio-lite':
115
+ return f"""(code) => {{
116
+ const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
117
+ const template = `{gradio_lite_html_template}`;
118
+ // Step 1: Generate the HTML content
119
+ const completedTemplate = template.replace(pattern, code);
120
+
121
+ // Step 2: Create a Blob from the HTML content
122
+ const blob = new Blob([completedTemplate], {{ type: "text/html" }});
123
+
124
+ // Step 3: Create a URL for the Blob
125
+ const url = URL.createObjectURL(blob);
126
+
127
+ // Step 4: Create a download link
128
+ const downloadLink = document.createElement("a");
129
+ downloadLink.href = url;
130
+ downloadLink.download = "gradio-lite-app.html"; // Specify the filename for the download
131
+
132
+ // Step 5: Trigger a click event on the download link
133
+ downloadLink.click();
134
+
135
+ // Clean up by revoking the URL
136
+ URL.revokeObjectURL(url);
137
+ }}"""
138
+ elif demo_type == 'stlite':
139
+ return Path('stlite_starting_code.py').read_text()
140
+ raise NotImplementedError(f'{demo_type} is not a supported demo type')
gradio-lite-playground.html β†’ templates/gradio-lite/gradio-lite-template.html RENAMED
File without changes
templates/gradio-lite/gradio_lite_starting_code.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ def greet(name):
4
+ return "Hello " + name + "!"
5
+
6
+ with gr.Blocks(theme="monochrome") as demo:
7
+ name = gr.Textbox(label="Name", value="World")
8
+ output = gr.Textbox(label="Output Box")
9
+ greet_btn = gr.Button("Greet")
10
+ greet_btn.click(fn=greet, inputs=name, outputs=output)
11
+ name.submit(fn=greet, inputs=name, outputs=output)
12
+
13
+ if __name__ == "__main__":
14
+ demo.css = "footer {visibility: hidden}"
15
+ demo.launch()
templates/stlite/stlite-snippet-template.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="KiteWindApp">
2
+ <iframe id="demo-iframe" width="100%" height="512px" src="about:blank"></iframe>
3
+ <script>
4
+ const template = \`<div id="root"></div>
5
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.39.0/build/stlite.css"/>
6
+ <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.42.3/build/stlite.js"><\\\/script>
7
+ <script>
8
+ const streamlitConfig = "[server]\\\\nrunOnSave = true";
9
+ const code = \\\`STARTING_CODE\\\`;
10
+ const appController = stlite.mount({
11
+ requirements: ["matplotlib"], // Packages to install
12
+ entrypoint: "streamlit_app.py", // The target file of the streamlit run command
13
+ files: {
14
+ ".streamlit/config.toml": streamlitConfig,
15
+ "streamlit_app.py": code,
16
+ },
17
+ }, document.getElementById("root"));
18
+ // Returned controller functions defined here:
19
+ window.appController = appController;
20
+ <\\\/script>\`
21
+ const frame = document.getElementById('demo-iframe');
22
+ frame.contentWindow.document.open();
23
+ frame.contentWindow.document.write(template);
24
+ frame.contentWindow.document.close();
25
+ </script>
26
+ </div>
templates/stlite/stlite-template.html ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html lang="en">
2
+ <head>
3
+ <meta charset="UTF-8" />
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
+ <title>stlite app</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.39.0/build/stlite.css"/>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.42.3/build/stlite.js"></script>
12
+ <script>
13
+ const streamlitConfig = "[server]\\nrunOnSave = true";
14
+ const code = \`STARTING_CODE\`;
15
+ // Mount options defined here: https://github.com/whitphx/stlite/blob/main/packages/mountable/src/options.ts#L7
16
+ const appController = stlite.mount({
17
+ requirements: ["matplotlib"], // Packages to install
18
+ entrypoint: "streamlit_app.py", // The target file of the streamlit run command
19
+ files: {
20
+ ".streamlit/config.toml": streamlitConfig,
21
+ "streamlit_app.py": code,
22
+ },
23
+ }, document.getElementById("root"));
24
+ // Returned controller functions defined here:
25
+ // https://github.com/whitphx/stlite/blob/1739c1cebb469a20e979906ce264f90254d154ba/packages/mountable/src/index.tsx#L55
26
+ window.appController = appController;
27
+ </script>
28
+ </body>
29
+ </html>
templates/stlite/stlite_starting_code.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ name = st.text_input('Your name')
4
+ st.write("Hello,", name or "world")