Add initial app files.
Browse files- README.md +16 -5
- app.py +190 -0
- gradio-lite-playground.html +28 -0
- requirements.txt +1 -0
README.md
CHANGED
@@ -1,12 +1,23 @@
|
|
1 |
---
|
2 |
-
title: KiteWind
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
sdk_version: 3.50.2
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: KiteWind Chat-assisted Web App Creator
|
3 |
+
emoji: πͺπ
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
sdk: gradio
|
7 |
sdk_version: 3.50.2
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
12 |
+
# KiteWind πͺπ
|
13 |
+
<h4>Chat-assisted web app creator by <a href="https://huggingface.co/gstaff">@gstaff</a></h4>
|
14 |
+
|
15 |
+
## Features
|
16 |
+
- Run your app in the browser with gradio-lite!
|
17 |
+
- Customize using voice requests and chat-assisted development!
|
18 |
+
- Export as a standalone HTML file or copy a snippet to use in another webpage!
|
19 |
+
|
20 |
+
## Current Limitations
|
21 |
+
- Only gradio-lite apps using the python standard libraries and gradio are supported
|
22 |
+
- The chat hasn't been tuned on gradio library data; it may make mistakes
|
23 |
+
- The app needs to fully reload each time it is changed
|
app.py
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from pathlib import Path
|
3 |
+
import gradio as gr
|
4 |
+
import re
|
5 |
+
import whisper
|
6 |
+
import requests
|
7 |
+
|
8 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
9 |
+
|
10 |
+
API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta"
|
11 |
+
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
12 |
+
|
13 |
+
code_pattern = r'```python\n(.*?)```'
|
14 |
+
|
15 |
+
starting_app_code = """import gradio as gr
|
16 |
+
|
17 |
+
def greet(name):
|
18 |
+
return "Hello " + name + "!"
|
19 |
+
|
20 |
+
with gr.Blocks(theme="monochrome") as demo:
|
21 |
+
name = gr.Textbox(label="Name", value="World")
|
22 |
+
output = gr.Textbox(label="Output Box")
|
23 |
+
greet_btn = gr.Button("Greet")
|
24 |
+
greet_btn.click(fn=greet, inputs=name, outputs=output)
|
25 |
+
name.submit(fn=greet, inputs=name, outputs=output)
|
26 |
+
|
27 |
+
if __name__ == "__main__":
|
28 |
+
demo.css = "footer {visibility: hidden}"
|
29 |
+
demo.launch()
|
30 |
+
"""
|
31 |
+
|
32 |
+
html_template = Path('gradio-lite-playground.html').read_text()
|
33 |
+
pattern = r"# APP CODE START(.*?)# APP CODE END"
|
34 |
+
|
35 |
+
load_js = f"""() => {{
|
36 |
+
const htmlString = '<iframe class="my-frame" width="100%" height="512px" src="about:blank"></iframe>';
|
37 |
+
const parser = new DOMParser();
|
38 |
+
const doc = parser.parseFromString(htmlString, 'text/html');
|
39 |
+
const iframe = doc.querySelector('.my-frame');
|
40 |
+
const div = document.getElementById('demoDiv');
|
41 |
+
div.appendChild(iframe);
|
42 |
+
|
43 |
+
const frame = document.querySelector('.my-frame');
|
44 |
+
frame.contentWindow.document.open('text/html', 'replace');
|
45 |
+
frame.contentWindow.document.write(`{html_template}`);
|
46 |
+
frame.contentWindow.document.close();
|
47 |
+
}}"""
|
48 |
+
|
49 |
+
# TODO: Works but is inefficient because the iframe has to be reloaded each time
|
50 |
+
update_iframe_js = f"""(code) => {{
|
51 |
+
console.log(`UPDATING CODE`);
|
52 |
+
console.log(code)
|
53 |
+
const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
|
54 |
+
const template = `{html_template}`;
|
55 |
+
const completedTemplate = template.replace(pattern, code);
|
56 |
+
|
57 |
+
console.log(completedTemplate);
|
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('demoDiv');
|
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 |
+
console.log(`UPDATE DONE`);
|
74 |
+
}}"""
|
75 |
+
|
76 |
+
copy_snippet_js = f"""async (code) => {{
|
77 |
+
console.log(`DOWNLOADING CODE`);
|
78 |
+
const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
|
79 |
+
const template = `<div id="KiteWindApp">\n<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
|
80 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
|
81 |
+
<gradio-lite>\n# APP CODE START\n\n# APP CODE END\n</gradio-lite>\n</div>\n`;
|
82 |
+
// Step 1: Generate the HTML content
|
83 |
+
const completedTemplate = template.replace(pattern, code);
|
84 |
+
|
85 |
+
const snippet = completedTemplate;
|
86 |
+
console.log(snippet);
|
87 |
+
|
88 |
+
await navigator.clipboard.writeText(snippet);
|
89 |
+
|
90 |
+
console.log(`COPY DONE`);
|
91 |
+
}}"""
|
92 |
+
|
93 |
+
download_code_js = f"""(code) => {{
|
94 |
+
console.log(`DOWNLOADING CODE`);
|
95 |
+
const pattern = /# APP CODE START(.*?)# APP CODE END/gs;
|
96 |
+
const template = `{html_template}`;
|
97 |
+
// Step 1: Generate the HTML content
|
98 |
+
const completedTemplate = template.replace(pattern, code);
|
99 |
+
|
100 |
+
// Step 2: Create a Blob from the HTML content
|
101 |
+
const blob = new Blob([completedTemplate], {{ type: "text/html" }});
|
102 |
+
|
103 |
+
// Step 3: Create a URL for the Blob
|
104 |
+
const url = URL.createObjectURL(blob);
|
105 |
+
|
106 |
+
// Step 4: Create a download link
|
107 |
+
const downloadLink = document.createElement("a");
|
108 |
+
downloadLink.href = url;
|
109 |
+
downloadLink.download = "gradio-lite-app.html"; // Specify the filename for the download
|
110 |
+
|
111 |
+
// Step 5: Trigger a click event on the download link
|
112 |
+
downloadLink.click();
|
113 |
+
|
114 |
+
// Clean up by revoking the URL
|
115 |
+
URL.revokeObjectURL(url);
|
116 |
+
|
117 |
+
console.log(`DOWNLOAD DONE`);
|
118 |
+
}}"""
|
119 |
+
|
120 |
+
|
121 |
+
def query(payload):
|
122 |
+
response = requests.post(API_URL, headers=headers, json=payload)
|
123 |
+
return response.json()
|
124 |
+
|
125 |
+
|
126 |
+
def generate_text(code, prompt):
|
127 |
+
print(f"Calling API with prompt:\n{prompt}")
|
128 |
+
prompt = f"```python\n{code}```\nGiven the code above return only updated code for the following request:\n{prompt}\n<|assistant|>"
|
129 |
+
params = {"max_new_tokens": 512}
|
130 |
+
output = query({
|
131 |
+
"inputs": prompt,
|
132 |
+
"parameters": params,
|
133 |
+
})
|
134 |
+
print(f'API RESPONSE\n{output[0]["generated_text"]}')
|
135 |
+
assistant_reply = output[0]["generated_text"].split('<|assistant|>')[1]
|
136 |
+
match = re.search(code_pattern, assistant_reply, re.DOTALL)
|
137 |
+
new_code = match.group(1)
|
138 |
+
print(new_code)
|
139 |
+
# TODO: error handling here
|
140 |
+
return assistant_reply, new_code
|
141 |
+
|
142 |
+
|
143 |
+
model = whisper.load_model('medium')
|
144 |
+
|
145 |
+
|
146 |
+
def transcribe(audio):
|
147 |
+
result = model.transcribe(audio, language='en', verbose=False)
|
148 |
+
return result["text"]
|
149 |
+
|
150 |
+
|
151 |
+
with gr.Blocks() as demo:
|
152 |
+
gr.Markdown("<h1 align=\"center\">KiteWind πͺοΏ½οΏ½οΏ½</h1>")
|
153 |
+
gr.Markdown("<h4 align=\"center\">Chat-assisted web app creator by <a href=\"https://huggingface.co/gstaff\">@gstaff</a></h4>")
|
154 |
+
with gr.Row():
|
155 |
+
with gr.Column():
|
156 |
+
gr.Markdown("## 1. Run your app in the browser!")
|
157 |
+
html = gr.HTML(value='<div id="demoDiv"></div>')
|
158 |
+
gr.Markdown("## 2. Customize using voice requests!")
|
159 |
+
with gr.Row():
|
160 |
+
with gr.Column():
|
161 |
+
with gr.Group():
|
162 |
+
in_audio = gr.Audio(label="Record a voice request", source='microphone', type='filepath')
|
163 |
+
in_prompt = gr.Textbox(label="Or type a text request and press Enter",
|
164 |
+
placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Hola\n- Put the reversed name output into a separate textbox\n- Change the theme from monochrome to soft")
|
165 |
+
out_text = gr.TextArea(label="Chat Assistant Response")
|
166 |
+
clear = gr.ClearButton([in_prompt, in_audio, out_text])
|
167 |
+
with gr.Column():
|
168 |
+
code_area = gr.Code(label="App Code - You can also edit directly and then click Update App", language='python', value=starting_app_code)
|
169 |
+
update_btn = gr.Button("Update App", variant="primary")
|
170 |
+
update_btn.click(None, inputs=code_area, outputs=None, _js=update_iframe_js)
|
171 |
+
in_prompt.submit(generate_text, [code_area, in_prompt], [out_text, code_area]).then(None, inputs=code_area, outputs=None, _js=update_iframe_js)
|
172 |
+
in_audio.stop_recording(transcribe, [in_audio], [in_prompt]).then(generate_text, [code_area, in_prompt], [out_text, code_area]).then(None, inputs=code_area, outputs=None, _js=update_iframe_js)
|
173 |
+
with gr.Row():
|
174 |
+
with gr.Column():
|
175 |
+
gr.Markdown("## 3. Export your app to share!")
|
176 |
+
copy_snippet_btn = gr.Button("Copy app snippet to paste in another page")
|
177 |
+
copy_snippet_btn.click(None, code_area, None, _js=copy_snippet_js)
|
178 |
+
download_btn = gr.Button("Download app as a standalone file")
|
179 |
+
download_btn.click(None, code_area, None, _js=download_code_js)
|
180 |
+
with gr.Row():
|
181 |
+
with gr.Column():
|
182 |
+
gr.Markdown("## Current limitations")
|
183 |
+
with gr.Accordion("Click to view", open=False):
|
184 |
+
gr.Markdown("- 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")
|
185 |
+
|
186 |
+
demo.load(None, None, None, _js=load_js)
|
187 |
+
demo.css = "footer {visibility: hidden}"
|
188 |
+
|
189 |
+
if __name__ == "__main__":
|
190 |
+
demo.queue().launch()
|
gradio-lite-playground.html
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
|
4 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
|
5 |
+
</head>
|
6 |
+
<body>
|
7 |
+
<gradio-lite id="KiteWindApp">
|
8 |
+
# APP CODE START
|
9 |
+
import gradio as gr
|
10 |
+
|
11 |
+
def greet(name):
|
12 |
+
return "Hello " + name + "!"
|
13 |
+
|
14 |
+
with gr.Blocks(theme="monochrome") as demo:
|
15 |
+
name = gr.Textbox(label="Name", value="World")
|
16 |
+
output = gr.Textbox(label="Output Box")
|
17 |
+
greet_btn = gr.Button("Greet")
|
18 |
+
greet_btn.click(fn=greet, inputs=name, outputs=output)
|
19 |
+
name.submit(fn=greet, inputs=name, outputs=output)
|
20 |
+
|
21 |
+
if __name__ == "__main__":
|
22 |
+
demo.css = "footer {visibility: hidden}"
|
23 |
+
demo.launch()
|
24 |
+
|
25 |
+
# APP CODE END
|
26 |
+
</gradio-lite>
|
27 |
+
</body>
|
28 |
+
</html>
|
requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
openai-whisper
|