99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
import { randomUUID } from "node:crypto"
|
|
import { AccessToken, RoomAgentDispatch, RoomConfiguration } from "livekit-server-sdk"
|
|
import {
|
|
getVoiceAssistantAgentName,
|
|
getVoiceAssistantBootstrap,
|
|
getVoiceAssistantSiteUrl,
|
|
} from "@/lib/voice-assistant/shared"
|
|
import type {
|
|
LiveKitTokenResponse,
|
|
VoiceAssistantVisitorMetadata,
|
|
} from "@/lib/voice-assistant/types"
|
|
import { VOICE_ASSISTANT_SOURCE } from "@/lib/voice-assistant/types"
|
|
|
|
function readRequiredEnv(name: string) {
|
|
const value = process.env[name]
|
|
|
|
if (!value) {
|
|
throw new Error(`Missing required voice assistant environment variable: ${name}`)
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
export function getVoiceAssistantServerEnv() {
|
|
const livekitUrl = readRequiredEnv("LIVEKIT_URL")
|
|
const livekitApiKey = readRequiredEnv("LIVEKIT_API_KEY")
|
|
const livekitApiSecret = readRequiredEnv("LIVEKIT_API_SECRET")
|
|
const xaiApiKey = readRequiredEnv("XAI_API_KEY")
|
|
const realtimeModel = process.env.XAI_REALTIME_MODEL || "grok-4-1-fast-non-reasoning"
|
|
|
|
return {
|
|
livekitUrl,
|
|
livekitApiKey,
|
|
livekitApiSecret,
|
|
xaiApiKey,
|
|
realtimeModel,
|
|
siteUrl: getVoiceAssistantSiteUrl(),
|
|
}
|
|
}
|
|
|
|
function buildRoomName() {
|
|
return `rmv-voice-${randomUUID().slice(0, 8)}`
|
|
}
|
|
|
|
function buildParticipantIdentity() {
|
|
return `website-visitor-${randomUUID().slice(0, 10)}`
|
|
}
|
|
|
|
function buildVisitorMetadata(pathname: string): VoiceAssistantVisitorMetadata {
|
|
const siteUrl = getVoiceAssistantSiteUrl()
|
|
const safePathname = pathname.startsWith("/") ? pathname : `/${pathname}`
|
|
|
|
return {
|
|
source: VOICE_ASSISTANT_SOURCE,
|
|
pathname: safePathname,
|
|
pageUrl: new URL(safePathname, siteUrl).toString(),
|
|
startedAt: new Date().toISOString(),
|
|
}
|
|
}
|
|
|
|
export async function createVoiceAssistantTokenResponse(pathname: string): Promise<LiveKitTokenResponse> {
|
|
const env = getVoiceAssistantServerEnv()
|
|
const roomName = buildRoomName()
|
|
const participantIdentity = buildParticipantIdentity()
|
|
const visitorMetadata = buildVisitorMetadata(pathname)
|
|
const agentName = getVoiceAssistantAgentName()
|
|
|
|
const token = new AccessToken(env.livekitApiKey, env.livekitApiSecret, {
|
|
identity: participantIdentity,
|
|
name: "Website visitor",
|
|
metadata: JSON.stringify(visitorMetadata),
|
|
ttl: "1 hour",
|
|
})
|
|
|
|
token.addGrant({
|
|
roomJoin: true,
|
|
room: roomName,
|
|
canPublish: true,
|
|
canPublishData: true,
|
|
canSubscribe: true,
|
|
})
|
|
|
|
token.roomConfig = new RoomConfiguration({
|
|
agents: [
|
|
new RoomAgentDispatch({
|
|
agentName,
|
|
metadata: JSON.stringify(visitorMetadata),
|
|
}),
|
|
],
|
|
})
|
|
|
|
return {
|
|
serverUrl: env.livekitUrl,
|
|
participantToken: await token.toJwt(),
|
|
roomName,
|
|
participantIdentity,
|
|
bootstrap: getVoiceAssistantBootstrap(),
|
|
}
|
|
}
|