{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "dc49e5ae", "metadata": {}, "outputs": [], "source": [ "from openai import OpenAI\n", "from dotenv import load_dotenv\n", "import os\n", "load_dotenv()\n", "import gradio as gr\n", "import base64\n", "from io import BytesIO\n", "from PIL import Image\n", "from IPython.display import Audio, display\n", "import google.generativeai\n", "import anthropic\n", "\n", "client = OpenAI(api_key=os.getenv(\"OPENAI_API_KEY\"))\n", "\n", "# Configure Gemini\n", "google.generativeai.configure(api_key=os.getenv(\"GOOGLE_API_KEY\"))\n", "\n", "# Configure Claude\n", "claude = anthropic.Anthropic(api_key=os.getenv(\"ANTHROPIC_API_KEY\"))\n", "openAI_model = \"gpt-3.5-turbo\"\n", "gemini_model = \"gemini-2.0-flash\"\n", "claude_model = \"claude-sonnet-4-20250514\"\n", "openai_audio_model = \"tts-1\"\n", "\n", "# Figma onboarding knowledge base\n", "FIGMA_KNOWLEDGE = \"\"\"\n", "You are a helpful Figma onboarding assistant. You help new users learn Figma's core features and workflows.\n", "\n", "Key Figma concepts to help users with:\n", "- Interface overview (toolbar, layers panel, properties panel)\n", "- Creating and editing frames\n", "- Working with shapes, text, and components\n", "- Using the pen tool for custom shapes\n", "- Auto Layout for responsive designs\n", "- Components and variants\n", "- Prototyping and interactions\n", "- Collaboration features\n", "- Design systems and libraries\n", "- Exporting assets\n", "- Keyboard shortcuts\n", "\n", "Always provide clear, step-by-step instructions and mention relevant keyboard shortcuts when applicable.\n", "\"\"\"\n", "\n", "promts = {\n", " \"Charlie\": FIGMA_KNOWLEDGE\n", "}\n", "\n", "def truncate_for_tts(text, max_length=4000):\n", " \"\"\"Truncate text for TTS while preserving complete sentences\"\"\"\n", " if len(text) <= max_length:\n", " return text\n", " \n", " # Try to truncate at sentence boundaries\n", " sentences = text.split('. ')\n", " truncated = \"\"\n", " \n", " for sentence in sentences:\n", " if len(truncated + sentence + '. ') <= max_length:\n", " truncated += sentence + '. '\n", " else:\n", " break\n", " \n", " # If we couldn't fit any complete sentences, just truncate hard\n", " if not truncated.strip():\n", " truncated = text[:max_length-10] + \"...\"\n", " \n", " return truncated.strip()\n", "\n", "def talker_openai(message):\n", " \"\"\"Generate audio from text using OpenAI TTS\"\"\"\n", " try:\n", " # Truncate message for TTS\n", " truncated_message = truncate_for_tts(message)\n", " \n", " response = client.audio.speech.create(\n", " model=\"tts-1\",\n", " voice=\"onyx\",\n", " input=truncated_message\n", " )\n", "\n", " audio_stream = BytesIO(response.content)\n", " output_filename = \"output_audio_openai.mp3\"\n", " with open(output_filename, \"wb\") as f:\n", " f.write(audio_stream.read())\n", "\n", " return output_filename\n", " except Exception as e:\n", " print(f\"Error generating audio with OpenAI: {str(e)}\")\n", " return None\n", "\n", "# def talker_gemini(message):\n", "# \"\"\"Generate audio from text using Gemini TTS\"\"\"\n", "# try:\n", "# # Try the newer Gemini 2.0 TTS API\n", "# model = google.generativeai.GenerativeModel(gemini_model)\n", " \n", "# # Truncate message for TTS\n", "# truncated_message = truncate_for_tts(message)\n", " \n", "# # Generate audio using Gemini with simplified config\n", "# response = model.generate_content(\n", "# truncated_message,\n", "# generation_config={\n", "# \"response_modalities\": [\"AUDIO\"]\n", "# }\n", "# )\n", " \n", "# # Check if response has audio data\n", "# if hasattr(response, 'audio_data') and response.audio_data:\n", "# output_filename = \"output_audio_gemini.wav\"\n", "# with open(output_filename, \"wb\") as f:\n", "# f.write(response.audio_data)\n", "# return output_filename\n", "# else:\n", "# print(\"Gemini response does not contain audio data\")\n", "# raise Exception(\"No audio data in Gemini response\")\n", " \n", "# except Exception as e:\n", "# print(f\"Error generating audio with Gemini: {str(e)}\")\n", "# print(\"Gemini TTS not available, using OpenAI TTS with different voice\")\n", "# # Use OpenAI TTS but with a different voice to distinguish\n", "# try:\n", "# # Truncate message for TTS\n", "# truncated_message = truncate_for_tts(message)\n", " \n", "# response = client.audio.speech.create(\n", "# model=\"tts-1\",\n", "# voice=\"alloy\", # Different voice to indicate it's for Gemini responses\n", "# input=truncated_message\n", "# )\n", "# audio_stream = BytesIO(response.content)\n", "# output_filename = \"output_audio_gemini_fallback.mp3\"\n", "# with open(output_filename, \"wb\") as f:\n", "# f.write(audio_stream.read())\n", "# return output_filename\n", "# except Exception as fallback_error:\n", "# print(f\"Fallback TTS also failed: {str(fallback_error)}\")\n", "# return None\n", "\n", "# def talker_claude(message):\n", "# \"\"\"Generate audio from text using Claude TTS (fallback to OpenAI)\"\"\"\n", "# try:\n", "# # Truncate message for TTS\n", "# truncated_message = truncate_for_tts(message)\n", " \n", "# # Claude doesn't have native TTS, so we'll use OpenAI TTS\n", "# # but with a different filename to distinguish\n", "# response = client.audio.speech.create(\n", "# model=\"tts-1\",\n", "# voice=\"nova\", # Different voice for Claude responses\n", "# input=truncated_message\n", "# )\n", "\n", "# audio_stream = BytesIO(response.content)\n", "# output_filename = \"output_audio_claude.mp3\"\n", "# with open(output_filename, \"wb\") as f:\n", "# f.write(audio_stream.read())\n", "\n", "# return output_filename\n", "# except Exception as e:\n", "# print(f\"Error generating audio for Claude: {str(e)}\")\n", "# return None\n", "\n", "def talker(message, model_choice):\n", " \"\"\"Generate audio from text using selected model\"\"\"\n", " # if model_choice == \"Google Gemini (2.0 Flash)\":\n", " # return talker_gemini(message)\n", " # elif model_choice == \"Claude (Sonnet 4)\":\n", " # return talker_claude(message)\n", " # else:\n", " return talker_openai(message)\n", "\n", "def get_figma_help_openai(user_question, chat_history):\n", " \"\"\"Get Figma onboarding assistance using OpenAI\"\"\"\n", " try:\n", " messages = [\n", " {\"role\": \"system\", \"content\": FIGMA_KNOWLEDGE}\n", " ]\n", " \n", " # Convert messages format chat history to OpenAI format\n", " for msg in chat_history:\n", " if msg[\"role\"] == \"user\":\n", " messages.append({\"role\": \"user\", \"content\": msg[\"content\"]})\n", " elif msg[\"role\"] == \"assistant\":\n", " messages.append({\"role\": \"assistant\", \"content\": msg[\"content\"]})\n", " \n", " messages.append({\"role\": \"user\", \"content\": user_question})\n", " \n", " response = client.chat.completions.create(\n", " model=openAI_model,\n", " messages=messages,\n", " max_tokens=500,\n", " temperature=0.7\n", " )\n", " return response.choices[0].message.content\n", " \n", " except Exception as e:\n", " return f\"Sorry, I encountered an error with OpenAI: {str(e)}\"\n", "\n", "def get_figma_help_gemini(user_question, chat_history):\n", " \"\"\"Get Figma onboarding assistance using Gemini\"\"\"\n", " try:\n", " gemini = google.generativeai.GenerativeModel(\n", " model_name=gemini_model,\n", " system_instruction=FIGMA_KNOWLEDGE,\n", " )\n", " \n", " # Build conversation context from messages format\n", " conversation_context = \"\"\n", " for msg in chat_history:\n", " if msg[\"role\"] == \"user\":\n", " conversation_context += f\"User: {msg['content']}\\n\"\n", " elif msg[\"role\"] == \"assistant\":\n", " conversation_context += f\"Assistant: {msg['content']}\\n\\n\"\n", " \n", " message = conversation_context + f\"User: {user_question}\"\n", " response = gemini.generate_content(message)\n", " reply = response.text\n", " return reply\n", " \n", " except Exception as e:\n", " return f\"Sorry, I encountered an error with Gemini: {str(e)}\"\n", "\n", "def get_figma_help_claude(user_question, chat_history):\n", " \"\"\"Get Figma onboarding assistance using Claude\"\"\"\n", " try:\n", " # Convert messages format to Claude format\n", " claude_messages = []\n", " for msg in chat_history:\n", " if msg[\"role\"] == \"user\":\n", " claude_messages.append({\"role\": \"user\", \"content\": msg[\"content\"]})\n", " elif msg[\"role\"] == \"assistant\":\n", " claude_messages.append({\"role\": \"assistant\", \"content\": msg[\"content\"]})\n", " \n", " # Add the current question\n", " claude_messages.append({\"role\": \"user\", \"content\": user_question})\n", " \n", " response = claude.messages.create(\n", " model=claude_model,\n", " max_tokens=500,\n", " temperature=0.7,\n", " system=promts[\"Charlie\"],\n", " messages=claude_messages,\n", " )\n", " reply = response.content[0].text\n", " return reply\n", " \n", " except Exception as e:\n", " return f\"Sorry, I encountered an error with Claude: {str(e)}\"\n", "\n", "def respond(message, chat_history, model_choice):\n", " if not message.strip():\n", " return \"\", chat_history, \"\", model_choice\n", " \n", " bot_message = get_figma_help(message, chat_history, model_choice)\n", " \n", " # Add user message and bot response in messages format\n", " new_history = chat_history + [\n", " {\"role\": \"user\", \"content\": message},\n", " {\"role\": \"assistant\", \"content\": bot_message}\n", " ]\n", " \n", " return \"\", new_history, bot_message, model_choice\n", "\n", "def clear_chat():\n", " \"\"\"Clear the chat history\"\"\"\n", " return [], \"\", None\n", "\n", "def get_figma_help(user_question, chat_history, model_choice):\n", " \"\"\"Get Figma onboarding assistance using selected model\"\"\"\n", " if model_choice == \"OpenAI (GPT-3.5)\":\n", " return get_figma_help_openai(user_question, chat_history)\n", " elif model_choice == \"Google Gemini (2.0 Flash)\":\n", " return get_figma_help_gemini(user_question, chat_history)\n", " elif model_choice == \"Claude (Sonnet 4)\":\n", " return get_figma_help_claude(user_question, chat_history)\n", " else:\n", " return \"Please select a valid model.\"\n", "\n", "\n", "custom_css = \"\"\"\n", "\n", "/* Chat area styling */\n", ".styled-chat {\n", " border-radius: 15px !important;\n", " box-shadow: 0 4px 12px var(--shadow-color) !important;\n", " border: 1px solid var(--border-color) !important;\n", " padding: 10px;\n", " # background-color: #fff;\n", "}\n", "\n", "/* Audio player styling */\n", ".styled-audio {\n", " border-radius: 15px !important;\n", " box-shadow: 0 4px 12px var(--shadow-color) !important;\n", " border: 10px solid var(--block-background-fill) !important;\n", " padding: 10px;\n", " background-color: var(--background-fill-secondary) !important;\n", "}\n", "\"\"\"\n", "\n", "# Create Gradio interface\n", "with gr.Blocks(title=\"Figma Onboarding Assistant\", theme=gr.themes.Soft(),css=custom_css) as demo:\n", " gr.Markdown(\n", " \"\"\"\n", "
Your AI-powered Figma learning companion
\n", "🚀 Getting Started
\n",
" Interface overview, basic navigation
🛠️ Tools & Features
\n",
" Pen tool, shapes, text, layers
📐 Auto Layout
\n",
" Responsive design techniques
🔗 Prototyping
\n",
" Interactions and animations
🧩 Components
\n",
" Creating reusable elements
👥 Collaboration
\n",
" Sharing and team workflows
📚 Design Systems
\n",
" Libraries and style guides
⚡ Shortcuts
\n",
" Productivity tips and tricks
💡 Pro tip: Ask specific questions like \\\"How do I create a button component?\\\" for the best results!
\n", "Click any question below to get started instantly!
\n", "