Spaces:
Sleeping
Sleeping
| 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) | |