Spaces:
Running
Running
| /** | |
| * Copyright 2024 Google LLC | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| import { SchemaType } from "@google/generative-ai"; | |
| import { useEffect, useRef, memo } from "react"; | |
| import { useLiveAPIContext } from "../../contexts/LiveAPIContext"; | |
| import type { ToolCall } from "../../multimodal-live-types"; | |
| // Add type definitions for window functions | |
| declare global { | |
| interface Window { | |
| initSketch: (container: HTMLElement) => void; | |
| updateSketch: (code: string, container: HTMLElement) => boolean; | |
| removeSketch: () => void; | |
| } | |
| } | |
| interface SketchArgs { | |
| sketch: string; | |
| } | |
| function P5SketchComponent() { | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| const { client, setConfig } = useLiveAPIContext(); | |
| useEffect(() => { | |
| if (containerRef.current) { | |
| window.initSketch(containerRef.current); | |
| } | |
| // Cleanup on unmount | |
| return () => { | |
| window.removeSketch(); | |
| }; | |
| }, []); | |
| useEffect(() => { | |
| setConfig({ | |
| model: "models/gemini-2.0-flash-exp", | |
| generationConfig: { | |
| temperature: 0.1, | |
| responseModalities: "audio", | |
| speechConfig: { | |
| voiceConfig: { | |
| prebuiltVoiceConfig: { | |
| voiceName: "Puck" | |
| } | |
| } | |
| } | |
| }, | |
| systemInstruction: { | |
| parts: [ | |
| { | |
| text: `You are a P5.js creative coding expert that helps users create interactive sketches. | |
| When a user requests a sketch: | |
| 1. Always use the updateSketch function to create or modify sketches | |
| 2. NEVER output code directly in the response - only use the function | |
| 3. After the sketch is created, explain what the sketch does and how to interact with it | |
| 4. If the user's request is unclear, just take your best guess to create a sketch | |
| You can create sketches using: | |
| - Basic shapes, colors, and animations | |
| - Mouse and keyboard interaction (mouseX, mouseY, keyPressed, keyCode, etc.) | |
| - Sound effects (p5.sound library) | |
| - Sprite-based games (p5.play library) | |
| - Full window canvas with automatic resizing | |
| Focus on creating visually engaging and interactive experiences. As soon as the user requests a sketch, confirm you heard them, THEN you should create the sketch and then explain what it does and how to interact with it. Be EXTREMELY brief and pithy.` | |
| }, | |
| ], | |
| }, | |
| tools: [ | |
| { | |
| functionDeclarations: [ | |
| { | |
| name: "updateSketch", | |
| description: "Create or update the P5.js sketch with new code. The sketch will run in a full-window canvas.", | |
| parameters: { | |
| type: SchemaType.OBJECT, | |
| properties: { | |
| sketch: { | |
| type: SchemaType.STRING, | |
| description: "Complete P5.js sketch code including all variable declarations, setup(), draw(), and any additional functions needed. The code should be a complete, self-contained sketch." | |
| } | |
| }, | |
| required: ["sketch"] | |
| } | |
| } | |
| ], | |
| }, | |
| ], | |
| }); | |
| }, [setConfig]); | |
| useEffect(() => { | |
| const onToolCall = async (toolCall: ToolCall) => { | |
| console.log("Received tool call:", toolCall); | |
| for (const fc of toolCall.functionCalls) { | |
| if (fc.name === "updateSketch" && containerRef.current) { | |
| const args = fc.args as SketchArgs; | |
| const result = window.updateSketch(args.sketch, containerRef.current); | |
| // Send the function response back to Gemini | |
| await client.sendToolResponse({ | |
| functionResponses: [ | |
| { | |
| response: { | |
| success: result, | |
| message: result ? "Sketch updated successfully" : "Failed to update sketch" | |
| }, | |
| id: fc.id, | |
| }, | |
| ], | |
| }); | |
| } else { | |
| console.error("Unhandled function call:", fc.name); | |
| } | |
| } | |
| }; | |
| client.on("toolcall", onToolCall); | |
| return () => { | |
| client.off("toolcall", onToolCall); | |
| }; | |
| }, [client]); | |
| return ( | |
| <div | |
| ref={containerRef} | |
| style={{ | |
| width: "100%", | |
| height: "100%", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| display: "flex", | |
| position: "relative", | |
| background: "#000" | |
| }} | |
| /> | |
| ); | |
| } | |
| export const P5Sketch = memo(P5SketchComponent); | |