Example: Function calling

View as MarkdownOpen in Claude

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

⬇️ Jump to the Full VoxEngine scenario.

Prerequisites

  • Set up an inbound entrypoint for the caller.
  • Create a routing rule that points the destination to this scenario.
  • Store your Gemini API key in Voximplant ApplicationStorage under GEMINI_API_KEY.

Tool definitions

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.
  • Tool implementations in the example are stubs. Replace with real integrations as needed.

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