umgefahren
Hide api
71eb4f4 unverified
import gradio as gr
from agent import session_service, root_agent, APP_NAME, runner, COMPONENT_INFO_DICT
from google.genai import types
from pprint import pprint, pformat
from uuid import uuid4
import os
import tempfile
def generate_mermaid(components, wires=[], direction: str = "LR") -> str:
"""
Build a Mermaid-JS flow-chart for a simple β€œhigh-level” schematic.
Parameters
----------
components : list[dict]
Each dict needs the keys
β€’ 'component_name' – human-readable label (e.g. "Arduino Uno R3")
β€’ 'id' – unique node ID used in the diagram
wires : list[dict]
Each dict needs the keys
β€’ 'start_id', 'start_pin' – origin node & pin
β€’ 'end_id', 'end_pin' – destination node & pin
direction : str, optional
Flow direction for the chart header; one of "LR", "RL", "TB", "TD".
Defaults to "LR" (left-to-right).
Returns
-------
str
A Markdown-fenced Mermaid block you can drop straight into GitHub,
Obsidian, Notion, etc.
"""
mermaid = [f"```mermaid", f"flowchart {direction}"]
# 1) Declare nodes
for node_id, node_name in components.items():
label = f"{node_name}\n({node_id})"
mermaid.append(f' {node_id}["{label}"]')
# 2) Draw labelled connections
for wire in wires:
start = wire["start_id"]
end = wire["end_id"]
label = f'{wire["start_pin"]} β†’ {wire["end_pin"]}'
mermaid.append(f' {start} -- "{label}" --> {end}')
mermaid.append("```")
return "\n".join(mermaid)
async def interact_with_agent(
prompt,
history: list[gr.ChatMessage],
session_id: gr.State,
local_storage: gr.BrowserState,
request: gr.Request,
):
messages = []
messages.append(
gr.ChatMessage(
role="assistant", content="Thinking...", metadata={"status": "pending"}
)
)
yield messages, None, None, None
# yield messages
user_id = local_storage[0]
print(user_id)
print(session_id)
session = session_service.get_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
)
if session is None:
session = session_service.create_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
)
fetch_code = lambda: session_service.get_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
).state.get("code")
fetch_components = lambda: session_service.get_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
).state.get("components")
fetch_wires = lambda: session_service.get_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
).state.get("wires")
def mermaid_assemble():
components = fetch_components()
wires = fetch_wires()
if components == None:
return gr.Markdown("No components yet")
if wires == None:
wires = []
mermaid = generate_mermaid(components, wires)
return gr.Markdown(mermaid)
content = types.Content(role="user", parts=[types.Part(text=prompt)])
temp = None
async for event in runner.run_async(
user_id=user_id, session_id=session_id, new_message=content
):
"""
print(
f" [Event] Author: {event.author}, Type: {type(event).__name__}, Final: {event.is_final_response()}, Content: {event.content}"
)
"""
for call in event.get_function_calls():
messages.append(
gr.ChatMessage(
role="assistant",
content=(
f"Invoking {call.name} with args: \n {pformat(call.args)}"
if not call.name == "write_code"
else gr.Code(call.args["code"], language="cpp")
),
metadata={"title": f"πŸ› οΈ Used tool {call.name}"},
)
)
yield messages, None, None, None
for call in event.get_function_responses():
if call.name == "compile_with_arduino" and call.response["success"]:
path = call.response["project_dir"]
elf_path = os.path.join(path, "output", "arduino_project.ino.elf")
print(f"elf_path: {elf_path}")
os.chmod(elf_path, 0o777)
yield messages, None, None, gr.DownloadButton(
"Download ELF",
elf_path,
variant="primary",
render=False,
interactive=True,
)
if event.is_final_response():
messages.append(
gr.ChatMessage(role="assistant", content=event.content.parts[0].text)
)
yield messages, gr.Code(
language="cpp", value=fetch_code()
), mermaid_assemble(), (
None
if not temp
else gr.DownloadButton(
"Download ELF",
temp,
variant="primary",
render=False,
interactive=True,
)
)
session = session_service.get_session(
app_name=APP_NAME, user_id=user_id, session_id=session_id
)
pprint(session.state)
with gr.Blocks() as demo:
session_id = gr.State(str(uuid4()))
code = gr.Code(language="cpp", render=False)
markdown_schematic = gr.Markdown(render=False)
local_storage = gr.BrowserState([str(uuid4())])
download_button = gr.DownloadButton(
"Download ELF",
"/tmp/arduino_project/output/arduino_project.ino.elf",
interactive=False,
render=False,
)
COMPONENT_NAMES_MD = "\n".join([f'- {name}' for name in COMPONENT_INFO_DICT.keys()])
with gr.Row():
gr.Markdown(
f"""
# Arduino Hardware and Software Vibecoder
This is a prototype of a vibecoder (like Lovable) that can generate Arduino code and schematics for you.
Given a prompt, it well look up the components it has availble and generate a high-level schematic and code.
It compiles the code to make sure it works and then gives you a download link for the ELF file that can then be uploaded to the Arduino.
It has the following components:
{COMPONENT_NAMES_MD}
"""
)
with gr.Row():
with gr.Column():
chatbot = gr.Chatbot(label="Agent", type="messages")
def clear_chat():
return [
str(uuid4()),
gr.Code(language="cpp", render=False),
gr.Markdown(render=False),
gr.DownloadButton(
"Download ELF",
"/tmp/arduino_project/output/arduino_project.ino.elf",
render=False,
interactive=False,
),
]
chatbot.clear(
lambda: clear_chat(),
outputs=[session_id, code, markdown_schematic, download_button],
)
chat_interface = gr.ChatInterface(
interact_with_agent,
chatbot=chatbot,
type="messages",
examples=[
[
'Design a schematic and write code for an arduino driven "Joke of the day machine" where the user presses a physical button and then a random joke of the day is displayed on the display.'
],
["Write a Hello World program for an Arduino Uno"],
["Write a program to blink an LED on an Arduino Uno"],
],
additional_inputs=[session_id, local_storage],
additional_outputs=[code, markdown_schematic, download_button],
fill_height=True,
show_progress="full",
)
with gr.Column():
code.render()
markdown_schematic.render()
with gr.Row():
download_button.render()
# mm
demo.launch(show_api=False)