Example: Function calling

View as Markdown

For the complete documentation index, see llms.txt.

This example answers an inbound call, enables Gemini tool calling, and sends tool responses back to the model.

⬇️ Jump to the Full VoxEngine scenario.

Gemini 3.1 Flash Live Preview

This page reflects the current gemini-3.1-flash-live-preview flow from Google’s Live API docs: https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-live-preview

Prerequisites

Tool definitions

The full example also uses thinkingConfig: { thinkingLevel: "minimal" } to keep tool-calling turns responsive.

The example registers tools in connectConfig.tools using functionDeclarations and JSON Schema parameters:

1const TOOLS = [
2 {
3 functionDeclarations: [
4 {
5 name: "get_weather",
6 description: "Get current weather for a location (demo stub)",
7 parametersJsonSchema: {
8 type: "object",
9 properties: {
10 location: { type: "string" },
11 },
12 required: ["location"],
13 },
14 },
15 ],
16 },
17];

Handling tool calls

Gemini sends tool calls via Gemini.LiveAPIEvents.ToolCall. Respond with sendToolResponse:

1geminiLiveAPIClient.addEventListener(Gemini.LiveAPIEvents.ToolCall, (event) => {
2 const functionCalls = event?.data?.payload?.functionCalls || [];
3 const responses = functionCalls.map((fn) => ({
4 id: fn.id,
5 name: fn.name,
6 response: { output: { ok: true } },
7 }));
8 geminiLiveAPIClient.sendToolResponse({ functionResponses: responses });
9});

Notes

  • The example uses the Gemini Developer API (Gemini.Backend.GEMINI_API), not Vertex AI.
  • The current sample uses gemini-3.1-flash-live-preview.
  • Tool implementations in the example are stubs. Replace with real integrations as needed.
Gemini 2.5 compatibility

If you are updating a 2.5 Gemini Live function-calling sample, switch the startup prompt from sendClientContent(...) to sendRealtimeInput(...) for 3.1. Also replace the older thinkingBudget field with thinkingLevel.

See the VoxEngine API Reference for more details.

Full VoxEngine scenario

voxeengine-gemini-function-calling.js
1/**
2 * Voximplant + Gemini Live API connector demo
3 * Scenario: answer an incoming call and handle Gemini tool calls.
4 */
5
6require(Modules.Gemini);
7const SYSTEM_INSTRUCTION = `
8You are Voxi, a helpful voice assistant for phone callers.
9Keep responses short and telephony-friendly (usually 1-2 sentences).
10If the caller asks about the weather, call the get_weather tool.
11If the caller wants to end the call, call the hangup_call tool.
12`;
13
14const CONNECT_CONFIG = {
15 responseModalities: ["AUDIO"],
16 thinkingConfig: {thinkingLevel: "minimal"},
17 systemInstruction: {
18 parts: [{text: SYSTEM_INSTRUCTION}],
19 },
20 tools: [
21 {
22 functionDeclarations: [
23 {
24 name: "get_weather",
25 description: "Get current weather for a location (demo stub)",
26 parametersJsonSchema: {
27 type: "object",
28 properties: {
29 location: {
30 type: "string",
31 description: "City name, for example: San Francisco",
32 },
33 },
34 required: ["location"],
35 },
36 },
37 {
38 name: "hangup_call",
39 description: "Hang up the current call",
40 parametersJsonSchema: {
41 type: "object",
42 properties: {},
43 required: [],
44 },
45 },
46 ],
47 },
48 ],
49 inputAudioTranscription: {},
50 outputAudioTranscription: {},
51};
52
53VoxEngine.addEventListener(AppEvents.CallAlerting, async ({call}) => {
54 let voiceAIClient;
55
56 // Termination functions - add cleanup and logging as needed
57 call.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
58 call.addEventListener(CallEvents.Failed, VoxEngine.terminate);
59
60 try {
61 call.answer();
62
63 voiceAIClient = await Gemini.createLiveAPIClient({
64 apiKey: VoxEngine.getSecretValue('GEMINI_API_KEY'),
65 model: "gemini-3.1-flash-live-preview",
66 backend: Gemini.Backend.GEMINI_API,
67 connectConfig: CONNECT_CONFIG,
68 onWebSocketClose: (event) => {
69 Logger.write("===Gemini.WebSocket.Close===");
70 if (event) Logger.write(JSON.stringify(event));
71 VoxEngine.terminate();
72 },
73 });
74
75 voiceAIClient.addEventListener(Gemini.LiveAPIEvents.SetupComplete, () => {
76 VoxEngine.sendMediaBetween(call, voiceAIClient);
77 voiceAIClient.sendRealtimeInput({
78 text: "Say hello and ask how you can help.",
79 });
80 });
81
82 voiceAIClient.addEventListener(Gemini.LiveAPIEvents.ToolCall, (event) => {
83 const functionCalls = event?.data?.payload?.functionCalls || [];
84 if (!functionCalls.length) return;
85
86 const responses = functionCalls.map((fn) => {
87 const {id, name, args} = fn || {};
88 if (!id || !name) return null;
89
90 if (name === "get_weather") {
91 const location = args?.location || "Unknown";
92 return {
93 id,
94 name,
95 response: {
96 output: {
97 location,
98 temperature_f: 72,
99 condition: "sunny",
100 },
101 },
102 };
103 }
104
105 if (name === "hangup_call") {
106 // hang up after 5 seconds to allow message playback
107 setTimeout(()=>{
108 call.hangup();
109 VoxEngine.terminate();
110 }, 5_000);
111 return {
112 id,
113 name,
114 response: {
115 output: {
116 result: "Hanging up now. Goodbye!",
117 },
118 },
119 };
120 }
121
122 return {
123 id,
124 name,
125 response: {
126 error: `Unhandled tool: ${name}`,
127 },
128 };
129 }).filter(Boolean);
130
131 if (responses.length) {
132 voiceAIClient.sendToolResponse({
133 functionResponses: responses,
134 });
135 }
136 });
137
138 // handle barge-in
139 voiceAIClient.addEventListener(Gemini.LiveAPIEvents.ServerContent, (event) => {
140 const payload = event?.data?.payload || {};
141 if (payload.interrupted) {
142 Logger.write("===BARGE-IN=== Gemini.LiveAPIEvents.ServerContent");
143 voiceAIClient.clearMediaBuffer();
144 }
145 });
146
147 [
148 Gemini.LiveAPIEvents.SetupComplete,
149 Gemini.LiveAPIEvents.ServerContent,
150 Gemini.LiveAPIEvents.ToolCall,
151 Gemini.LiveAPIEvents.ToolCallCancellation,
152 Gemini.LiveAPIEvents.ConnectorInformation,
153 Gemini.LiveAPIEvents.Unknown,
154 Gemini.Events.WebSocketMediaStarted,
155 Gemini.Events.WebSocketMediaEnded,
156 ].forEach((eventName) => {
157 voiceAIClient.addEventListener(eventName, (event) => {
158 Logger.write(`===${event.name}===`);
159 if (event?.data) Logger.write(JSON.stringify(event.data));
160 });
161 });
162 } catch (error) {
163 Logger.write("===SOMETHING_WENT_WRONG===");
164 Logger.write(error);
165 voiceAIClient?.close();
166 VoxEngine.terminate();
167 }
168});