UNPKG

livekit-voice-assistant

Version:

A customizable voice assistant component built with LiveKit

3 lines (2 loc) 10.5 kB
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("livekit-client"),n=require("@livekit/components-react"),a=require("@react-aria/utils"),r=require("framer-motion"),o=require("@livekit/components-react/krisp"),i=require("livekit-server-sdk");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var a=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,a.get?a:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var l=s(e),u=c(e);function d(t){const n=e.useRef(null),[a,r]=e.useState(!1),o=e.useRef(!1);return["listening","thinking","speaking"].includes(t.state)&&0==o.current&&(o.current=!0),e.useEffect((()=>("connecting"===t.state?n.current=window.setTimeout((()=>{"connecting"===t.state&&!1===o.current&&r(!0)}),1e4):(n.current&&window.clearTimeout(n.current),r(!1)),()=>{n.current&&window.clearTimeout(n.current)})),[t.state]),l.default.createElement(l.default.Fragment,null,a?l.default.createElement("div",{className:"lk-va-notification"},l.default.createElement("div",null,l.default.createElement("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},l.default.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M9.85068 3.63564C10.8197 2.00589 13.1793 2.00589 14.1484 3.63564L21.6323 16.2223C22.6232 17.8888 21.4223 20 19.4835 20H4.51555C2.57676 20 1.37584 17.8888 2.36671 16.2223L9.85068 3.63564ZM12 8.5C12.2761 8.5 12.5 8.72386 12.5 9V13.5C12.5 13.7761 12.2761 14 12 14C11.7239 14 11.5 13.7761 11.5 13.5V9C11.5 8.72386 11.7239 8.5 12 8.5ZM12.75 16C12.75 16.4142 12.4142 16.75 12 16.75C11.5858 16.75 11.25 16.4142 11.25 16C11.25 15.5858 11.5858 15.25 12 15.25C12.4142 15.25 12.75 15.5858 12.75 16Z",fill:"#666666"}))),l.default.createElement("p",{className:"lk-va-notification-text"},"It's quiet... too quiet. Is your agent lost? Ensure your agent is properly configured and running on your machine."),l.default.createElement("a",{href:"https://docs.livekit.io/agents/quickstarts/s2s/",target:"_blank",rel:"noopener noreferrer",className:"lk-va-notification-link"},"View guide"),l.default.createElement("button",{onClick:()=>r(!1),className:"lk-va-notification-close"},l.default.createElement("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},l.default.createElement("path",{d:"M3.16602 3.16666L12.8327 12.8333M12.8327 3.16666L3.16602 12.8333",stroke:"#999999",strokeWidth:"1.5",strokeLinecap:"square"})))):null)}function m({controls:e,saveUserChoices:r=!0,onDeviceError:o,...i}){var s;const c={leave:!0,microphone:!0,...e},l=n.useLocalParticipantPermissions(),{microphoneTrack:d,localParticipant:m}=n.useLocalParticipant(),f=u.useMemo((()=>({participant:m,source:t.Track.Source.Microphone,publication:d})),[m,d]);l?null!==(s=c.microphone)&&void 0!==s||(c.microphone=l.canPublish):c.microphone=!1;const p=a.mergeProps({className:"lk-agent-control-bar"},i),{saveAudioInputEnabled:v,saveAudioInputDeviceId:E}=n.usePersistentUserChoices({preventSave:!r}),h=u.useCallback(((e,t)=>{t&&v(e)}),[v]);return u.createElement("div",{...p},c.microphone&&u.createElement("div",{className:"lk-button-group"},u.createElement(n.TrackToggle,{source:t.Track.Source.Microphone,showIcon:!0,onChange:h,onDeviceError:e=>null==o?void 0:o({source:t.Track.Source.Microphone,error:e})},u.createElement(n.BarVisualizer,{trackRef:f,barCount:10,options:{minHeight:3,maxHeight:6}}))),c.leave&&u.createElement(n.DisconnectButton,null,"Disconnect"),u.createElement(n.StartMediaButton,null))}function f(){return"undefined"!=typeof process&&process.env?{LIVEKIT_API_KEY:process.env.LIVEKIT_API_KEY,LIVEKIT_API_SECRET:process.env.LIVEKIT_API_SECRET,LIVEKIT_URL:process.env.LIVEKIT_URL,NEXT_PUBLIC_CONN_DETAILS_ENDPOINT:process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT}:"undefined"!=typeof window?{NEXT_PUBLIC_CONN_DETAILS_ENDPOINT:window.ENV_NEXT_PUBLIC_CONN_DETAILS_ENDPOINT}:{}}function p(e){const t=f(),n=e.filter((e=>!t[e]));if(n.length>0)throw new Error(`Missing required environment variables: ${n.join(", ")}`)}async function v(e,t){const n=f(),a=new URL(e||n.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT||"/api/connection-details","undefined"!=typeof window?window.location.origin:void 0),r={userName:(null==t?void 0:t.userName)||"User",agentId:(null==t?void 0:t.agentId)||"default_agent",userId:(null==t?void 0:t.userId)||"default_user"};try{const e=await fetch(a.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!e.ok)throw new Error(`Failed to fetch connection details: ${e.statusText}`);return await e.json()}catch(e){throw console.error("Error fetching connection details:",e),e}}function E(t){const{state:a,audioTrack:r}=n.useVoiceAssistant();return e.useEffect((()=>{t.onStateChange(a)}),[t,a]),l.default.createElement("div",{className:"lk-va-visualizer-container"},l.default.createElement(n.BarVisualizer,{state:a,barCount:10,trackRef:r,className:"lk-va-visualizer",options:{minHeight:3,maxHeight:6,barWidth:3,gap:1,color:"#3B82F6"}}))}function h(t){const a=o.useKrispNoiseFilter();return e.useEffect((()=>{a.setNoiseFilterEnabled(!0)}),[a]),"disconnected"!==t.agentState&&"connecting"!==t.agentState?l.default.createElement("div",{className:"lk-va-controls"},l.default.createElement(m,{controls:{leave:!1}}),l.default.createElement(n.DisconnectButton,{onClick:t.onDisconnect},l.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"lk-va-disconnect-icon"},l.default.createElement("path",{d:"M9 18l6-6-6-6"})))):null}async function g(e){var t;p(["LIVEKIT_API_KEY","LIVEKIT_API_SECRET","LIVEKIT_URL"]);const n=f(),a=n.LIVEKIT_API_KEY,r=n.LIVEKIT_API_SECRET,o=n.LIVEKIT_URL,{userName:s,agentId:c,userId:l}=e.body||(null===(t=e.json)||void 0===t?void 0:t.call(e))||{},u=`voice_assistant_user_${Math.floor(1e4*Math.random())}`,d=`voice_assistant_room_${Math.floor(1e4*Math.random())}`,m=new i.AgentDispatchClient(o,a,r),v=await m.createDispatch(d,"inbound-agent",{metadata:'{"customData": "example"}'});console.log("Dispatch created:",v);const E=await function(e,t,n,a){const r=new i.AccessToken(n,a,{...e,ttl:"15m"}),o={room:t,roomJoin:!0,canPublish:!0,canPublishData:!0,canSubscribe:!0};return r.addGrant(o),r.toJwt()}({identity:u,name:s,attributes:{agentId:c,userId:l},metadata:"this-is-metadata"},d,a,r);return{serverUrl:o,roomName:d,participantToken:E,participantName:u}}exports.CloseIcon=function(){return l.default.createElement("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},l.default.createElement("path",{d:"M3.33398 3.33334L12.6673 12.6667M12.6673 3.33334L3.33398 12.6667",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"square"}))},exports.NoAgentNotification=d,exports.VoiceAssistant=function({connectionDetailsEndpoint:t,userName:a="User",agentId:o="agent_"+Math.random().toString(36).substring(2,9),userId:i="user_"+Math.random().toString(36).substring(2,9),buttonText:s="Start Voice Assistant",buttonClassName:c="",containerClassName:u="",onError:m,onConnected:f,onDisconnected:p,onStateChange:g}){const[k,w]=e.useState(void 0),[I,N]=e.useState("disconnected"),[C,_]=e.useState(!1),T=e.useCallback((e=>{N(e),null==g||g(e)}),[g]),y=e.useCallback((async()=>{try{_(!0);const e=await v(t,{userName:a,agentId:o,userId:i});w(e),null==f||f()}catch(e){const t=e instanceof Error?e:new Error(String(e));console.error("Failed to connect to voice assistant:",t),null==m||m(t),_(!1)}}),[t,a,o,i,f,m]),L=e.useCallback((()=>{w(void 0),_(!1),null==p||p()}),[p]),b=e.useCallback((e=>{console.error("Media device failure:",e),m&&e&&m(new Error(`Media device failure: ${e.name}`))}),[m]);return l.default.createElement("div",{className:"lk-voice-assistant-root"},l.default.createElement(r.AnimatePresence,null,!C&&l.default.createElement(r.motion.button,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},exit:{opacity:0,y:20},transition:{duration:.3,ease:"easeOut"},className:`lk-va-start-button ${c}`,onClick:y},s)),l.default.createElement(r.AnimatePresence,null,C&&l.default.createElement(r.motion.div,{initial:{opacity:0,y:50,scale:.9},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:50,scale:.9},transition:{duration:.3,ease:"easeOut"},className:`lk-va-container ${u}`},l.default.createElement(n.LiveKitRoom,{token:null==k?void 0:k.participantToken,serverUrl:null==k?void 0:k.serverUrl,connect:void 0!==k,audio:!0,video:!1,onMediaDeviceFailure:b,onDisconnected:L,className:"lk-va-room"},l.default.createElement("div",{className:"lk-va-content"},l.default.createElement("div",{className:"lk-va-header"},l.default.createElement("span",{className:"lk-va-title"},"Voice Assistant"),"disconnected"!==I&&l.default.createElement("button",{onClick:L,className:"lk-va-close-button"},l.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},l.default.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),l.default.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"})))),l.default.createElement(E,{onStateChange:T}),l.default.createElement(h,{agentState:I,onDisconnect:L})),l.default.createElement(n.RoomAudioRenderer,null),l.default.createElement(d,{state:I})))))},exports.VoiceAssistantControlBar=m,exports.checkRequiredEnv=function(e){const t=f();return e.every((e=>!!t[e]))},exports.connectionHandler=g,exports.fetchConnectionDetails=v,exports.loadEnv=f,exports.nextApiHandler=async function(e,t){if("POST"!==e.method)return t.status(405).json({error:"Method not allowed"});try{const n=await g(e);return t.status(200).json(n)}catch(e){return console.error("Error in connection handler:",e),t.status(500).json({error:e instanceof Error?e.message:"Unknown error"})}},exports.nextAppRouteHandler=async function(e){try{const t=await g(e);return new Response(JSON.stringify(t),{headers:{"Content-Type":"application/json","Cache-Control":"no-store"}})}catch(e){return console.error("Error in connection handler:",e),new Response(JSON.stringify({error:e instanceof Error?e.message:"Unknown error"}),{status:500,headers:{"Content-Type":"application/json"}})}},exports.validateServerEnv=p; //# sourceMappingURL=index.js.map