import logging
import os
import re
import warnings
import gradio as gr
import requests
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline, Pipeline
from templates import starting_app_code, update_iframe_js, copy_snippet_js, download_code_js, load_js, DemoType
# Filter the UserWarning raised by the audio component.
warnings.filterwarnings("ignore", message='Trying to convert audio automatically from int32 to 16-bit int format')
logging.basicConfig(
level=logging.INFO, # Set the logging level to INFO or any other desired level
format="%(asctime)s - %(message)s", # Define the log message format
datefmt="%Y-%m-%d %H:%M:%S", # Define the timestamp format
)
logger = logging.getLogger("my_logger")
HF_TOKEN = os.getenv("HF_TOKEN")
if not HF_TOKEN:
raise Exception("HF_TOKEN environment variable is required to call remote API.")
API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta"
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
def init_speech_to_text_model() -> Pipeline:
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
model_id = "distil-whisper/distil-medium.en"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)
return pipeline(
"automatic-speech-recognition",
model=model,
tokenizer=processor.tokenizer,
feature_extractor=processor.feature_extractor,
max_new_tokens=128,
torch_dtype=torch_dtype,
device=device,
)
whisper_pipe = init_speech_to_text_model()
code_pattern = re.compile(r'```python\n(.*?)```', re.DOTALL)
def query(payload: dict):
response = requests.post(API_URL, headers=headers, json=payload)
return response.json()
def generate_text(code: str, prompt: str) -> (str, str, str):
logger.info(f"Calling API with prompt:\n{prompt}")
prompt = f"```python\n{code}```\nGiven the code above return only updated code for the following request:\n{prompt}\n<|assistant|>"
params = {"max_new_tokens": 512}
output = query({"inputs": prompt, "parameters": params})
if 'error' in output:
logger.warning(f'Language model call failed: {output["error"]}')
raise gr.Warning(f'Language model call failed: {output["error"]}')
logger.info(f'API RESPONSE\n{output[0]["generated_text"]}')
assistant_reply = output[0]["generated_text"].split('<|assistant|>')[1]
match = re.search(code_pattern, assistant_reply)
if not match:
return assistant_reply, code, None
new_code = match.group(1)
logger.info(f'NEW CODE:\nnew_code')
return assistant_reply, new_code, None
def transcribe(audio: str) -> (str, str):
result = whisper_pipe(audio)
return result["text"], None
def copy_notify(code):
gr.Info("App code snippet copied!")
def add_hotkeys() -> str:
return """() => {
function gradioApp() {
const elems = document.getElementsByTagName('gradio-app');
const elem = elems.length == 0 ? document : elems[0];
if (elem !== document) {
elem.getElementById = function(id) {
return document.getElementById(id);
};
}
return elem.shadowRoot ? elem.shadowRoot : elem;
}
window.addEventListener('keydown', (e) => {
if (e.keyCode == 192 && e.ctrlKey) { // CTRL + ` key
const recordButton = gradioApp().querySelector("div.mic-wrap > button");
console.log(recordButton);
recordButton.click();
}
});
}
"""
with gr.Blocks() as demo:
gr.Markdown("
KiteWind 🪁🍃
")
gr.Markdown(
"Chat-assisted web app creator by @gstaff
")
selectedTab = gr.State(value='gradio-lite')
with gr.Tab('Gradio (gradio-lite)') as gradio_lite_tab:
with gr.Row():
with gr.Column():
gr.Markdown("## 1. Run your app in the browser!")
gr.HTML(value='')
gr.Markdown("## 2. Customize using voice requests!")
with gr.Row():
with gr.Column():
with gr.Group():
in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)", source='microphone', type='filepath')
in_prompt = gr.Textbox(label="Or type a text request and press Enter",
placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Put the reversed name output into a separate textbox")
out_text = gr.TextArea(label="Chat Assistant Response")
clear = gr.ClearButton([in_prompt, in_audio, out_text])
with gr.Column():
code_area = gr.Code(label="App Code - You can also edit directly and then click Update App",
language='python', value=starting_app_code(DemoType.GRADIO))
update_btn = gr.Button("Update App", variant="primary")
code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None,
'_js': update_iframe_js(DemoType.GRADIO)}
gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt],
'outputs': [out_text, code_area]}
transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
update_btn.click(**code_update_params)
in_prompt.submit(**gen_text_params).then(**code_update_params)
in_audio.stop_recording(**transcribe_params).then(**gen_text_params).then(**code_update_params)
with gr.Row():
with gr.Column():
gr.Markdown("## 3. Export your app to share!")
copy_snippet_btn = gr.Button("Copy app snippet to paste in another page")
copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js(DemoType.GRADIO))
download_btn = gr.Button("Download app as a standalone file")
download_btn.click(None, code_area, None, _js=download_code_js(DemoType.GRADIO))
with gr.Row():
with gr.Column():
gr.Markdown("## Current limitations")
with gr.Accordion("Click to view", open=False):
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")
with gr.Tab('Streamlit (stlite)') as stlite_tab:
with gr.Row():
with gr.Column():
gr.Markdown("## 1. Run your app in the browser!")
gr.HTML(value='')
gr.Markdown("## 2. Customize using voice requests!")
with gr.Row():
with gr.Column():
with gr.Group():
in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)", source='microphone', type='filepath')
in_prompt = gr.Textbox(label="Or type a text request and press Enter",
placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Make the button primary")
out_text = gr.TextArea(label="Chat Assistant Response")
clear_btn = gr.ClearButton([in_prompt, in_audio, out_text])
with gr.Column():
code_area = gr.Code(label="App Code - You can also edit directly and then click Update App",
language='python', value=starting_app_code(DemoType.STREAMLIT))
update_btn = gr.Button("Update App", variant="primary")
code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None,
'_js': update_iframe_js(DemoType.STREAMLIT)}
gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt],
'outputs': [out_text, code_area]}
transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
update_btn.click(**code_update_params)
in_prompt.submit(**gen_text_params).then(**code_update_params)
in_audio.stop_recording(**transcribe_params).then(**gen_text_params).then(**code_update_params)
with gr.Row():
with gr.Column():
gr.Markdown("## 3. Export your app to share!")
copy_snippet_btn = gr.Button("Copy app snippet to paste in another page")
copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js(DemoType.STREAMLIT))
download_btn = gr.Button("Download app as a standalone file")
download_btn.click(None, code_area, None, _js=download_code_js(DemoType.STREAMLIT))
with gr.Row():
with gr.Column():
gr.Markdown("## Current limitations")
with gr.Accordion("Click to view", open=False):
gr.Markdown(
"- Only Streamlit apps using libraries available in pyodide are supported\n- The chat hasn't been tuned on Streamlit library data; it may make mistakes")
gradio_lite_tab.select(lambda: "gradio-lite", None, selectedTab).then(None, None, None,
_js=load_js(DemoType.GRADIO))
stlite_tab.select(lambda: "stlite", None, selectedTab).then(None, None, None, _js=load_js(DemoType.STREAMLIT))
demo.load(None, None, None, _js=load_js(DemoType.GRADIO))
demo.load(None, None, None, _js=add_hotkeys())
demo.css = "footer {visibility: hidden}"
if __name__ == "__main__":
demo.queue().launch()