Skip to content
Closed
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
42 changes: 34 additions & 8 deletions agents/nextjs/guardrails/example/app/api/agent/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { ElevenLabsClient, ElevenLabsError } from "@elevenlabs/elevenlabs-js";
import { ClientEvent } from "@elevenlabs/elevenlabs-js/api/types/ClientEvent";
import type { ConversationConfigInput } from "@elevenlabs/elevenlabs-js/api/types/ConversationConfigInput";
import { NextResponse } from "next/server";
Expand All @@ -15,8 +15,13 @@ const SYSTEM_PROMPT = `You are a friendly banking voice assistant for the Eleven
- Do not recommend investments, specific stocks, ETFs, crypto, or portfolio allocations.
- If asked for investment advice, explain briefly that you cannot provide recommendations and suggest speaking with a licensed financial advisor or using official educational resources instead.`;

function requireApiKey(): string | null {
const key = process.env.ELEVENLABS_API_KEY;
return key?.trim() ? key : null;
}

function getClient() {
const apiKey = process.env.ELEVENLABS_API_KEY;
const apiKey = requireApiKey();
if (!apiKey) {
return {
error: NextResponse.json(
Expand All @@ -28,6 +33,23 @@ function getClient() {
return { client: new ElevenLabsClient({ apiKey }) };
}

function apiErrorMessage(err: unknown): string {
if (err instanceof ElevenLabsError) {
return err.message;
}
if (err instanceof Error) {
return err.message;
}
return "An unexpected error occurred.";
}

function apiErrorStatus(err: unknown): number {
if (err instanceof ElevenLabsError && err.statusCode) {
return err.statusCode >= 400 && err.statusCode < 600 ? err.statusCode : 502;
}
return 502;
}

export async function GET(request: Request) {
const { client, error } = getClient();
if (error) return error;
Expand All @@ -47,9 +69,10 @@ export async function GET(request: Request) {
agentName: agent.name,
});
} catch (e) {
const message =
e instanceof Error ? e.message : "Failed to load agent from ElevenLabs.";
return NextResponse.json({ error: message }, { status: 502 });
return NextResponse.json(
{ error: apiErrorMessage(e) },
{ status: apiErrorStatus(e) }
);
}
}

Expand Down Expand Up @@ -106,6 +129,8 @@ export async function POST() {
name: "No investment recommendations",
isEnabled: true,
executionMode: "blocking",
model: "gemini-2.5-flash-lite",
historyMessageCount: 1,
prompt:
"Block any response that recommends investments, suggests specific stocks, ETFs, funds, bonds, crypto, or portfolio allocations, or otherwise gives personalized financial or investment advice. If the agent starts giving investment recommendations, end the conversation immediately.",
triggerAction: { type: "end_call" },
Expand All @@ -127,8 +152,9 @@ export async function POST() {
agentName: DEMO_AGENT_NAME,
});
} catch (e) {
const message =
e instanceof Error ? e.message : "Failed to create agent on ElevenLabs.";
return NextResponse.json({ error: message }, { status: 502 });
return NextResponse.json(
{ error: apiErrorMessage(e) },
{ status: apiErrorStatus(e) }
);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { ElevenLabsClient, ElevenLabsError } from "@elevenlabs/elevenlabs-js";
import { NextResponse } from "next/server";

function requireApiKey(): string | null {
const key = process.env.ELEVENLABS_API_KEY;
return key?.trim() ? key : null;
}

function apiErrorMessage(err: unknown): string {
if (err instanceof ElevenLabsError) {
return err.message;
}
if (err instanceof Error) {
return err.message;
}
return "An unexpected error occurred.";
}

export async function GET(request: Request) {
const apiKey = process.env.ELEVENLABS_API_KEY;
const apiKey = requireApiKey();
if (!apiKey) {
return NextResponse.json(
{ error: "Server misconfiguration: ELEVENLABS_API_KEY is not set." },
Expand All @@ -18,17 +33,18 @@ export async function GET(request: Request) {
);
}

const client = new ElevenLabsClient({ apiKey });

try {
const { signedUrl } =
await client.conversationalAi.conversations.getSignedUrl({
agentId,
});
return NextResponse.json({ signedUrl });
} catch (e) {
const message =
e instanceof Error ? e.message : "Failed to create signed URL.";
return NextResponse.json({ error: message }, { status: 502 });
const client = new ElevenLabsClient({ apiKey });
const res = await client.conversationalAi.conversations.getWebrtcToken({
agentId,
});
return NextResponse.json({ token: res.token });
} catch (err) {
const status =
err instanceof ElevenLabsError && err.statusCode ? err.statusCode : 502;
return NextResponse.json(
{ error: apiErrorMessage(err) },
{ status: status >= 400 && status < 600 ? status : 502 }
);
}
}
9 changes: 4 additions & 5 deletions agents/nextjs/guardrails/example/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,14 @@ function GuardrailsPage({
const res = await fetch(
`/api/conversation-token?agentId=${encodeURIComponent(trimmedId)}`
);
const data: { signedUrl?: string; error?: string } = await res.json();
if (!res.ok || !data.signedUrl) {
setSessionError(data.error ?? "Could not get signed URL.");
const data: { token?: string; error?: string } = await res.json();
if (!res.ok || !data.token) {
setSessionError(data.error ?? "Could not get conversation token.");
return;
}

await startSession({
connectionType: "websocket",
signedUrl: data.signedUrl,
conversationToken: data.token,
});
} catch (error) {
const nextMessage =
Expand Down
2 changes: 1 addition & 1 deletion agents/nextjs/guardrails/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
"pnpm": {
"overrides": {
"livekit-client": "2.19.1"
"livekit-client": "2.16.1"
}
}
}
Loading