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 { type 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"; | |
const declaration: FunctionDeclaration = { | |
name: "render_altair", | |
description: "Displays an altair graph in json format.", | |
parameters: { | |
type: SchemaType.OBJECT, | |
properties: { | |
json_graph: { | |
type: SchemaType.STRING, | |
description: | |
"JSON STRING representation of the graph to render. Must be a string, not a json object", | |
}, | |
}, | |
required: ["json_graph"], | |
}, | |
}; | |
function AltairComponent() { | |
const [jsonString, setJSONString] = useState<string>(""); | |
const { client, setConfig } = useLiveAPIContext(); | |
useEffect(() => { | |
setConfig({ | |
model: "models/gemini-2.0-flash-exp", | |
generationConfig: { | |
responseModalities: "audio", | |
speechConfig: { | |
voiceConfig: { prebuiltVoiceConfig: { voiceName: "Aoede" } }, | |
}, | |
}, | |
systemInstruction: { | |
parts: [ | |
{ | |
text: 'You are my helpful assistant. Any time I ask you for a graph call the "render_altair" function I have provided you. Dont ask for additional information just make your best judgement.', | |
}, | |
], | |
}, | |
tools: [ | |
// there is a free-tier quota for search | |
{ googleSearch: {} }, | |
{ functionDeclarations: [declaration] }, | |
], | |
}); | |
}, [setConfig]); | |
useEffect(() => { | |
const onToolCall = (toolCall: ToolCall) => { | |
console.log(`got toolcall`, toolCall); | |
const fc = toolCall.functionCalls.find( | |
(fc) => fc.name === declaration.name, | |
); | |
if (fc) { | |
const str = (fc.args as any).json_graph; | |
setJSONString(str); | |
} | |
// send data for the response of your tool call | |
// in this case Im just saying it was successful | |
if (toolCall.functionCalls.length) { | |
setTimeout( | |
() => | |
client.sendToolResponse({ | |
functionResponses: toolCall.functionCalls.map((fc) => ({ | |
response: { output: { success: true } }, | |
id: fc.id, | |
})), | |
}), | |
200, | |
); | |
} | |
}; | |
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 className="vega-embed" ref={embedRef} />; | |
} | |
export const Altair = memo(AltairComponent); | |