Example: Function calling
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
ApplicationStorageunderGEMINI_API_KEY.
Tool definitions
The example registers tools in connectConfig.tools using functionDeclarations and JSON Schema parameters:
1 const 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:
1 geminiLiveAPIClient.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 6 require(Modules.Gemini); 7 require(Modules.ApplicationStorage); 8 9 const SYSTEM_INSTRUCTION = ` 10 You are Voxi, a helpful voice assistant for phone callers. 11 Keep responses short and telephony-friendly (usually 1-2 sentences). 12 If the caller asks about the weather, call the get_weather tool. 13 If the caller wants to end the call, call the hangup_call tool. 14 `; 15 16 const 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 54 VoxEngine.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 });