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
67 changes: 67 additions & 0 deletions recipes/javascript/voice-agents/v1/conversation-export/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Conversation Export to JSON, SRT, and WebVTT (Voice Agents v1)

Export a voice agent conversation to three industry-standard transcript formats in real time.

## What it does

Connects to a Deepgram voice agent session and accumulates every `ConversationText` event (both user and agent turns) with elapsed timestamps. When the session ends, the collected turns are formatted and printed as:

- **JSON** — structured array with role, content, and millisecond timestamps for database storage or search indexing
- **SRT** — numbered subtitle cues with `HH:MM:SS,mmm` timestamps and speaker labels, compatible with most video players
- **WebVTT** — W3C web caption format with `<v Speaker>` voice tags for browser-native `<track>` elements

Transcripts are available the moment the conversation ends with no post-processing step required.

## Key parameters

| Parameter | Value | Description |
|-----------|-------|-------------|
| `listen.provider` | `deepgram/nova-3` | Speech recognition model |
| `think.provider` | `open_ai/gpt-4o-mini` | LLM for conversation logic |
| `speak.provider` | `deepgram/aura-2-thalia-en` | TTS voice model |
| `ConversationText` event | `role`, `content` | Captured per turn with elapsed time |

## Example output

```
Agent: Hello! How can I help you today?
User: Tell me about the spacewalk.

=== JSON ===
{
"turns": [
{ "role": "Agent", "content": "Hello! How can I help you today?", "timestamp_ms": 1023 },
{ "role": "User", "content": "Tell me about the spacewalk.", "timestamp_ms": 5210 }
]
}

=== SRT ===
1
00:00:01,023 --> 00:00:05,210
[Agent] Hello! How can I help you today?

2
00:00:05,210 --> 00:00:08,210
[User] Tell me about the spacewalk.

=== WebVTT ===
WEBVTT

00:00:01.023 --> 00:00:05.210
<v Agent>Hello! How can I help you today?

00:00:05.210 --> 00:00:08.210
<v User>Tell me about the spacewalk.
```

## Prerequisites

- Node.js 20+
- Set `DEEPGRAM_API_KEY` environment variable
- Install dependencies: `npm install`

## Run

```bash
node example.js
```
47 changes: 47 additions & 0 deletions recipes/javascript/voice-agents/v1/conversation-export/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { DeepgramClient } from "@deepgram/sdk";

const turns = [], start = Date.now();

function ts(ms) {
const s = Math.floor(ms / 1000), m = Math.floor(s / 60), h = Math.floor(m / 60);
return `${String(h).padStart(2,"0")}:${String(m%60).padStart(2,"0")}:${String(s%60).padStart(2,"0")}.${String(ms%1000).padStart(3,"0")}`;
}
function printExports() {
console.log("=== JSON ===");
console.log(JSON.stringify({ turns: turns.map(t => ({ role: t.role, content: t.content, timestamp_ms: t.ms })) }, null, 2));
console.log("\n=== SRT ===");
turns.forEach((t, i) => { const end = turns[i+1]?.ms ?? t.ms+3000;
console.log(`${i+1}\n${ts(t.ms).replace(".",",")} --> ${ts(end).replace(".",",")}\n[${t.role}] ${t.content}\n`); });
console.log("=== WebVTT ===\nWEBVTT\n");
turns.forEach((t, i) => { const end = turns[i+1]?.ms ?? t.ms+3000;
console.log(`${ts(t.ms)} --> ${ts(end)}\n<v ${t.role}>${t.content}\n`); });
}
async function main() {
const client = new DeepgramClient();
const connection = await client.agent.v1.createConnection();
connection.on("message", (data) => {
if (data.type === "ConversationText") {
const role = data.role === "assistant" ? "Agent" : "User";
turns.push({ role, content: data.content, ms: Date.now() - start });
console.log(`${role}: ${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 friendly AI assistant. Keep responses brief." },
speak: { provider: { type: "deepgram", model: "aura-2-thalia-en" } },
greeting: "Hello! How can I help you today?" },
});
const resp = await fetch("https://dpgr.am/spacewalk.wav");
const buffer = Buffer.from(await resp.arrayBuffer());
for (let i = 0; i < buffer.length; i += 4096) connection.sendMedia(buffer.subarray(i, i + 4096));
await new Promise((r) => setTimeout(r, 15000));
connection.close();
printExports();
}
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-conversation-export", () => {
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