Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions recipes/javascript/voice-agents/v1/parallel-tool-calling/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Parallel Tool Calling (Voice Agents v1)

Voice agents often need to gather data from multiple sources in a single turn — checking weather while looking up time zones, or querying a database and an external API simultaneously. Parallel tool calling lets the LLM request multiple functions at once, and your client executes them concurrently with `Promise.all`, reducing perceived latency compared to sequential calls.

When the agent decides it needs multiple tools, Deepgram sends a single `FunctionCallRequest` with an array of functions. Your client runs them all in parallel and sends back individual `FunctionCallResponse` messages. The agent then composes all results into one natural response.

## Key parameters

| Parameter | Value | Description |
|-----------|-------|-------------|
| `think.functions` | `[{name, description, parameters}]` | Array of tool definitions the LLM can invoke |
| `think.functions[].parameters` | JSON Schema object | Describes the expected arguments for each tool |
| `FunctionCallRequest.functions` | Array | Contains one or more function calls — multiple means parallel execution |
| `FunctionCallResponse.id` | String | Must match the `id` from the corresponding request |
| `FunctionCallResponse.content` | String | The tool's result, sent back to the agent |

## Example output

```
Settings applied with 3 tools registered
User: What's the weather, time, and population in New York?
Parallel call: 3 function(s) requested
get_weather({"city":"New York"}) → New York: 72°F, sunny
get_time({"timezone":"America/New_York"}) → Current time in America/New_York: 2:30 PM
get_population({"city":"New York"}) → New York population: 8.3 million
Agent: In New York, it's currently 72°F and sunny. The time is 2:30 PM, and the city has a population of about 8.3 million.
```

## Prerequisites

- Node.js 20+
- Set `DEEPGRAM_API_KEY` environment variable
- Install dependencies: `npm install` (from `recipes/javascript/`)

## Run

```bash
node example.js
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { DeepgramClient } from "@deepgram/sdk";

const tools = [
{ name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] } },
{ name: "get_time", description: "Get current time in a timezone", parameters: { type: "object", properties: { timezone: { type: "string" } }, required: ["timezone"] } },
{ name: "get_population", description: "Get population of a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] } },
];
const handlers = {
get_weather: ({ city }) => `${city}: 72°F, sunny`,
get_time: ({ timezone }) => `Current time in ${timezone}: 2:30 PM`,
get_population: ({ city }) => `${city} population: 8.3 million`,
};

async function main() {
const client = new DeepgramClient();
const connection = await client.agent.v1.createConnection();
connection.on("message", async (data) => {
if (data.type === "SettingsApplied") {
console.log("Settings applied with 3 tools registered");
connection.sendInjectUserMessage({ type: "InjectUserMessage", content: "What's the weather, time, and population in New York?" });
} else if (data.type === "FunctionCallRequest") {
console.log(`Parallel call: ${data.functions.length} function(s) requested`);
const results = await Promise.all(data.functions.map(async (fn) => {
const args = JSON.parse(fn.arguments);
const result = handlers[fn.name]?.(args) ?? "Unknown tool";
console.log(` ${fn.name}(${JSON.stringify(args)}) -> ${result}`);
return { id: fn.id, name: fn.name, content: result };
}));
results.forEach((r) => connection.sendFunctionCallResponse({ type: "FunctionCallResponse", ...r }));
} else if (data.type === "ConversationText") {
console.log(`${data.role === "assistant" ? "Agent" : "User"}: ${data.content}`);
}
});
connection.on("error", (err) => console.error("Error:", err));
connection.connect();
await connection.waitForOpen();
connection.sendSettings({
type: "Settings",
audio: { input: { encoding: "linear16", sample_rate: 24000 }, output: { encoding: "linear16", sample_rate: 16000, container: "wav" } },
agent: {
language: "en",
listen: { provider: { type: "deepgram", model: "nova-3" } },
think: { provider: { type: "open_ai", model: "gpt-4o-mini" }, prompt: "You are a helpful assistant. Use all available tools in parallel when the user asks about multiple things.", functions: tools },
speak: { provider: { type: "deepgram", model: "aura-2-thalia-en" } },
},
});
await new Promise((resolve) => setTimeout(resolve, 20000));
connection.close();
}
main().catch(console.error);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, it } from "node:test";
import { ok } from "node:assert";
import { execSync } from "node:child_process";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));

describe("voice-agent-parallel-tool-calling", () => {
it("runs without error and produces output", () => {
const output = execSync("node example.js", {
cwd: __dirname,
timeout: 60000,
env: process.env,
encoding: "utf8",
});
ok(output.trim().length > 0, "Expected non-empty output");
});
});
Loading