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 { FunctionDeclaration, SchemaType } from "@google/generative-ai"; | |
| import { useEffect, useRef, useState, memo } from "react"; | |
| import vegaEmbed from "vega-embed"; | |
| import { useLiveAPIContext } from "../../contexts/LiveAPIContext"; | |
| import { ToolCall } from "../../multimodal-live-types"; | |
| function P5SketchComponent() { | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| const [jsonString, setJSONString] = useState<string>(""); | |
| const { client, setConfig } = useLiveAPIContext(); | |
| useEffect(() => { | |
| const w = window as any; | |
| if (containerRef.current && w.initSketch) { | |
| w.initSketch(containerRef.current); | |
| } | |
| }, [containerRef]); | |
| useEffect(() => { | |
| setConfig({ | |
| model: "models/gemini-2.0-flash-exp", | |
| generationConfig: { | |
| responseModalities: "text", | |
| //responseModalities: "audio", | |
| /* | |
| speechConfig: { | |
| voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kobe" } }, | |
| }, | |
| */ | |
| }, | |
| systemInstruction: { | |
| parts: [ | |
| { | |
| text: `We have circles on a canvas that we can move and resize. Every time that I ask you something, I want you to use the "get_circles" function to understand where the circles are. Once you receive the response for "get_circles" I want you to use "change_circle" to modify one or multiple circle properties to accomplish our mutual goal. Every time I ask you ask you to do something you should call "get_circles" then "change_circle" after evaluating the response. | |
| The circles blend additively using screen mode, so if I ask you mix colors, you can make the color by moving circles to the same position to blend their colors. | |
| ` | |
| }, | |
| ], | |
| }, | |
| tools: [ | |
| // there is a free-tier quota for search | |
| { googleSearch: {} }, | |
| { | |
| functionDeclarations: [ | |
| { | |
| name: "get_circles", | |
| description: "Get a list of circles in my application", | |
| }, | |
| { | |
| name: "change_circle", | |
| description: "Change one of the circles", | |
| parameters: { | |
| type: SchemaType.OBJECT, | |
| properties: { | |
| color: { | |
| type: SchemaType.STRING, | |
| }, | |
| x: { | |
| type: SchemaType.NUMBER, | |
| }, | |
| y: { | |
| type: SchemaType.NUMBER, | |
| }, | |
| radius: { | |
| type: SchemaType.NUMBER, | |
| }, | |
| }, | |
| required: ["color", "x", "y", "radius"], | |
| }, | |
| }, | |
| ], | |
| }, | |
| ], | |
| }); | |
| }, [setConfig]); | |
| useEffect(() => { | |
| const onToolCall = (toolCall: ToolCall) => { | |
| console.log(`got toolcall`, toolCall); | |
| toolCall.functionCalls.forEach((fc) => { | |
| const w = window as any; | |
| const func: Function = w[fc.name]; | |
| if (fc.name === "get_circles") { | |
| setTimeout( | |
| () => | |
| client.sendToolResponse({ | |
| functionResponses: [ | |
| { | |
| response: w.get_circles(), | |
| id: fc.id, | |
| }, | |
| ], | |
| }), | |
| 500, | |
| ); | |
| } else if (func && typeof func === "function") { | |
| func(fc.args); | |
| client.sendToolResponse({ | |
| functionResponses: [ | |
| { | |
| response: { | |
| content: { ...w.get_circles() }, | |
| }, | |
| id: fc.id, | |
| }, | |
| ], | |
| }); | |
| } else { | |
| console.error(`Unhandled function call for ${fc.name}`); | |
| } | |
| }); | |
| }; | |
| client.on("toolcall", onToolCall); | |
| return () => { | |
| client.off("toolcall", onToolCall); | |
| }; | |
| }, [client]); | |
| const embedRef = useRef<HTMLDivElement>(null); | |
| useEffect(() => { | |
| if (embedRef.current && jsonString) { | |
| vegaEmbed(embedRef.current, JSON.parse(jsonString)); | |
| } | |
| }, [embedRef, jsonString]); | |
| return ( | |
| <div | |
| ref={containerRef} | |
| style={{ | |
| width: "100vw", | |
| height: "100vh", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| display: "flex", | |
| }} | |
| /> | |
| ); | |
| } | |
| export const P5Sketch = memo(P5SketchComponent); | |