Skip to content
Merged
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
8 changes: 4 additions & 4 deletions DOCS/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ export interface StopConversationRequest {

Open the `components/LandingPage.tsx` file and update it with the full implementation. The key additions here are:

- **Module preloading**: Kick off dynamic imports on mount so they're cached when the user clicks "Try it now!" — this eliminates the ~1–2 second import delay
- **Module preloading**: Kick off dynamic imports on mount so they're cached when the user clicks "Try it Now" — this eliminates the ~1–2 second import delay
- **Parallel setup**: Agent invite and RTM login run in `Promise.all` — both only need the token, so there's no reason to run them sequentially
- **RTM client**: Created here and passed down to `ConversationComponent` so the toolkit can subscribe to transcript messages

Expand Down Expand Up @@ -558,7 +558,7 @@ export default function LandingPage() {
const [showConversation, setShowConversation] = useState(false);

// Preload heavy modules on mount so they're already cached when the user
// clicks "Try it now!" — eliminates the dynamic-import delay on first click.
// clicks "Try it Now" — eliminates the dynamic-import delay on first click.
useEffect(() => {
import('agora-rtc-react').catch(() => {});
import('agora-rtm').catch(() => {});
Expand Down Expand Up @@ -692,7 +692,7 @@ export default function LandingPage() {
hover:bg-[#00c2ff] hover:text-black transition-all duration-300 shadow-lg hover:shadow-[#00c2ff]/20
disabled:opacity-50 disabled:cursor-not-allowed text-lg"
>
{isLoading ? 'Starting...' : 'Try it now!'}
{isLoading ? 'Starting...' : 'Try it Now'}
</button>
{error && <p className="mt-4 text-red-500">{error}</p>}
</>
Expand Down Expand Up @@ -807,7 +807,7 @@ Now that we have our basic RTC functionality and token generation working, let's

1. Run the application using `pnpm dev`
2. Open the application in your browser, using the url `http://localhost:3000`
3. Click on the "Try it now!" button
3. Click on the "Try it Now" button
4. You should see the connection status change to "Connected"

## Add Agora's Conversational AI Engine
Expand Down
185 changes: 118 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,135 +1,186 @@
# Agora Conversational AI Next.js Quickstart

Official Next.js quickstart for building a browser-based voice AI experience with Agora Conversational AI Engine.
[![Build](https://github.com/AgoraIO-Conversational-AI/agent-quickstart-nextjs/actions/workflows/build-check.yml/badge.svg)](https://github.com/AgoraIO-Conversational-AI/agent-quickstart-nextjs/actions/workflows/build-check.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org/)

Build a production-style voice agent in minutes with Next.js and the Agora Conversational AI Engine, including voice agent visualizer ([Agent UIKit](https://agoraio-conversational-ai.github.io/agent-uikit/)), live transcript, and real-time pipeline latency via `AGENT_METRICS` ([Agent Toolkit](https://github.com/AgoraIO-Conversational-AI/agent-client-toolkit-ts)).

## Prerequisites

- Node.js 22+
- pnpm
- [Agora CLI](https://www.npmjs.com/package/agoraio-cli)
- Agora App ID and App Certificate with Conversational AI enabled
- [Node.js 22+](https://nodejs.org/en/download/)
- [pnpm](https://pnpm.io/installation)
- [Agora CLI](https://github.com/AgoraIO-Community/cli)

## Run It

1. Login with the Agora CLI and create a project:
`agora login`
`agora project create my-first-voice-agent --feature rtc --feature convoai`
`agora project use my-first-voice-agent`
2. Clone the repo and install dependencies.
3. Copy `env.local.example` to `.env.local`.
4. Fill `.env.local` using the values from `agora project env --with-secrets`.
5. Run `pnpm dev`.
6. Open `http://localhost:3000`.
Getting started is quick and easy: install the CLI _(skip if you already have it)_ , scaffold the Next.js quickstart using the Agora CLI, install dependencies, and run.

1. **Install the Agora CLI and sign in**
_(skip if `agora` is already on your PATH)_:

```bash
curl -fsSL https://raw.githubusercontent.com/AgoraIO/cli/main/install.sh | sh -s -- --add-to-path
agora login
```

2. **Scaffold and run**
`agora init` clones the starter, binds an Agora project, and writes `.env.local`. (replace `my-nextjs-demo` with your own project name):

```bash
agora init my-nextjs-demo --template nextjs
cd my-nextjs-demo
pnpm install
pnpm dev
```

3. Open [http://localhost:3000](http://localhost:3000) and click **Start conversation**.

If the agent does not join or transcripts do not appear, run **`agora project doctor --deep`** to check credentials, feature enablement, network reachability, and local env binding.

### Working from a clone of this repository

Use this path if you already cloned **this** repo (for example to contribute or fork):

```bash
git clone https://github.com/AgoraIO-Conversational-AI/agent-quickstart-nextjs.git
cd agent-quickstart-nextjs
agora login
agora project create my-first-voice-agent --feature rtc --feature convoai
agora project use my-first-voice-agent
agora project use <your-project>
pnpm install
cp env.local.example .env.local
# Map the exported Agora CLI values into the Next.js env names shown in env.local.example
# NEXT_PUBLIC_AGORA_APP_ID=<AGORA_APP_ID from `agora project env --with-secrets`>
# NEXT_AGORA_APP_CERTIFICATE=<AGORA_APP_CERTIFICATE from `agora project env --with-secrets`>
pnpm run doctor
agora project env write .env.local
agora project doctor --deep
pnpm dev
```

Required environment variables:
### Deploy to Vercel

- `NEXT_PUBLIC_AGORA_APP_ID`
- `NEXT_AGORA_APP_CERTIFICATE`
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs&project-name=agent-quickstart-nextjs&repository-name=agent-quickstart-nextjs&env=NEXT_PUBLIC_AGORA_APP_ID,NEXT_AGORA_APP_CERTIFICATE&envDescription=Agora%20credentials%20needed%20to%20run%20the%20app&envLink=https%3A%2F%2Fgithub.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs%23run-it&demo-title=Agora%20Conversational%20AI%20Next.js%20Quickstart&demo-description=Official%20Next.js%20quickstart%20for%20building%20browser-based%20voice%20AI%20with%20Agora&demo-image=https%3A%2F%2Fraw.githubusercontent.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs%2Fmain%2F.github%2Fassets%2FConversation-Ai-Client.gif)

Optional convenience override:
To populate Vercel env vars from your bound Agora project:

- `NEXT_PUBLIC_AGENT_UID` defaults to `123456`
```bash
agora project use <your-project>
agora project env write .env.local
rg "^(NEXT_PUBLIC_AGORA_APP_ID|NEXT_AGORA_APP_CERTIFICATE)=" .env.local
```

The default agent configuration in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts) uses Agora-managed defaults for STT, LLM, and TTS, so no additional vendor API keys are required for the base quickstart.
Copy those two values into Vercel Project Settings -> Environment Variables.

`env.local.example` is the reference for the Next.js-specific variable names. The recommended setup flow is to create/select the Agora project with the CLI, inspect the values with `agora project env --with-secrets`, and copy them into `.env.local` using the names above.
### Environment variables

## Commands
Defined in [`env.local.example`](env.local.example).

```bash
pnpm run doctor
pnpm dev
pnpm run lint
pnpm run typecheck
pnpm run verify:api
pnpm run build
pnpm run verify
```
| Variable | Required | Default | Notes |
| ---------------------------- | :------: | :------: | ---------------------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_AGORA_APP_ID` | ✅ | — | Agora Console → Project → App ID. |
| `NEXT_AGORA_APP_CERTIFICATE` | ✅ | — | Agora Console → Project → App Certificate. **Server-side only.** |
| `NEXT_PUBLIC_AGENT_UID` | | `123456` | Must match the `agentUid` in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts). |
| `NEXT_AGENT_GREETING` | | — | Override the agent's opening line. |

## Verification
The default agent configuration in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts) uses Agora-managed STT, LLM, and TTS, so no extra vendor API keys are required for the base quickstart.

Run this before shipping changes:
> **Default vs BYOK** — the quickstart ships with Agora-managed inference for a zero-key install. Switch to BYOK below when you need to bring your own provider quotas, models, or vendors.

## Commands

```bash
pnpm run verify
# Dev
pnpm dev # start the Next.js dev server

# Quality
pnpm run lint # eslint
pnpm run typecheck # tsc --noEmit
pnpm run doctor # local prereqs + env binding

# CI / pre-ship
pnpm run verify:api # API contract checks
pnpm run build # production build
pnpm run verify # doctor + lint + typecheck + verify:api + build
```

This checks local prerequisites, lint, type safety, the core API route contracts, and the production build.
Run `pnpm run verify` before shipping changes — it covers local prerequisites, lint, type safety, the core API route contracts, and the production build.

## Architecture

<picture>
<source media="(prefers-color-scheme: dark)" srcset="./system-architecture-dark.svg">
<img src="./system-architecture.svg" alt="System architecture" />
<img src="./system-architecture.svg" alt="System architecture">
</picture>

The browser uses the Next.js app for token generation and agent lifecycle calls, and connects to Agora Cloud for real-time audio, transcripts, and agent state.
The browser fetches a combined RTC + RTM token (`buildTokenWithRtm`) from this app, joins the channel using a single RTC client, and uses RTM as the data channel for transcript, agent state, metrics, and error events. The Conversational AI Engine joins the same channel as the configured `NEXT_PUBLIC_AGENT_UID` and runs the STT → LLM → TTS pipeline in Agora Cloud.

## What You Get

- browser voice client built with Next.js App Router
- RTC audio plus RTM transcript and state events
- server routes for token generation, invite, and stop
- `AgentVisualizer` for agent state and `ConvoTextStream` for live transcript UI
- [`AgentVisualizer`](https://agoraio-conversational-ai.github.io/agent-uikit/) for agent state and a built-in transcript panel for live turns
- per-stage latency header driven by `AGENT_METRICS`
- Agora-managed default STT, LLM, and TTS configuration

## How It Works

1. The browser requests an RTC + RTM token from `/api/generate-agora-token`.
2. The backend invites an Agora cloud agent with `/api/invite-agent`.
3. The browser joins the channel and publishes mic audio.
4. The client receives transcript and agent state updates over RTM.
5. The session is stopped with `/api/stop-conversation`.
4. The client receives transcript, agent state, and `AGENT_METRICS` (per-stage latency) events over RTM.
5. On end, the client unpublishes and stops the local microphone track, then calls `/api/stop-conversation` to terminate the agent session.

## Optional BYOK

Optional BYOK examples remain commented in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts).
The quickstart defaults to Agora-managed inference. To bring your own keys, uncomment the matching blocks in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts) and add the corresponding env vars.

Examples:

- `NEXT_LLM_URL` and `NEXT_LLM_API_KEY`
- `NEXT_DEEPGRAM_API_KEY`
- `NEXT_ELEVENLABS_API_KEY` and `NEXT_ELEVENLABS_VOICE_ID`
```bash
# Deepgram STT
NEXT_DEEPGRAM_API_KEY=...

## Deploy
# OpenAI-compatible LLM
NEXT_LLM_URL=https://api.openai.com/v1/chat/completions
NEXT_LLM_API_KEY=...

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs&project-name=agent-quickstart-nextjs&repository-name=agent-quickstart-nextjs&env=NEXT_PUBLIC_AGORA_APP_ID,NEXT_AGORA_APP_CERTIFICATE&envDescription=Agora%20credentials%20needed%20to%20run%20the%20app&envLink=https%3A%2F%2Fgithub.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs%23run-it&demo-title=Agora%20Conversational%20AI%20Next.js%20Quickstart&demo-description=Official%20Next.js%20quickstart%20for%20building%20browser-based%20voice%20AI%20with%20Agora&demo-image=https%3A%2F%2Fraw.githubusercontent.com%2FAgoraIO-Conversational-AI%2Fagent-quickstart-nextjs%2Fmain%2F.github%2Fassets%2FConversation-Ai-Client.gif)
# ElevenLabs TTS
NEXT_ELEVENLABS_API_KEY=...
NEXT_ELEVENLABS_VOICE_ID=...
```

## Repo Map

- `app/api/generate-agora-token/route.ts` issues RTC + RTM tokens
- `app/api/invite-agent/route.ts` starts the agent session
- `app/api/stop-conversation/route.ts` stops the agent session
- `components/LandingPage.tsx` starts the session and manages RTM login
- `components/ConversationComponent.tsx` manages RTC, transcript state, `AgentVisualizer`, and `ConvoTextStream`
- `AGENTS.md` is the primary agent-facing guide
- `app/api/generate-agora-token/route.ts` — issues RTC + RTM tokens
- `app/api/invite-agent/route.ts` — starts the agent session and configures the pipeline
- `app/api/stop-conversation/route.ts` — stops the agent session
- `components/LandingPage.tsx` — entry point: token fetch, RTM login, conversation lifecycle
- `components/ConversationComponent.tsx` — RTC client, transcript state, `AGENT_METRICS`, mic release
- `components/QuickstartConversationLayout.tsx` — in-call header, transcript rail, controls dock
- `components/QuickstartPipelineMetrics.tsx` — per-stage latency chips in the header
- `components/QuickstartTranscriptPanel.tsx` — live transcript rail
- `components/QuickstartPreCallCard.tsx` — pre-call hero card
- `lib/conversation.ts` — transcript normalization and visualizer state mapping
- `AGENTS.md` — primary agent-facing guide

## Troubleshooting

- `pnpm run doctor` fails: copy `env.local.example` to `.env.local` and set `NEXT_PUBLIC_AGORA_APP_ID` plus `NEXT_AGORA_APP_CERTIFICATE`
- Unsure where the values come from: run `agora project use my-first-voice-agent` and `agora project env --with-secrets`
- RTM login fails: confirm the token route still uses `RtcTokenBuilder.buildTokenWithRtm`
- Transcript speakers are inverted: check the `uid === "0"` remap logic in `components/ConversationComponent.tsx`
- Agent never appears in channel: confirm `NEXT_PUBLIC_AGENT_UID` matches the value used in `app/api/invite-agent/route.ts`
- **Agent does not join or transcripts are missing:** run `agora project doctor --deep`.
- **`pnpm run doctor` fails:** run `agora project env write .env.local`, then retry.
- **Manual clone / env values:** `agora project use <your-project>` then `agora project env write .env.local`.
- **RTM login fails:** keep [`app/api/generate-agora-token/route.ts`](app/api/generate-agora-token/route.ts) on `RtcTokenBuilder.buildTokenWithRtm` — RTC-only tokens will not satisfy `rtm.login`.
- **Transcript speakers inverted:** check the `uid === "0"` remap in [`components/ConversationComponent.tsx`](components/ConversationComponent.tsx).
- **Agent never appears in channel:** ensure `NEXT_PUBLIC_AGENT_UID` matches the value used in [`app/api/invite-agent/route.ts`](app/api/invite-agent/route.ts).

## More Docs

- [DOCS/GUIDE.md](./DOCS/GUIDE.md)
- [DOCS/TEXT_STREAMING_GUIDE.md](./DOCS/TEXT_STREAMING_GUIDE.md)
- [AGENTS.md](./AGENTS.md)
- [Agent UIKit Preview](https://agoraio-conversational-ai.github.io/agent-uikit/)

## Contributing

Pull requests welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and conventions.

## Security

Please do **not** open public issues for security reports. Email security@agora.io with details and reproduction steps.

## License

Released under the [MIT License](./LICENSE).
16 changes: 9 additions & 7 deletions app/api/generate-agora-token/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ export async function GET(request: NextRequest) {
if (!APP_ID || !APP_CERTIFICATE) {
return NextResponse.json(
{ error: 'Agora credentials are not set' },
{ status: 500 }
{ status: 500 },
);
}

const { searchParams } = new URL(request.url);
const uidStr = searchParams.get('uid') || '0';
const parsedUid = parseInt(uidStr, 10);
const uid = isNaN(parsedUid) ? 0 : parsedUid;
const uidStr = searchParams.get('uid');
const parsedUid = uidStr ? parseInt(uidStr, 10) : Number.NaN;
const uid = Number.isNaN(parsedUid)
? Math.floor(Math.random() * 9_999_000) + 1000
: parsedUid;
const channelName = searchParams.get('channel') || generateChannelName();

const expirationTime =
Expand All @@ -36,10 +38,10 @@ export async function GET(request: NextRequest) {
APP_ID,
APP_CERTIFICATE,
channelName,
uid,
uid.toString(),
RtcRole.PUBLISHER,
expirationTime,
expirationTime
expirationTime,
);
// console.log('Token generated successfully (RTC + RTM)');

Expand All @@ -55,7 +57,7 @@ export async function GET(request: NextRequest) {
error: 'Failed to generate Agora token',
details: error instanceof Error ? error.message : String(error),
},
{ status: 500 }
{ status: 500 },
);
}
}
7 changes: 6 additions & 1 deletion app/api/invite-agent/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ export async function POST(request: NextRequest) {
// Required for browser RTM events:
// - data_channel: 'rtm' enables RTM delivery path for state/metrics/errors
// - enable_error_message emits AGENT_ERROR payloads
parameters: { data_channel: 'rtm', enable_error_message: true },
// - enable_metrics emits AGENT_METRICS latency payloads
parameters: {
data_channel: 'rtm',
enable_error_message: true,
enable_metrics: true,
},
})
.withStt(
new DeepgramSTT({
Expand Down
Loading
Loading