diff --git a/agents/nextjs/guardrails/example/app/api/agent/route.ts b/agents/nextjs/guardrails/example/app/api/agent/route.ts index a0739156..4b5fad06 100644 --- a/agents/nextjs/guardrails/example/app/api/agent/route.ts +++ b/agents/nextjs/guardrails/example/app/api/agent/route.ts @@ -1,6 +1,6 @@ import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; -import type { ConversationConfig } from "@elevenlabs/elevenlabs-js/api/types/ConversationConfig"; import { ClientEvent } from "@elevenlabs/elevenlabs-js/api/types/ClientEvent"; +import type { ConversationConfigInput } from "@elevenlabs/elevenlabs-js/api/types/ConversationConfigInput"; import { NextResponse } from "next/server"; const DEMO_AGENT_NAME = "Guardrails Demo Voice"; @@ -57,7 +57,7 @@ export async function POST() { const { client, error } = getClient(); if (error) return error; - const clientEvents: NonNullable = [ + const clientEvents: NonNullable = [ ClientEvent.Audio, ClientEvent.Interruption, ClientEvent.UserTranscript, diff --git a/agents/nextjs/guardrails/example/app/api/conversation-token/route.ts b/agents/nextjs/guardrails/example/app/api/conversation-token/route.ts index ecd8e313..b027a0e8 100644 --- a/agents/nextjs/guardrails/example/app/api/conversation-token/route.ts +++ b/agents/nextjs/guardrails/example/app/api/conversation-token/route.ts @@ -21,14 +21,14 @@ export async function GET(request: Request) { const client = new ElevenLabsClient({ apiKey }); try { - const { token } = - await client.conversationalAi.conversations.getWebrtcToken({ + const { signedUrl } = + await client.conversationalAi.conversations.getSignedUrl({ agentId, }); - return NextResponse.json({ token }); + return NextResponse.json({ signedUrl }); } catch (e) { const message = - e instanceof Error ? e.message : "Failed to create conversation token."; + e instanceof Error ? e.message : "Failed to create signed URL."; return NextResponse.json({ error: message }, { status: 502 }); } } diff --git a/agents/nextjs/guardrails/example/app/page.tsx b/agents/nextjs/guardrails/example/app/page.tsx index 179f71ad..d2b747b5 100644 --- a/agents/nextjs/guardrails/example/app/page.tsx +++ b/agents/nextjs/guardrails/example/app/page.tsx @@ -112,14 +112,15 @@ function GuardrailsPage({ const res = await fetch( `/api/conversation-token?agentId=${encodeURIComponent(trimmedId)}` ); - const data: { token?: string; error?: string } = await res.json(); - if (!res.ok || !data.token) { - setSessionError(data.error ?? "Could not get conversation token."); + const data: { signedUrl?: string; error?: string } = await res.json(); + if (!res.ok || !data.signedUrl) { + setSessionError(data.error ?? "Could not get signed URL."); return; } await startSession({ - conversationToken: data.token, + connectionType: "websocket", + signedUrl: data.signedUrl, }); } catch (error) { const nextMessage = @@ -195,11 +196,11 @@ function GuardrailsPage({

{lookupError}

) : null} -
+

Try asking for investment advice

-

+

Example questions: "What should I invest ten thousand dollars in right now?" or "Should I buy Bitcoin or index funds this month?" If the agent crosses the line into investment diff --git a/agents/nextjs/guardrails/example/package.json b/agents/nextjs/guardrails/example/package.json index c2100a50..07b142e4 100644 --- a/agents/nextjs/guardrails/example/package.json +++ b/agents/nextjs/guardrails/example/package.json @@ -9,32 +9,32 @@ "lint": "eslint" }, "dependencies": { - "@elevenlabs/elevenlabs-js": "^2.43.0", - "@elevenlabs/react": "^1.1.0", + "@elevenlabs/elevenlabs-js": "^2.51.0", + "@elevenlabs/react": "^1.6.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^0.575.0", - "next": "16.1.6", + "lucide-react": "^1.17.0", + "next": "16.2.7", "radix-ui": "^1.4.3", - "react": "19.2.3", - "react-dom": "19.2.3", - "tailwind-merge": "^3.5.0" + "react": "19.2.7", + "react-dom": "19.2.7", + "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@tailwindcss/postcss": "^4", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "16.1.6", - "shadcn": "^3.8.5", - "tailwindcss": "^4", + "@tailwindcss/postcss": "^4.3.0", + "@types/node": "^25.9.1", + "@types/react": "^19.2.16", + "@types/react-dom": "^19.2.3", + "eslint": "^9.39.4", + "eslint-config-next": "16.2.7", + "shadcn": "^4.10.0", + "tailwindcss": "^4.3.0", "tw-animate-css": "^1.4.0", - "typescript": "^5" + "typescript": "^6.0.3" }, "pnpm": { "overrides": { - "livekit-client": "2.16.1" + "livekit-client": "2.19.1" } } } diff --git a/agents/nextjs/guardrails/setup.sh b/agents/nextjs/guardrails/setup.sh index c3c8ea18..786ae1b9 100755 --- a/agents/nextjs/guardrails/setup.sh +++ b/agents/nextjs/guardrails/setup.sh @@ -35,7 +35,7 @@ node -e " delete pkg.dependencies['@elevenlabs/client']; pkg.pnpm = pkg.pnpm || {}; pkg.pnpm.overrides = pkg.pnpm.overrides || {}; - pkg.pnpm.overrides['livekit-client'] = '2.16.1'; + pkg.pnpm.overrides['livekit-client'] = '2.19.1'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); " diff --git a/speech-engine/nextjs/quickstart/PROMPT.md b/speech-engine/nextjs/quickstart/PROMPT.md index 68f5b711..15cc7a3d 100644 --- a/speech-engine/nextjs/quickstart/PROMPT.md +++ b/speech-engine/nextjs/quickstart/PROMPT.md @@ -27,7 +27,7 @@ Before writing any code, invoke the `/speech-engine` skill to learn the correct ## 6. `lib/assistant.ts` -- Shared OpenAI Responses API helpers: `ASSISTANT_INSTRUCTIONS`, `VOICE_FIRST_MESSAGE`, `isChatRole`, `normalizeChatMessages`, `transcriptToChatMessages`, `createAssistantReply(messages, signal)`, and `createAssistantStream(messages, signal)`. +- Shared OpenAI Responses API helpers: `ASSISTANT_INSTRUCTIONS`, `isChatRole`, `normalizeChatMessages`, `transcriptToChatMessages`, `createAssistantReply(messages, signal)`, and `createAssistantStream(messages, signal)`. - Map Speech Engine `agent` role to OpenAI `assistant`; keep responses concise. - Normalize history before sending it to OpenAI: trim content, drop empty messages, cap message count and per-message length. @@ -74,7 +74,7 @@ Before writing any code, invoke the `/speech-engine` skill to learn the correct - Build a single chat UI that works like a normal assistant chat first, then can switch into voice mode without losing context. - Keep one `messages` state array with `{ id, role: "user" | "assistant", content, channel: "chat" | "voice", pending?, error? }`. - Text submit path: append the user message, POST the full normalized history to `/api/chat`, replace the pending assistant message with the reply, and keep the composer usable. -- Voice start path: request microphone access, POST current non-pending/non-error chat history to `/api/voice-history`, fetch `/api/token`, then `startSession({ conversationToken: token, overrides: { agent: { firstMessage: VOICE_FIRST_MESSAGE } }, onConversationCreated })`. +- Voice start path: request microphone access, POST current non-pending/non-error chat history to `/api/voice-history`, fetch `/api/token`, then start the voice session with an inline `overrides.agent.firstMessage` greeting. - In `onConversationCreated`, link the stored `historyId` to `voiceConversation.getId()` with `/api/voice-history/link`; if useful, also send a contextual update summarizing recent typed chat. - Voice event path: use `useConversation({ onMessage })` to append or update transcript messages in the same `messages` array, mapping SDK role `agent` to local role `assistant`. - While voice is connected, allow typed messages too: append them to the same thread and call `conversation.sendUserMessage(content)`; avoid duplicating echoes from `onMessage`. diff --git a/speech-engine/nextjs/quickstart/example/app/page.tsx b/speech-engine/nextjs/quickstart/example/app/page.tsx index e818cae0..e3dd9489 100644 --- a/speech-engine/nextjs/quickstart/example/app/page.tsx +++ b/speech-engine/nextjs/quickstart/example/app/page.tsx @@ -11,8 +11,6 @@ import { type KeyboardEvent, } from "react"; -import { VOICE_FIRST_MESSAGE } from "@/lib/assistant-constants"; - type DemoRole = "user" | "assistant"; type DemoChannel = "chat" | "voice"; @@ -379,7 +377,7 @@ function ConversationView() { conversationToken: token, overrides: { agent: { - firstMessage: VOICE_FIRST_MESSAGE, + firstMessage: "Hello! How can I help you today?", }, }, onConversationCreated: voiceConversation => { diff --git a/speech-engine/nextjs/quickstart/example/lib/assistant-constants.ts b/speech-engine/nextjs/quickstart/example/lib/assistant-constants.ts deleted file mode 100644 index ffda1c93..00000000 --- a/speech-engine/nextjs/quickstart/example/lib/assistant-constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const VOICE_FIRST_MESSAGE = "Hello! How can I help you today?";