@1interface/voice-core 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/webrtc.js +1 -1
- package/dist/webrtc.mjs +1 -1
- package/package.json +1 -1
package/dist/webrtc.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sessionHelpers-ALyIwLPk.cjs"),r=require("react"),t=require("react-redux"),n=require("./useVoiceSessionCore-Bir8eqjh.cjs"),i=require("@1interface/shared-core");function s(){const e={};for(const[r,t]of Object.entries(i.getCustomHeaders()))t&&(e[r]=t);const r=i.readAccessToken();r&&(e[i.AUTH_HEADERS.ACCESS_TOKEN]=r);const t=i.readDeviceToken();return t&&(e[i.DEVICE_TOKEN_HEADER]=t),e}async function o(e){try{const r=await fetch(i.API_ENDPOINTS.WEBRTC_ICE(e),{headers:s()});return r.ok?(await r.json()).iceServers??[]:(i.logger.warn({message:"Voice ICE fetch failed — continuing without TURN",api_name:"voice-webrtc-ice",status:r.status}),[])}catch(e){return i.logger.warn({message:"Voice ICE fetch error — continuing without TURN",api_name:"voice-webrtc-ice",error_message:e instanceof Error?e.message:String(e)}),[]}}exports.useVoiceProviderValue=({conversationId:c,voiceBridge:a,transportFactory:u,assistantStartsConversation:d})=>{const l=function(c,a,u,d){const l=t.useDispatch(),g=t.useStore(),_=e.useAudioPlatform(),m=t.useSelector(e=>e.auth.accessToken),v=r.useRef(null),{bridgeRef:p,botTurnMsgIdRef:E,liveSegmentsRef:I,startResponseTimer:T,clearIndicator:S,sharedDeps:f,commitBotTurn:R,discardBotTurn:V,openBotTurnIfNeeded:C,ingestBotSegment:b,routeServerMessage:O}=n.useVoiceSessionCore(c,a,{apiName:"voice-rtvi",onSessionEnded:e=>{v.current={reason:e}}}),h=r.useRef(!1),N=r.useRef(!1),A=r.useRef(null),k=r.useRef(""),M=r.useRef(null),P=r.useRef(null),w=r.useRef(null),U=r.useRef(null),y=r.useRef(!0),L=r.useRef(null),x=r.useRef(!1),D=r.useRef(""),B=r.useRef(0),H=r.useRef(0),j=r.useRef(!1),q=r.useRef(0),F=r.useRef(null),G=r.useRef(0),W=r.useRef(0),X=r.useRef(null),K=r.useCallback(()=>{U.current&&(clearTimeout(U.current),U.current=null)},[]),Y=r.useCallback(r=>{K(),L.current&&(clearTimeout(L.current),L.current=null),x.current=!1,D.current="",l(e.setMicrophones([])),l(e.setSpeakers([])),w.current?.(),w.current=null,M.current=null,j.current=!1,l(e.setListeningEnabled(!1)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.DISCONNECTED)),l(e.setInterimTranscript(null)),l(e.setServerMuted(!1)),A.current=null,k.current="",h.current=!1,N.current=!1,r?R():V(),S(i.INDICATOR_TIMER_ACTIONS.CANCEL)},[l,S,K,R,V]),Z=r.useCallback(e=>{const r=e.spoken_progress?(e.spoken_progress?.accumulated_text??"")+(e.spoken_progress?.remaining_text??""):e.text??"";if(!r)return;C();const t=h.current?n.foldRealtimeBotOutput(I.current,r):n.foldCascadeBotOutput(I.current,e,r,N.current);b(t)},[C,b,I]),$=r.useRef(()=>{}),z=r.useRef(async()=>{}),J=r.useCallback((r,t)=>W.current>=e.VOICE_MAX_AUTH_REFRESHES||null==c?(i.logger.warn({message:"Voice RTVI auth refresh budget exhausted — giving up",api_name:"voice-rtvi",conversation_id:c??void 0,trigger:t}),!1):(W.current+=1,i.logger.info({message:"Voice RTVI refreshing token then reconnecting",api_name:"voice-rtvi",conversation_id:c??void 0,auth_refresh_count:W.current,trigger:t}),(async()=>{let t=!1;try{t=!!await(i.getTokenRefreshHandler()?.())}catch{}r===H.current&&(t?z.current():(i.logger.warn({message:"Voice RTVI token refresh failed — no reconnect",api_name:"voice-rtvi",conversation_id:c??void 0}),i.showErrorToast(e.VOICE_CONNECT_ERROR_MESSAGE),l(e.signalSessionEnded())))})(),!0),[c,l]),Q=r.useCallback(r=>{const t=()=>r===B.current;return{onConnected:()=>{if(!t())return;i.logger.info({message:"Voice RTVI connected",api_name:"voice-rtvi",conversation_id:c??void 0}),l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.CONNECTED)),l(e.setListeningEnabled(!0)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),M.current&&M.current.isMicEnabled!==y.current&&(i.logger.debug({message:"Voice re-asserting mic state on reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,enabled:y.current}),M.current.enableMic(y.current));const{selectedMicId:r,selectedSpeakerId:n}=g.getState().voice;r&&M.current?.updateMic(r),n&&M.current?.updateSpeaker(n);const s=e=>r=>i.logger.warn({message:"Voice device enumeration failed",api_name:"voice-mic",conversation_id:c??void 0,device_kind:e,error_message:r instanceof Error?r.message:String(r)});M.current?.getAllMics().then(r=>{t()&&l(e.setMicrophones(r.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(s("mics")),M.current?.getAllSpeakers().then(r=>{t()&&l(e.setSpeakers(r.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(s("speakers")),x.current=!0},onBotReady:r=>{if(!t())return;const n=r?.about;h.current=n?.pipeline_mode===e.VOICE_PIPELINE_MODES.REALTIME,N.current=!0===n?.tts_word_timestamps,G.current=0,W.current=0,q.current=0,i.logger.info({message:"Voice RTVI bot-ready",api_name:"voice-rtvi",conversation_id:c??void 0,pipeline_mode:n?.pipeline_mode??"cascade",tts_word_timestamps:N.current,assistant_starts:d}),K(),d&&(l(e.setIsAgentWorking(!0)),T())},onDisconnected:()=>{if(!t())return;i.logger.info({message:"Voice RTVI disconnected",api_name:"voice-rtvi",conversation_id:c??void 0,drop_outcome:X.current??"transient",reconnect_attempts:G.current}),Y(!0);const r=H.current,n=X.current;X.current=null;const s=v.current;if(v.current=null,s)return i.logger.info({message:"Voice session ended by server — no reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,reason:s.reason}),"idle_timeout"===s.reason&&i.showToast({title:"Voice ended",description:"It got quiet for a while, so we wrapped up. Start again anytime."}),void l(e.signalSessionEnded());if("terminal"===n)return i.logger.warn({message:"Voice RTVI terminal error — no retry",api_name:"voice-rtvi"}),void l(e.signalSessionEnded());if("auth"!==n){if(G.current>=e.VOICE_MAX_AUTO_RECONNECTS||null==c)return G.current>=e.VOICE_MAX_AUTO_RECONNECTS&&(i.logger.warn({message:"Voice RTVI auto-reconnect cap reached — giving up",api_name:"voice-rtvi",conversation_id:c??void 0,reconnect_attempts:G.current}),i.showErrorToast("Voice connection lost. Please try again.")),void l(e.signalSessionEnded());G.current+=1,i.logger.info({message:"Voice RTVI scheduling auto-reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,reconnect_attempts:G.current,delay_ms:e.VOICE_RECONNECT_DELAY_MS}),F.current&&clearTimeout(F.current),F.current=setTimeout(()=>{F.current=null,z.current()},e.VOICE_RECONNECT_DELAY_MS)}else J(r,"auth_drop")||l(e.signalSessionEnded())},onError:r=>{if(!t())return;const n=r?.data,s=n?.error??n?.message??"Voice error";i.handleSharedEvent({kind:"error",error:s},f),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),S(i.INDICATOR_TIMER_ACTIONS.CANCEL),n?.fatal&&(e.VOICE_AUTH_ERROR_PATTERNS.some(e=>e.test(s))?X.current="auth":e.VOICE_TERMINAL_ERROR_PATTERNS.some(e=>e.test(s))&&(X.current="terminal"),i.logger.warn({message:"Voice RTVI fatal error — closing transport",api_name:"voice-rtvi",conversation_id:c??void 0,drop_outcome:X.current??"generic"}),M.current?.disconnect().catch(()=>{}))},onTrackStarted:(e,r)=>{t()&&(r?.local||"audio"!==e.kind||(i.logger.debug({message:"Voice RTVI bot audio track started",api_name:"voice-rtvi",conversation_id:c??void 0}),w.current?.(),w.current=_.attachBotAudioTrack(e)))},onAvailableMicsUpdated:r=>{t()&&(l(e.setMicrophones(r.map(e=>({deviceId:e.deviceId,label:e.label})))),x.current&&(L.current&&clearTimeout(L.current),L.current=setTimeout(()=>{if(L.current=null,!M.current)return;const{selectedMicId:e,microphones:r}=g.getState().voice;i.logger.info({message:"Voice audio devices changed",api_name:"voice-mic",microphones:r.map(e=>e.label).join(", ")}),i.logger.debug({message:"Voice re-acquiring mic (device change)",api_name:"voice-rtvi",enabled:y.current,device:e||"default"}),M.current.updateMic(e)},300)))},onAvailableSpeakersUpdated:r=>{if(!t())return;if(l(e.setSpeakers(r.map(e=>({deviceId:e.deviceId,label:e.label})))),!x.current)return;const n=r.map(e=>e.label).join(", ");n!==D.current&&(D.current=n,i.logger.info({message:"Voice audio output devices changed",api_name:"voice-mic",speakers:n}))},onMicUpdated:e=>{if(!t())return;const r=M.current;r&&r.isMicEnabled!==y.current&&(i.logger.debug({message:"Voice re-asserting mic state after track swap",api_name:"voice-rtvi",conversation_id:c??void 0,enabled:y.current,device:e?.label}),r.enableMic(y.current))},onUserMuteStarted:()=>{t()&&i.logger.debug({message:"Voice RTVI user-mute-started (ignored)",api_name:"voice-rtvi",conversation_id:c??void 0})},onUserMuteStopped:()=>{t()&&(i.logger.debug({message:"Voice RTVI user-mute-stopped",api_name:"voice-rtvi",conversation_id:c??void 0,honored:d}),d&&l(e.setServerMuted(!1)))},onUserStartedSpeaking:()=>{t()&&(i.logger.debug({message:"Voice RTVI user started speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),A.current=null,k.current="",l(e.setInterimTranscript(null)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.LISTENING)))},onUserStoppedSpeaking:()=>{if(!t())return;i.logger.debug({message:"Voice RTVI user stopped speaking",api_name:"voice-rtvi",conversation_id:c??void 0,has_interim:!!k.current.trim()}),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.PROCESSING)),l(e.setIsAgentWorking(!0)),l(e.setStatusText(null)),T();const r=k.current.trim();if(r){const t=`${e.VOICE_USER_ID_PREFIX}${i.generateUniqueId()}`;A.current=t,l(e.setInterimTranscript(null)),p.current.setMessages(n=>[...n,e.makeUserMessage(r,t)])}},onUserTranscript:r=>{if(!t())return;if(!r.final)return void(r.text&&(k.current=r.text,h.current||l(e.setInterimTranscript(r.text))));l(e.setInterimTranscript(null));const n=r.text&&r.text.trim()?r.text:"";i.logger.debug({message:"Voice RTVI user transcript (final)",api_name:"voice-rtvi",conversation_id:c??void 0,has_text:!!n});const s=A.current;A.current=null,s?n&&p.current.setMessages(e=>e.map(e=>e.id===s?{...e,message:n}:e)):n&&p.current.setMessages(r=>[...r,e.makeUserMessage(n)])},onBotStartedSpeaking:()=>{t()&&(i.logger.debug({message:"Voice RTVI bot started speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),C(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.SPEAKING)),S(i.INDICATOR_TIMER_ACTIONS.FINALIZE))},onBotOutput:e=>{t()&&Z(e)},onBotStoppedSpeaking:()=>{t()&&(i.logger.debug({message:"Voice RTVI bot stopped speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),R(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),S(i.INDICATOR_TIMER_ACTIONS.FINALIZE),d&&g.getState().voice.serverMuted&&(i.logger.warn({message:"Voice greeting mute-hold released on bot-turn end (server unmute missing)",api_name:"voice-rtvi",conversation_id:c??void 0}),l(e.setServerMuted(!1))))},onServerMessage:e=>{t()&&O(e)}}},[l,g,_,K,Y,S,T,Z,C,R,O,f,p,d,c,J]),ee=r.useCallback(async()=>{if(M.current||j.current)return;if(!c)return;if(j.current=!0,i.logger.info({message:"Voice RTVI connecting",api_name:"voice-rtvi",conversation_id:c,assistant_starts:d}),d&&(y.current=!1,l(e.setServerMuted(!0)),i.logger.debug({message:"Voice greeting mute-hold armed",api_name:"voice-rtvi",conversation_id:c})),i.isTokenAboutToExpire(i.readTokenExpiresAt())&&q.current<e.VOICE_MAX_AUTH_REFRESHES){q.current+=1,i.logger.debug({message:"Voice pre-connect token refresh (freshness gate)",api_name:"voice-rtvi",conversation_id:c,freshness_refresh_count:q.current});const e=H.current;try{await Promise.resolve(i.getTokenRefreshHandler()?.())}catch{}if(e!==H.current)return}const r=++B.current,t=H.current;v.current=null,X.current=null,l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.CONNECTING));const n=()=>t!==H.current||r!==B.current;try{const[{PipecatClient:t,LogLevel:a,RTVIMessage:d},g,_]=await Promise.all([import("@pipecat-ai/client-js"),o(c),u()]);if(n())return;P.current=d;const m=new t({transport:_,enableMic:!0,enableCam:!1,callbacks:Q(r)});if(m.setLogLevel(a.WARN),n())return void m.disconnect().catch(()=>{});M.current=m,i.logger.debug({message:"Voice RTVI client constructed",api_name:"voice-rtvi",conversation_id:c,enable_mic:y.current}),K(),U.current=setTimeout(()=>{i.logger.warn({message:"Voice RTVI connect timed out (no bot-ready)",api_name:"voice-rtvi"}),i.showErrorToast("Voice connection timed out. Please try again."),l(e.signalSessionEnded()),$.current()},e.VOICE_CONNECT_TIMEOUT_MS),await m.connect({webrtcRequestParams:{endpoint:i.API_ENDPOINTS.WEBRTC_OFFER(c),headers:new Headers(s())},iceConfig:{iceServers:g}})}catch(n){const s=(e=>{const r="object"==typeof e&&null!==e&&"status"in e?e.status:void 0;return"number"==typeof r?r:void 0})(n);if(401===s&&r===B.current&&W.current<e.VOICE_MAX_AUTH_REFRESHES)return i.logger.warn({message:"Voice RTVI offer refused with 401 — refreshing token",api_name:"voice-rtvi",conversation_id:c}),F.current&&(clearTimeout(F.current),F.current=null),G.current=0,Y(!1),void J(t,"offer_401");i.logger.error({message:"Voice RTVI connect failed",api_name:"voice-rtvi",status:s,error_message:n instanceof Error?n.message:String(n)});const o=(e=>void 0!==e&&e>=400&&e<500)(s),a=o?await(async(r,t)=>{const n=429===t?"Voice session limit reached. Please try again later.":e.VOICE_CONNECT_ERROR_MESSAGE,i="object"==typeof r&&null!==r&&"cause"in r?r.cause:void 0;if(!(i instanceof Response))return n;try{const e=await i.json(),r=e?.data?.error_message??e?.data?.message;return"string"==typeof r?r:n}catch{return n}})(n,s):e.VOICE_CONNECT_ERROR_MESSAGE;r===B.current&&(i.showErrorToast(a),o&&(F.current&&(clearTimeout(F.current),F.current=null),G.current=0,l(e.signalSessionEnded())),Y(!1))}},[c,l,u,Q,K,Y,d,J]),re=r.useCallback((r=!0)=>{i.logger.info({message:"Voice RTVI disconnect (intentional teardown)",api_name:"voice-rtvi",conversation_id:c??void 0,commit_turn:r}),H.current+=1,B.current+=1,q.current=0,F.current&&(clearTimeout(F.current),F.current=null),G.current=0,W.current=0,X.current=null,y.current=!0,l(e.setMuted(!1));const t=M.current;t?.disconnect().catch(e=>{i.logger.warn({message:"Voice RTVI disconnect threw",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}),Y(r)},[Y,l,c]),te=r.useCallback(e=>{y.current=e,M.current&&(i.logger.debug({message:"Voice mic enable toggled",api_name:"voice-rtvi",enabled:e}),M.current.enableMic(e))},[]),ne=r.useCallback(r=>{l(e.setSelectedMicId(r)),M.current?.updateMic(r)},[l]),ie=r.useCallback(r=>{l(e.setSelectedSpeakerId(r)),M.current?.updateSpeaker(r)},[l]),se=r.useCallback((r,t)=>{const n=M.current;if(!n)return;const s=t?.modelContext;i.logger.info({message:"Voice RTVI send-text",api_name:"voice-rtvi",conversation_id:c??void 0,run_immediately:!0,has_model_context:Boolean(s?.length)});try{const e=P.current;s?.length&&e?n.transport.sendMessage(new e("send-text",{content:r,options:{run_immediately:!0,audio_response:!0},model_context:s})):n.sendText(r,{run_immediately:!0,audio_response:!0})}catch(e){return void i.logger.error({message:"Voice RTVI send-text failed",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}E.current&&R(),p.current.setMessages(n=>[...n,e.makeUserMessage(r,void 0,t?.documents,t?.messageType)]),l(e.setIsAgentWorking(!0)),l(e.setStatusText(null)),T(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.PROCESSING))},[l,T,R,E,p,c]);r.useEffect(()=>{$.current=re},[re]),r.useEffect(()=>{z.current=ee},[ee]);const oe=r.useRef(c),ce=r.useRef(!1);r.useEffect(()=>{const e=oe.current;if(oe.current=c,e===c)return;const r=j.current;r&&(ce.current=!0),r&&re(!1),ce.current&&null!=c&&(ce.current=!1,ee())},[c,re,ee]);const ae=r.useRef(m);return r.useEffect(()=>{const e=ae.current;ae.current=m,e&&!m&&j.current&&re(!1)},[m,re]),r.useEffect(()=>()=>{$.current()},[]),{connect:ee,disconnect:re,sendText:se,setMicEnabled:te,setInputDevice:ne,setOutputDevice:ie}}(c,a,u,d);return e.useVoiceContextValue(l,{voiceBridge:a})};
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sessionHelpers-ALyIwLPk.cjs"),r=require("react"),t=require("react-redux"),n=require("./useVoiceSessionCore-Bir8eqjh.cjs"),s=require("@1interface/shared-core");function i(){const e={};for(const[r,t]of Object.entries(s.getCustomHeaders()))t&&(e[r]=t);const r=s.readAccessToken();r&&(e[s.AUTH_HEADERS.ACCESS_TOKEN]=r);const t=s.readDeviceToken();return t&&(e[s.DEVICE_TOKEN_HEADER]=t),e}async function o(e){try{const r=await fetch(s.API_ENDPOINTS.WEBRTC_ICE(e),{headers:i()});return r.ok?(await r.json()).iceServers??[]:(s.logger.warn({message:"Voice ICE fetch failed — continuing without TURN",api_name:"voice-webrtc-ice",status:r.status}),[])}catch(e){return s.logger.warn({message:"Voice ICE fetch error — continuing without TURN",api_name:"voice-webrtc-ice",error_message:e instanceof Error?e.message:String(e)}),[]}}exports.useVoiceProviderValue=({conversationId:c,voiceBridge:a,transportFactory:u,assistantStartsConversation:d})=>{const l=function(c,a,u,d){const l=t.useDispatch(),g=t.useStore(),m=e.useAudioPlatform(),_=t.useSelector(e=>e.auth.accessToken),v=r.useRef(null),{bridgeRef:p,botTurnMsgIdRef:E,liveSegmentsRef:I,startResponseTimer:T,clearIndicator:S,sharedDeps:f,commitBotTurn:R,discardBotTurn:V,openBotTurnIfNeeded:C,ingestBotSegment:h,routeServerMessage:b}=n.useVoiceSessionCore(c,a,{apiName:"voice-rtvi",onSessionEnded:e=>{v.current={reason:e}}}),O=r.useRef(!1),N=r.useRef(!1),k=r.useRef(!1),A=r.useRef([]),M=r.useRef(null),P=r.useRef(""),w=r.useRef(null),U=r.useRef(null),y=r.useRef(null),L=r.useRef(null),x=r.useRef(!0),D=r.useRef(null),B=r.useRef(!1),H=r.useRef(""),j=r.useRef(0),q=r.useRef(0),F=r.useRef(!1),G=r.useRef(0),W=r.useRef(null),X=r.useRef(0),K=r.useRef(0),Y=r.useRef(null),Z=r.useCallback(()=>{L.current&&(clearTimeout(L.current),L.current=null)},[]),$=r.useCallback(r=>{Z(),D.current&&(clearTimeout(D.current),D.current=null),B.current=!1,H.current="",l(e.setMicrophones([])),l(e.setSpeakers([])),y.current?.(),y.current=null,w.current=null,F.current=!1,l(e.setListeningEnabled(!1)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.DISCONNECTED)),l(e.setInterimTranscript(null)),l(e.setServerMuted(!1)),M.current=null,P.current="",O.current=!1,N.current=!1,k.current=!1,A.current=[],r?R():V(),S(s.INDICATOR_TIMER_ACTIONS.CANCEL)},[l,S,Z,R,V]),z=r.useCallback(e=>{const r=e.spoken_progress?(e.spoken_progress?.accumulated_text??"")+(e.spoken_progress?.remaining_text??""):e.text??"";if(!r)return;C();const t=O.current?n.foldRealtimeBotOutput(I.current,r):n.foldCascadeBotOutput(I.current,e,r,N.current);h(t)},[C,h,I]),J=r.useRef(()=>{}),Q=r.useRef(async()=>{}),ee=r.useCallback((r,t)=>K.current>=e.VOICE_MAX_AUTH_REFRESHES||null==c?(s.logger.warn({message:"Voice RTVI auth refresh budget exhausted — giving up",api_name:"voice-rtvi",conversation_id:c??void 0,trigger:t}),!1):(K.current+=1,s.logger.info({message:"Voice RTVI refreshing token then reconnecting",api_name:"voice-rtvi",conversation_id:c??void 0,auth_refresh_count:K.current,trigger:t}),(async()=>{let t=!1;try{t=!!await(s.getTokenRefreshHandler()?.())}catch{}r===q.current&&(t?Q.current():(s.logger.warn({message:"Voice RTVI token refresh failed — no reconnect",api_name:"voice-rtvi",conversation_id:c??void 0}),s.showErrorToast(e.VOICE_CONNECT_ERROR_MESSAGE),l(e.signalSessionEnded())))})(),!0),[c,l]),re=r.useCallback(r=>{const t=()=>r===j.current;return{onConnected:()=>{if(!t())return;s.logger.info({message:"Voice RTVI connected",api_name:"voice-rtvi",conversation_id:c??void 0}),l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.CONNECTED)),l(e.setListeningEnabled(!0)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),w.current&&w.current.isMicEnabled!==x.current&&(s.logger.debug({message:"Voice re-asserting mic state on reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,enabled:x.current}),w.current.enableMic(x.current));const{selectedMicId:r,selectedSpeakerId:n}=g.getState().voice;r&&w.current?.updateMic(r),n&&w.current?.updateSpeaker(n);const i=e=>r=>s.logger.warn({message:"Voice device enumeration failed",api_name:"voice-mic",conversation_id:c??void 0,device_kind:e,error_message:r instanceof Error?r.message:String(r)});w.current?.getAllMics().then(r=>{t()&&l(e.setMicrophones(r.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(i("mics")),w.current?.getAllSpeakers().then(r=>{t()&&l(e.setSpeakers(r.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(i("speakers")),B.current=!0},onBotReady:r=>{if(!t())return;const n=r?.about;O.current=n?.pipeline_mode===e.VOICE_PIPELINE_MODES.REALTIME,N.current=!0===n?.tts_word_timestamps;const i=null!==E.current||A.current.length>0;k.current=!0;for(const e of A.current.splice(0))"output"===e.kind?z(e.data):"turnEnd"===e.kind?R():p.current.setMessages(r=>[...r,e.message]);X.current=0,K.current=0,G.current=0,s.logger.info({message:"Voice RTVI bot-ready",api_name:"voice-rtvi",conversation_id:c??void 0,pipeline_mode:n?.pipeline_mode??"cascade",tts_word_timestamps:N.current,assistant_starts:d}),Z(),d&&!i&&(l(e.setIsAgentWorking(!0)),T())},onDisconnected:()=>{if(!t())return;s.logger.info({message:"Voice RTVI disconnected",api_name:"voice-rtvi",conversation_id:c??void 0,drop_outcome:Y.current??"transient",reconnect_attempts:X.current}),$(!0);const r=q.current,n=Y.current;Y.current=null;const i=v.current;if(v.current=null,i)return s.logger.info({message:"Voice session ended by server — no reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,reason:i.reason}),"idle_timeout"===i.reason&&s.showToast({title:"Voice ended",description:"It got quiet for a while, so we wrapped up. Start again anytime."}),void l(e.signalSessionEnded());if("terminal"===n)return s.logger.warn({message:"Voice RTVI terminal error — no retry",api_name:"voice-rtvi"}),void l(e.signalSessionEnded());if("auth"!==n){if(X.current>=e.VOICE_MAX_AUTO_RECONNECTS||null==c)return X.current>=e.VOICE_MAX_AUTO_RECONNECTS&&(s.logger.warn({message:"Voice RTVI auto-reconnect cap reached — giving up",api_name:"voice-rtvi",conversation_id:c??void 0,reconnect_attempts:X.current}),s.showErrorToast("Voice connection lost. Please try again.")),void l(e.signalSessionEnded());X.current+=1,s.logger.info({message:"Voice RTVI scheduling auto-reconnect",api_name:"voice-rtvi",conversation_id:c??void 0,reconnect_attempts:X.current,delay_ms:e.VOICE_RECONNECT_DELAY_MS}),W.current&&clearTimeout(W.current),W.current=setTimeout(()=>{W.current=null,Q.current()},e.VOICE_RECONNECT_DELAY_MS)}else ee(r,"auth_drop")||l(e.signalSessionEnded())},onError:r=>{if(!t())return;const n=r?.data,i=n?.error??n?.message??"Voice error";s.handleSharedEvent({kind:"error",error:i},f),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),S(s.INDICATOR_TIMER_ACTIONS.CANCEL),n?.fatal&&(e.VOICE_AUTH_ERROR_PATTERNS.some(e=>e.test(i))?Y.current="auth":e.VOICE_TERMINAL_ERROR_PATTERNS.some(e=>e.test(i))&&(Y.current="terminal"),s.logger.warn({message:"Voice RTVI fatal error — closing transport",api_name:"voice-rtvi",conversation_id:c??void 0,drop_outcome:Y.current??"generic"}),w.current?.disconnect().catch(()=>{}))},onTrackStarted:(e,r)=>{t()&&(r?.local||"audio"!==e.kind||(s.logger.debug({message:"Voice RTVI bot audio track started",api_name:"voice-rtvi",conversation_id:c??void 0}),y.current?.(),y.current=m.attachBotAudioTrack(e)))},onAvailableMicsUpdated:r=>{t()&&(l(e.setMicrophones(r.map(e=>({deviceId:e.deviceId,label:e.label})))),B.current&&(D.current&&clearTimeout(D.current),D.current=setTimeout(()=>{if(D.current=null,!w.current)return;const{selectedMicId:e,microphones:r}=g.getState().voice;s.logger.info({message:"Voice audio devices changed",api_name:"voice-mic",microphones:r.map(e=>e.label).join(", ")}),s.logger.debug({message:"Voice re-acquiring mic (device change)",api_name:"voice-rtvi",enabled:x.current,device:e||"default"}),w.current.updateMic(e)},300)))},onAvailableSpeakersUpdated:r=>{if(!t())return;if(l(e.setSpeakers(r.map(e=>({deviceId:e.deviceId,label:e.label})))),!B.current)return;const n=r.map(e=>e.label).join(", ");n!==H.current&&(H.current=n,s.logger.info({message:"Voice audio output devices changed",api_name:"voice-mic",speakers:n}))},onMicUpdated:e=>{if(!t())return;const r=w.current;r&&r.isMicEnabled!==x.current&&(s.logger.debug({message:"Voice re-asserting mic state after track swap",api_name:"voice-rtvi",conversation_id:c??void 0,enabled:x.current,device:e?.label}),r.enableMic(x.current))},onUserMuteStarted:()=>{t()&&s.logger.debug({message:"Voice RTVI user-mute-started (ignored)",api_name:"voice-rtvi",conversation_id:c??void 0})},onUserMuteStopped:()=>{t()&&(s.logger.debug({message:"Voice RTVI user-mute-stopped",api_name:"voice-rtvi",conversation_id:c??void 0,honored:d}),d&&l(e.setServerMuted(!1)))},onUserStartedSpeaking:()=>{t()&&(s.logger.debug({message:"Voice RTVI user started speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),M.current=null,P.current="",l(e.setInterimTranscript(null)),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.LISTENING)))},onUserStoppedSpeaking:()=>{if(!t())return;s.logger.debug({message:"Voice RTVI user stopped speaking",api_name:"voice-rtvi",conversation_id:c??void 0,has_interim:!!P.current.trim()}),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.PROCESSING)),l(e.setIsAgentWorking(!0)),l(e.setStatusText(null)),T();const r=P.current.trim();if(r){const t=`${e.VOICE_USER_ID_PREFIX}${s.generateUniqueId()}`;M.current=t,l(e.setInterimTranscript(null)),p.current.setMessages(n=>[...n,e.makeUserMessage(r,t)])}},onUserTranscript:r=>{if(!t())return;if(!r.final)return void(r.text&&(P.current=r.text,O.current||l(e.setInterimTranscript(r.text))));l(e.setInterimTranscript(null));const n=r.text&&r.text.trim()?r.text:"";s.logger.debug({message:"Voice RTVI user transcript (final)",api_name:"voice-rtvi",conversation_id:c??void 0,has_text:!!n});const i=M.current;M.current=null,i?n&&p.current.setMessages(e=>e.map(e=>e.id===i?{...e,message:n}:e)):n&&p.current.setMessages(r=>[...r,e.makeUserMessage(n)])},onBotStartedSpeaking:()=>{t()&&(s.logger.debug({message:"Voice RTVI bot started speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),C(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.SPEAKING)),S(s.INDICATOR_TIMER_ACTIONS.FINALIZE))},onBotOutput:e=>{t()&&(k.current?z(e):A.current.push({kind:"output",data:e}))},onBotStoppedSpeaking:()=>{t()&&(s.logger.debug({message:"Voice RTVI bot stopped speaking",api_name:"voice-rtvi",conversation_id:c??void 0}),!k.current&&A.current.length>0?A.current.push({kind:"turnEnd"}):R(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.IDLE)),S(s.INDICATOR_TIMER_ACTIONS.FINALIZE),d&&g.getState().voice.serverMuted&&(s.logger.warn({message:"Voice greeting mute-hold released on bot-turn end (server unmute missing)",api_name:"voice-rtvi",conversation_id:c??void 0}),l(e.setServerMuted(!1))))},onServerMessage:e=>{t()&&b(e)}}},[l,g,m,Z,$,S,T,z,C,R,E,b,f,p,d,c,ee]),te=r.useCallback(async()=>{if(w.current||F.current)return;if(!c)return;if(F.current=!0,s.logger.info({message:"Voice RTVI connecting",api_name:"voice-rtvi",conversation_id:c,assistant_starts:d}),d&&(x.current=!1,l(e.setServerMuted(!0)),s.logger.debug({message:"Voice greeting mute-hold armed",api_name:"voice-rtvi",conversation_id:c})),s.isTokenAboutToExpire(s.readTokenExpiresAt())&&G.current<e.VOICE_MAX_AUTH_REFRESHES){G.current+=1,s.logger.debug({message:"Voice pre-connect token refresh (freshness gate)",api_name:"voice-rtvi",conversation_id:c,freshness_refresh_count:G.current});const e=q.current;try{await Promise.resolve(s.getTokenRefreshHandler()?.())}catch{}if(e!==q.current)return}const r=++j.current;k.current=!1,A.current=[];const t=q.current;v.current=null,Y.current=null,l(e.setConnectionStatus(e.VOICE_CONNECTION_STATUSES.CONNECTING));const n=()=>t!==q.current||r!==j.current;try{const[{PipecatClient:t,LogLevel:a,RTVIMessage:d},g,m]=await Promise.all([import("@pipecat-ai/client-js"),o(c),u()]);if(n())return;U.current=d;const _=new t({transport:m,enableMic:!0,enableCam:!1,callbacks:re(r)});if(_.setLogLevel(a.WARN),n())return void _.disconnect().catch(()=>{});w.current=_,s.logger.debug({message:"Voice RTVI client constructed",api_name:"voice-rtvi",conversation_id:c,enable_mic:x.current}),Z(),L.current=setTimeout(()=>{s.logger.warn({message:"Voice RTVI connect timed out (no bot-ready)",api_name:"voice-rtvi"}),s.showErrorToast("Voice connection timed out. Please try again."),l(e.signalSessionEnded()),J.current()},e.VOICE_CONNECT_TIMEOUT_MS),await _.connect({webrtcRequestParams:{endpoint:s.API_ENDPOINTS.WEBRTC_OFFER(c),headers:new Headers(i())},iceConfig:{iceServers:g}})}catch(n){const i=(e=>{const r="object"==typeof e&&null!==e&&"status"in e?e.status:void 0;return"number"==typeof r?r:void 0})(n);if(401===i&&r===j.current&&K.current<e.VOICE_MAX_AUTH_REFRESHES)return s.logger.warn({message:"Voice RTVI offer refused with 401 — refreshing token",api_name:"voice-rtvi",conversation_id:c}),W.current&&(clearTimeout(W.current),W.current=null),X.current=0,$(!1),void ee(t,"offer_401");s.logger.error({message:"Voice RTVI connect failed",api_name:"voice-rtvi",status:i,error_message:n instanceof Error?n.message:String(n)});const o=(e=>void 0!==e&&e>=400&&e<500)(i),a=o?await(async(r,t)=>{const n=429===t?"Voice session limit reached. Please try again later.":e.VOICE_CONNECT_ERROR_MESSAGE,s="object"==typeof r&&null!==r&&"cause"in r?r.cause:void 0;if(!(s instanceof Response))return n;try{const e=await s.json(),r=e?.data?.error_message??e?.data?.message;return"string"==typeof r?r:n}catch{return n}})(n,i):e.VOICE_CONNECT_ERROR_MESSAGE;r===j.current&&(s.showErrorToast(a),o&&(W.current&&(clearTimeout(W.current),W.current=null),X.current=0,l(e.signalSessionEnded())),$(!1))}},[c,l,u,re,Z,$,d,ee]),ne=r.useCallback((r=!0)=>{s.logger.info({message:"Voice RTVI disconnect (intentional teardown)",api_name:"voice-rtvi",conversation_id:c??void 0,commit_turn:r}),q.current+=1,j.current+=1,G.current=0,W.current&&(clearTimeout(W.current),W.current=null),X.current=0,K.current=0,Y.current=null,x.current=!0,l(e.setMuted(!1));const t=w.current;t?.disconnect().catch(e=>{s.logger.warn({message:"Voice RTVI disconnect threw",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}),$(r)},[$,l,c]),se=r.useCallback(e=>{x.current=e,w.current&&(s.logger.debug({message:"Voice mic enable toggled",api_name:"voice-rtvi",enabled:e}),w.current.enableMic(e))},[]),ie=r.useCallback(r=>{l(e.setSelectedMicId(r)),w.current?.updateMic(r)},[l]),oe=r.useCallback(r=>{l(e.setSelectedSpeakerId(r)),w.current?.updateSpeaker(r)},[l]),ce=r.useCallback((r,t)=>{const n=w.current;if(!n)return;const i=t?.modelContext;s.logger.info({message:"Voice RTVI send-text",api_name:"voice-rtvi",conversation_id:c??void 0,run_immediately:!0,has_model_context:Boolean(i?.length)});try{const e=U.current;i?.length&&e?n.transport.sendMessage(new e("send-text",{content:r,options:{run_immediately:!0,audio_response:!0},model_context:i})):n.sendText(r,{run_immediately:!0,audio_response:!0})}catch(e){return void s.logger.error({message:"Voice RTVI send-text failed",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}const o=e.makeUserMessage(r,void 0,t?.documents,t?.messageType);!k.current&&A.current.length>0?A.current.push({kind:"turnEnd"},{kind:"userRow",message:o}):(E.current&&R(),p.current.setMessages(e=>[...e,o])),l(e.setIsAgentWorking(!0)),l(e.setStatusText(null)),T(),l(e.setPipelineStatus(e.VOICE_PIPELINE_STATUSES.PROCESSING))},[l,T,R,E,p,c]);r.useEffect(()=>{J.current=ne},[ne]),r.useEffect(()=>{Q.current=te},[te]);const ae=r.useRef(c),ue=r.useRef(!1);r.useEffect(()=>{const e=ae.current;if(ae.current=c,e===c)return;const r=F.current;r&&(ue.current=!0),r&&ne(!1),ue.current&&null!=c&&(ue.current=!1,te())},[c,ne,te]);const de=r.useRef(_);return r.useEffect(()=>{const e=de.current;de.current=_,e&&!_&&F.current&&ne(!1)},[_,ne]),r.useEffect(()=>()=>{J.current()},[]),{connect:te,disconnect:ne,sendText:ce,setMicEnabled:se,setInputDevice:ie,setOutputDevice:oe}}(c,a,u,d);return e.useVoiceContextValue(l,{voiceBridge:a})};
|
package/dist/webrtc.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as e,s as r,a as t,b as n,c as i,V as c,d as o,e as a,f as s,g as u,h as d,i as m,j as l,m as v,k as g,l as p,n as _,o as f,p as V,q as I,r as b,t as h,v as T,w as R,x as S,y as w,z as E}from"./sessionHelpers-CK66mJIw.js";import{useRef as k,useCallback as y,useEffect as M}from"react";import{useDispatch as C,useStore as x,useSelector as N}from"react-redux";import{u as B,f as L,a as A}from"./useVoiceSessionCore-C9xo14Pi.js";import{API_ENDPOINTS as j,logger as D,getCustomHeaders as O,readAccessToken as P,AUTH_HEADERS as U,readDeviceToken as F,DEVICE_TOKEN_HEADER as G,INDICATOR_TIMER_ACTIONS as q,getTokenRefreshHandler as W,showErrorToast as H,generateUniqueId as K,handleSharedEvent as Z,showToast as $,isTokenAboutToExpire as z,readTokenExpiresAt as J}from"@1interface/shared-core";function Q(){const e={};for(const[r,t]of Object.entries(O()))t&&(e[r]=t);const r=P();r&&(e[U.ACCESS_TOKEN]=r);const t=F();return t&&(e[G]=t),e}async function X(e){try{const r=await fetch(j.WEBRTC_ICE(e),{headers:Q()});return r.ok?(await r.json()).iceServers??[]:(D.warn({message:"Voice ICE fetch failed — continuing without TURN",api_name:"voice-webrtc-ice",status:r.status}),[])}catch(e){return D.warn({message:"Voice ICE fetch error — continuing without TURN",api_name:"voice-webrtc-ice",error_message:e instanceof Error?e.message:String(e)}),[]}}const Y=({conversationId:O,voiceBridge:P,transportFactory:U,assistantStartsConversation:F})=>{const G=function(E,O,P,U){const F=C(),G=x(),Y=e(),ee=N(e=>e.auth.accessToken),re=k(null),{bridgeRef:te,botTurnMsgIdRef:ne,liveSegmentsRef:ie,startResponseTimer:ce,clearIndicator:oe,sharedDeps:ae,commitBotTurn:se,discardBotTurn:ue,openBotTurnIfNeeded:de,ingestBotSegment:me,routeServerMessage:le}=B(E,O,{apiName:"voice-rtvi",onSessionEnded:e=>{re.current={reason:e}}}),ve=k(!1),ge=k(!1),pe=k(null),_e=k(""),fe=k(null),Ve=k(null),Ie=k(null),be=k(null),he=k(!0),Te=k(null),Re=k(!1),Se=k(""),we=k(0),Ee=k(0),ke=k(!1),ye=k(0),Me=k(null),Ce=k(0),xe=k(0),Ne=k(null),Be=y(()=>{be.current&&(clearTimeout(be.current),be.current=null)},[]),Le=y(e=>{Be(),Te.current&&(clearTimeout(Te.current),Te.current=null),Re.current=!1,Se.current="",F(r([])),F(t([])),Ie.current?.(),Ie.current=null,fe.current=null,ke.current=!1,F(n(!1)),F(i(c.IDLE)),F(o(a.DISCONNECTED)),F(s(null)),F(u(!1)),pe.current=null,_e.current="",ve.current=!1,ge.current=!1,e?se():ue(),oe(q.CANCEL)},[F,oe,Be,se,ue]),Ae=y(e=>{const r=e.spoken_progress?(e.spoken_progress?.accumulated_text??"")+(e.spoken_progress?.remaining_text??""):e.text??"";if(!r)return;de();const t=ve.current?L(ie.current,r):A(ie.current,e,r,ge.current);me(t)},[de,me,ie]),je=k(()=>{}),De=k(async()=>{}),Oe=y((e,r)=>xe.current>=d||null==E?(D.warn({message:"Voice RTVI auth refresh budget exhausted — giving up",api_name:"voice-rtvi",conversation_id:E??void 0,trigger:r}),!1):(xe.current+=1,D.info({message:"Voice RTVI refreshing token then reconnecting",api_name:"voice-rtvi",conversation_id:E??void 0,auth_refresh_count:xe.current,trigger:r}),(async()=>{let r=!1;try{r=!!await(W()?.())}catch{}e===Ee.current&&(r?De.current():(D.warn({message:"Voice RTVI token refresh failed — no reconnect",api_name:"voice-rtvi",conversation_id:E??void 0}),H(m),F(l())))})(),!0),[E,F]),Pe=y(e=>{const d=()=>e===we.current;return{onConnected:()=>{if(!d())return;D.info({message:"Voice RTVI connected",api_name:"voice-rtvi",conversation_id:E??void 0}),F(o(a.CONNECTED)),F(n(!0)),F(i(c.IDLE)),fe.current&&fe.current.isMicEnabled!==he.current&&(D.debug({message:"Voice re-asserting mic state on reconnect",api_name:"voice-rtvi",conversation_id:E??void 0,enabled:he.current}),fe.current.enableMic(he.current));const{selectedMicId:e,selectedSpeakerId:s}=G.getState().voice;e&&fe.current?.updateMic(e),s&&fe.current?.updateSpeaker(s);const u=e=>r=>D.warn({message:"Voice device enumeration failed",api_name:"voice-mic",conversation_id:E??void 0,device_kind:e,error_message:r instanceof Error?r.message:String(r)});fe.current?.getAllMics().then(e=>{d()&&F(r(e.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(u("mics")),fe.current?.getAllSpeakers().then(e=>{d()&&F(t(e.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(u("speakers")),Re.current=!0},onBotReady:e=>{if(!d())return;const r=e?.about;ve.current=r?.pipeline_mode===h.REALTIME,ge.current=!0===r?.tts_word_timestamps,Ce.current=0,xe.current=0,ye.current=0,D.info({message:"Voice RTVI bot-ready",api_name:"voice-rtvi",conversation_id:E??void 0,pipeline_mode:r?.pipeline_mode??"cascade",tts_word_timestamps:ge.current,assistant_starts:U}),Be(),U&&(F(g(!0)),ce())},onDisconnected:()=>{if(!d())return;D.info({message:"Voice RTVI disconnected",api_name:"voice-rtvi",conversation_id:E??void 0,drop_outcome:Ne.current??"transient",reconnect_attempts:Ce.current}),Le(!0);const e=Ee.current,r=Ne.current;Ne.current=null;const t=re.current;if(re.current=null,t)return D.info({message:"Voice session ended by server — no reconnect",api_name:"voice-rtvi",conversation_id:E??void 0,reason:t.reason}),"idle_timeout"===t.reason&&$({title:"Voice ended",description:"It got quiet for a while, so we wrapped up. Start again anytime."}),void F(l());if("terminal"===r)return D.warn({message:"Voice RTVI terminal error — no retry",api_name:"voice-rtvi"}),void F(l());if("auth"!==r){if(Ce.current>=I||null==E)return Ce.current>=I&&(D.warn({message:"Voice RTVI auto-reconnect cap reached — giving up",api_name:"voice-rtvi",conversation_id:E??void 0,reconnect_attempts:Ce.current}),H("Voice connection lost. Please try again.")),void F(l());Ce.current+=1,D.info({message:"Voice RTVI scheduling auto-reconnect",api_name:"voice-rtvi",conversation_id:E??void 0,reconnect_attempts:Ce.current,delay_ms:b}),Me.current&&clearTimeout(Me.current),Me.current=setTimeout(()=>{Me.current=null,De.current()},b)}else Oe(e,"auth_drop")||F(l())},onError:e=>{if(!d())return;const r=e?.data,t=r?.error??r?.message??"Voice error";Z({kind:"error",error:t},ae),F(i(c.IDLE)),oe(q.CANCEL),r?.fatal&&(f.some(e=>e.test(t))?Ne.current="auth":V.some(e=>e.test(t))&&(Ne.current="terminal"),D.warn({message:"Voice RTVI fatal error — closing transport",api_name:"voice-rtvi",conversation_id:E??void 0,drop_outcome:Ne.current??"generic"}),fe.current?.disconnect().catch(()=>{}))},onTrackStarted:(e,r)=>{d()&&(r?.local||"audio"!==e.kind||(D.debug({message:"Voice RTVI bot audio track started",api_name:"voice-rtvi",conversation_id:E??void 0}),Ie.current?.(),Ie.current=Y.attachBotAudioTrack(e)))},onAvailableMicsUpdated:e=>{d()&&(F(r(e.map(e=>({deviceId:e.deviceId,label:e.label})))),Re.current&&(Te.current&&clearTimeout(Te.current),Te.current=setTimeout(()=>{if(Te.current=null,!fe.current)return;const{selectedMicId:e,microphones:r}=G.getState().voice;D.info({message:"Voice audio devices changed",api_name:"voice-mic",microphones:r.map(e=>e.label).join(", ")}),D.debug({message:"Voice re-acquiring mic (device change)",api_name:"voice-rtvi",enabled:he.current,device:e||"default"}),fe.current.updateMic(e)},300)))},onAvailableSpeakersUpdated:e=>{if(!d())return;if(F(t(e.map(e=>({deviceId:e.deviceId,label:e.label})))),!Re.current)return;const r=e.map(e=>e.label).join(", ");r!==Se.current&&(Se.current=r,D.info({message:"Voice audio output devices changed",api_name:"voice-mic",speakers:r}))},onMicUpdated:e=>{if(!d())return;const r=fe.current;r&&r.isMicEnabled!==he.current&&(D.debug({message:"Voice re-asserting mic state after track swap",api_name:"voice-rtvi",conversation_id:E??void 0,enabled:he.current,device:e?.label}),r.enableMic(he.current))},onUserMuteStarted:()=>{d()&&D.debug({message:"Voice RTVI user-mute-started (ignored)",api_name:"voice-rtvi",conversation_id:E??void 0})},onUserMuteStopped:()=>{d()&&(D.debug({message:"Voice RTVI user-mute-stopped",api_name:"voice-rtvi",conversation_id:E??void 0,honored:U}),U&&F(u(!1)))},onUserStartedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI user started speaking",api_name:"voice-rtvi",conversation_id:E??void 0}),pe.current=null,_e.current="",F(s(null)),F(i(c.LISTENING)))},onUserStoppedSpeaking:()=>{if(!d())return;D.debug({message:"Voice RTVI user stopped speaking",api_name:"voice-rtvi",conversation_id:E??void 0,has_interim:!!_e.current.trim()}),F(i(c.PROCESSING)),F(g(!0)),F(p(null)),ce();const e=_e.current.trim();if(e){const r=`${_}${K()}`;pe.current=r,F(s(null)),te.current.setMessages(t=>[...t,v(e,r)])}},onUserTranscript:e=>{if(!d())return;if(!e.final)return void(e.text&&(_e.current=e.text,ve.current||F(s(e.text))));F(s(null));const r=e.text&&e.text.trim()?e.text:"";D.debug({message:"Voice RTVI user transcript (final)",api_name:"voice-rtvi",conversation_id:E??void 0,has_text:!!r});const t=pe.current;pe.current=null,t?r&&te.current.setMessages(e=>e.map(e=>e.id===t?{...e,message:r}:e)):r&&te.current.setMessages(e=>[...e,v(r)])},onBotStartedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI bot started speaking",api_name:"voice-rtvi",conversation_id:E??void 0}),de(),F(i(c.SPEAKING)),oe(q.FINALIZE))},onBotOutput:e=>{d()&&Ae(e)},onBotStoppedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI bot stopped speaking",api_name:"voice-rtvi",conversation_id:E??void 0}),se(),F(i(c.IDLE)),oe(q.FINALIZE),U&&G.getState().voice.serverMuted&&(D.warn({message:"Voice greeting mute-hold released on bot-turn end (server unmute missing)",api_name:"voice-rtvi",conversation_id:E??void 0}),F(u(!1))))},onServerMessage:e=>{d()&&le(e)}}},[F,G,Y,Be,Le,oe,ce,Ae,de,se,le,ae,te,U,E,Oe]),Ue=y(async()=>{if(fe.current||ke.current)return;if(!E)return;if(ke.current=!0,D.info({message:"Voice RTVI connecting",api_name:"voice-rtvi",conversation_id:E,assistant_starts:U}),U&&(he.current=!1,F(u(!0)),D.debug({message:"Voice greeting mute-hold armed",api_name:"voice-rtvi",conversation_id:E})),z(J())&&ye.current<d){ye.current+=1,D.debug({message:"Voice pre-connect token refresh (freshness gate)",api_name:"voice-rtvi",conversation_id:E,freshness_refresh_count:ye.current});const e=Ee.current;try{await Promise.resolve(W()?.())}catch{}if(e!==Ee.current)return}const e=++we.current,r=Ee.current;re.current=null,Ne.current=null,F(o(a.CONNECTING));const t=()=>r!==Ee.current||e!==we.current;try{const[{PipecatClient:r,LogLevel:n,RTVIMessage:i},c,o]=await Promise.all([import("@pipecat-ai/client-js"),X(E),P()]);if(t())return;Ve.current=i;const a=new r({transport:o,enableMic:!0,enableCam:!1,callbacks:Pe(e)});if(a.setLogLevel(n.WARN),t())return void a.disconnect().catch(()=>{});fe.current=a,D.debug({message:"Voice RTVI client constructed",api_name:"voice-rtvi",conversation_id:E,enable_mic:he.current}),Be(),be.current=setTimeout(()=>{D.warn({message:"Voice RTVI connect timed out (no bot-ready)",api_name:"voice-rtvi"}),H("Voice connection timed out. Please try again."),F(l()),je.current()},T),await a.connect({webrtcRequestParams:{endpoint:j.WEBRTC_OFFER(E),headers:new Headers(Q())},iceConfig:{iceServers:c}})}catch(t){const n=(e=>{const r="object"==typeof e&&null!==e&&"status"in e?e.status:void 0;return"number"==typeof r?r:void 0})(t);if(401===n&&e===we.current&&xe.current<d)return D.warn({message:"Voice RTVI offer refused with 401 — refreshing token",api_name:"voice-rtvi",conversation_id:E}),Me.current&&(clearTimeout(Me.current),Me.current=null),Ce.current=0,Le(!1),void Oe(r,"offer_401");D.error({message:"Voice RTVI connect failed",api_name:"voice-rtvi",status:n,error_message:t instanceof Error?t.message:String(t)});const i=(e=>void 0!==e&&e>=400&&e<500)(n),c=i?await(async(e,r)=>{const t=429===r?"Voice session limit reached. Please try again later.":m,n="object"==typeof e&&null!==e&&"cause"in e?e.cause:void 0;if(!(n instanceof Response))return t;try{const e=await n.json(),r=e?.data?.error_message??e?.data?.message;return"string"==typeof r?r:t}catch{return t}})(t,n):m;e===we.current&&(H(c),i&&(Me.current&&(clearTimeout(Me.current),Me.current=null),Ce.current=0,F(l())),Le(!1))}},[E,F,P,Pe,Be,Le,U,Oe]),Fe=y((e=!0)=>{D.info({message:"Voice RTVI disconnect (intentional teardown)",api_name:"voice-rtvi",conversation_id:E??void 0,commit_turn:e}),Ee.current+=1,we.current+=1,ye.current=0,Me.current&&(clearTimeout(Me.current),Me.current=null),Ce.current=0,xe.current=0,Ne.current=null,he.current=!0,F(R(!1));const r=fe.current;r?.disconnect().catch(e=>{D.warn({message:"Voice RTVI disconnect threw",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}),Le(e)},[Le,F,E]),Ge=y(e=>{he.current=e,fe.current&&(D.debug({message:"Voice mic enable toggled",api_name:"voice-rtvi",enabled:e}),fe.current.enableMic(e))},[]),qe=y(e=>{F(S(e)),fe.current?.updateMic(e)},[F]),We=y(e=>{F(w(e)),fe.current?.updateSpeaker(e)},[F]),He=y((e,r)=>{const t=fe.current;if(!t)return;const n=r?.modelContext;D.info({message:"Voice RTVI send-text",api_name:"voice-rtvi",conversation_id:E??void 0,run_immediately:!0,has_model_context:Boolean(n?.length)});try{const r=Ve.current;n?.length&&r?t.transport.sendMessage(new r("send-text",{content:e,options:{run_immediately:!0,audio_response:!0},model_context:n})):t.sendText(e,{run_immediately:!0,audio_response:!0})}catch(e){return void D.error({message:"Voice RTVI send-text failed",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}ne.current&&se(),te.current.setMessages(t=>[...t,v(e,void 0,r?.documents,r?.messageType)]),F(g(!0)),F(p(null)),ce(),F(i(c.PROCESSING))},[F,ce,se,ne,te,E]);M(()=>{je.current=Fe},[Fe]),M(()=>{De.current=Ue},[Ue]);const Ke=k(E),Ze=k(!1);M(()=>{const e=Ke.current;if(Ke.current=E,e===E)return;const r=ke.current;r&&(Ze.current=!0),r&&Fe(!1),Ze.current&&null!=E&&(Ze.current=!1,Ue())},[E,Fe,Ue]);const $e=k(ee);return M(()=>{const e=$e.current;$e.current=ee,e&&!ee&&ke.current&&Fe(!1)},[ee,Fe]),M(()=>()=>{je.current()},[]),{connect:Ue,disconnect:Fe,sendText:He,setMicEnabled:Ge,setInputDevice:qe,setOutputDevice:We}}(O,P,U,F);return E(G,{voiceBridge:P})};export{Y as useVoiceProviderValue};
|
|
1
|
+
import{u as e,s as r,a as t,b as n,c,V as i,d as o,e as a,f as s,g as u,h as d,i as m,j as l,m as v,k as g,l as p,n as _,o as f,p as V,q as h,r as I,t as b,v as T,w as R,x as S,y as E,z as k}from"./sessionHelpers-CK66mJIw.js";import{useRef as w,useCallback as y,useEffect as M}from"react";import{useDispatch as C,useStore as x,useSelector as N}from"react-redux";import{u as B,f as L,a as A}from"./useVoiceSessionCore-C9xo14Pi.js";import{API_ENDPOINTS as j,logger as D,getCustomHeaders as O,readAccessToken as P,AUTH_HEADERS as U,readDeviceToken as F,DEVICE_TOKEN_HEADER as G,INDICATOR_TIMER_ACTIONS as q,getTokenRefreshHandler as W,showErrorToast as H,generateUniqueId as K,handleSharedEvent as Z,showToast as $,isTokenAboutToExpire as z,readTokenExpiresAt as J}from"@1interface/shared-core";function Q(){const e={};for(const[r,t]of Object.entries(O()))t&&(e[r]=t);const r=P();r&&(e[U.ACCESS_TOKEN]=r);const t=F();return t&&(e[G]=t),e}async function X(e){try{const r=await fetch(j.WEBRTC_ICE(e),{headers:Q()});return r.ok?(await r.json()).iceServers??[]:(D.warn({message:"Voice ICE fetch failed — continuing without TURN",api_name:"voice-webrtc-ice",status:r.status}),[])}catch(e){return D.warn({message:"Voice ICE fetch error — continuing without TURN",api_name:"voice-webrtc-ice",error_message:e instanceof Error?e.message:String(e)}),[]}}const Y=({conversationId:O,voiceBridge:P,transportFactory:U,assistantStartsConversation:F})=>{const G=function(k,O,P,U){const F=C(),G=x(),Y=e(),ee=N(e=>e.auth.accessToken),re=w(null),{bridgeRef:te,botTurnMsgIdRef:ne,liveSegmentsRef:ce,startResponseTimer:ie,clearIndicator:oe,sharedDeps:ae,commitBotTurn:se,discardBotTurn:ue,openBotTurnIfNeeded:de,ingestBotSegment:me,routeServerMessage:le}=B(k,O,{apiName:"voice-rtvi",onSessionEnded:e=>{re.current={reason:e}}}),ve=w(!1),ge=w(!1),pe=w(!1),_e=w([]),fe=w(null),Ve=w(""),he=w(null),Ie=w(null),be=w(null),Te=w(null),Re=w(!0),Se=w(null),Ee=w(!1),ke=w(""),we=w(0),ye=w(0),Me=w(!1),Ce=w(0),xe=w(null),Ne=w(0),Be=w(0),Le=w(null),Ae=y(()=>{Te.current&&(clearTimeout(Te.current),Te.current=null)},[]),je=y(e=>{Ae(),Se.current&&(clearTimeout(Se.current),Se.current=null),Ee.current=!1,ke.current="",F(r([])),F(t([])),be.current?.(),be.current=null,he.current=null,Me.current=!1,F(n(!1)),F(c(i.IDLE)),F(o(a.DISCONNECTED)),F(s(null)),F(u(!1)),fe.current=null,Ve.current="",ve.current=!1,ge.current=!1,pe.current=!1,_e.current=[],e?se():ue(),oe(q.CANCEL)},[F,oe,Ae,se,ue]),De=y(e=>{const r=e.spoken_progress?(e.spoken_progress?.accumulated_text??"")+(e.spoken_progress?.remaining_text??""):e.text??"";if(!r)return;de();const t=ve.current?L(ce.current,r):A(ce.current,e,r,ge.current);me(t)},[de,me,ce]),Oe=w(()=>{}),Pe=w(async()=>{}),Ue=y((e,r)=>Be.current>=d||null==k?(D.warn({message:"Voice RTVI auth refresh budget exhausted — giving up",api_name:"voice-rtvi",conversation_id:k??void 0,trigger:r}),!1):(Be.current+=1,D.info({message:"Voice RTVI refreshing token then reconnecting",api_name:"voice-rtvi",conversation_id:k??void 0,auth_refresh_count:Be.current,trigger:r}),(async()=>{let r=!1;try{r=!!await(W()?.())}catch{}e===ye.current&&(r?Pe.current():(D.warn({message:"Voice RTVI token refresh failed — no reconnect",api_name:"voice-rtvi",conversation_id:k??void 0}),H(m),F(l())))})(),!0),[k,F]),Fe=y(e=>{const d=()=>e===we.current;return{onConnected:()=>{if(!d())return;D.info({message:"Voice RTVI connected",api_name:"voice-rtvi",conversation_id:k??void 0}),F(o(a.CONNECTED)),F(n(!0)),F(c(i.IDLE)),he.current&&he.current.isMicEnabled!==Re.current&&(D.debug({message:"Voice re-asserting mic state on reconnect",api_name:"voice-rtvi",conversation_id:k??void 0,enabled:Re.current}),he.current.enableMic(Re.current));const{selectedMicId:e,selectedSpeakerId:s}=G.getState().voice;e&&he.current?.updateMic(e),s&&he.current?.updateSpeaker(s);const u=e=>r=>D.warn({message:"Voice device enumeration failed",api_name:"voice-mic",conversation_id:k??void 0,device_kind:e,error_message:r instanceof Error?r.message:String(r)});he.current?.getAllMics().then(e=>{d()&&F(r(e.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(u("mics")),he.current?.getAllSpeakers().then(e=>{d()&&F(t(e.map(e=>({deviceId:e.deviceId,label:e.label}))))}).catch(u("speakers")),Ee.current=!0},onBotReady:e=>{if(!d())return;const r=e?.about;ve.current=r?.pipeline_mode===b.REALTIME,ge.current=!0===r?.tts_word_timestamps;const t=null!==ne.current||_e.current.length>0;pe.current=!0;for(const e of _e.current.splice(0))"output"===e.kind?De(e.data):"turnEnd"===e.kind?se():te.current.setMessages(r=>[...r,e.message]);Ne.current=0,Be.current=0,Ce.current=0,D.info({message:"Voice RTVI bot-ready",api_name:"voice-rtvi",conversation_id:k??void 0,pipeline_mode:r?.pipeline_mode??"cascade",tts_word_timestamps:ge.current,assistant_starts:U}),Ae(),U&&!t&&(F(g(!0)),ie())},onDisconnected:()=>{if(!d())return;D.info({message:"Voice RTVI disconnected",api_name:"voice-rtvi",conversation_id:k??void 0,drop_outcome:Le.current??"transient",reconnect_attempts:Ne.current}),je(!0);const e=ye.current,r=Le.current;Le.current=null;const t=re.current;if(re.current=null,t)return D.info({message:"Voice session ended by server — no reconnect",api_name:"voice-rtvi",conversation_id:k??void 0,reason:t.reason}),"idle_timeout"===t.reason&&$({title:"Voice ended",description:"It got quiet for a while, so we wrapped up. Start again anytime."}),void F(l());if("terminal"===r)return D.warn({message:"Voice RTVI terminal error — no retry",api_name:"voice-rtvi"}),void F(l());if("auth"!==r){if(Ne.current>=h||null==k)return Ne.current>=h&&(D.warn({message:"Voice RTVI auto-reconnect cap reached — giving up",api_name:"voice-rtvi",conversation_id:k??void 0,reconnect_attempts:Ne.current}),H("Voice connection lost. Please try again.")),void F(l());Ne.current+=1,D.info({message:"Voice RTVI scheduling auto-reconnect",api_name:"voice-rtvi",conversation_id:k??void 0,reconnect_attempts:Ne.current,delay_ms:I}),xe.current&&clearTimeout(xe.current),xe.current=setTimeout(()=>{xe.current=null,Pe.current()},I)}else Ue(e,"auth_drop")||F(l())},onError:e=>{if(!d())return;const r=e?.data,t=r?.error??r?.message??"Voice error";Z({kind:"error",error:t},ae),F(c(i.IDLE)),oe(q.CANCEL),r?.fatal&&(f.some(e=>e.test(t))?Le.current="auth":V.some(e=>e.test(t))&&(Le.current="terminal"),D.warn({message:"Voice RTVI fatal error — closing transport",api_name:"voice-rtvi",conversation_id:k??void 0,drop_outcome:Le.current??"generic"}),he.current?.disconnect().catch(()=>{}))},onTrackStarted:(e,r)=>{d()&&(r?.local||"audio"!==e.kind||(D.debug({message:"Voice RTVI bot audio track started",api_name:"voice-rtvi",conversation_id:k??void 0}),be.current?.(),be.current=Y.attachBotAudioTrack(e)))},onAvailableMicsUpdated:e=>{d()&&(F(r(e.map(e=>({deviceId:e.deviceId,label:e.label})))),Ee.current&&(Se.current&&clearTimeout(Se.current),Se.current=setTimeout(()=>{if(Se.current=null,!he.current)return;const{selectedMicId:e,microphones:r}=G.getState().voice;D.info({message:"Voice audio devices changed",api_name:"voice-mic",microphones:r.map(e=>e.label).join(", ")}),D.debug({message:"Voice re-acquiring mic (device change)",api_name:"voice-rtvi",enabled:Re.current,device:e||"default"}),he.current.updateMic(e)},300)))},onAvailableSpeakersUpdated:e=>{if(!d())return;if(F(t(e.map(e=>({deviceId:e.deviceId,label:e.label})))),!Ee.current)return;const r=e.map(e=>e.label).join(", ");r!==ke.current&&(ke.current=r,D.info({message:"Voice audio output devices changed",api_name:"voice-mic",speakers:r}))},onMicUpdated:e=>{if(!d())return;const r=he.current;r&&r.isMicEnabled!==Re.current&&(D.debug({message:"Voice re-asserting mic state after track swap",api_name:"voice-rtvi",conversation_id:k??void 0,enabled:Re.current,device:e?.label}),r.enableMic(Re.current))},onUserMuteStarted:()=>{d()&&D.debug({message:"Voice RTVI user-mute-started (ignored)",api_name:"voice-rtvi",conversation_id:k??void 0})},onUserMuteStopped:()=>{d()&&(D.debug({message:"Voice RTVI user-mute-stopped",api_name:"voice-rtvi",conversation_id:k??void 0,honored:U}),U&&F(u(!1)))},onUserStartedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI user started speaking",api_name:"voice-rtvi",conversation_id:k??void 0}),fe.current=null,Ve.current="",F(s(null)),F(c(i.LISTENING)))},onUserStoppedSpeaking:()=>{if(!d())return;D.debug({message:"Voice RTVI user stopped speaking",api_name:"voice-rtvi",conversation_id:k??void 0,has_interim:!!Ve.current.trim()}),F(c(i.PROCESSING)),F(g(!0)),F(p(null)),ie();const e=Ve.current.trim();if(e){const r=`${_}${K()}`;fe.current=r,F(s(null)),te.current.setMessages(t=>[...t,v(e,r)])}},onUserTranscript:e=>{if(!d())return;if(!e.final)return void(e.text&&(Ve.current=e.text,ve.current||F(s(e.text))));F(s(null));const r=e.text&&e.text.trim()?e.text:"";D.debug({message:"Voice RTVI user transcript (final)",api_name:"voice-rtvi",conversation_id:k??void 0,has_text:!!r});const t=fe.current;fe.current=null,t?r&&te.current.setMessages(e=>e.map(e=>e.id===t?{...e,message:r}:e)):r&&te.current.setMessages(e=>[...e,v(r)])},onBotStartedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI bot started speaking",api_name:"voice-rtvi",conversation_id:k??void 0}),de(),F(c(i.SPEAKING)),oe(q.FINALIZE))},onBotOutput:e=>{d()&&(pe.current?De(e):_e.current.push({kind:"output",data:e}))},onBotStoppedSpeaking:()=>{d()&&(D.debug({message:"Voice RTVI bot stopped speaking",api_name:"voice-rtvi",conversation_id:k??void 0}),!pe.current&&_e.current.length>0?_e.current.push({kind:"turnEnd"}):se(),F(c(i.IDLE)),oe(q.FINALIZE),U&&G.getState().voice.serverMuted&&(D.warn({message:"Voice greeting mute-hold released on bot-turn end (server unmute missing)",api_name:"voice-rtvi",conversation_id:k??void 0}),F(u(!1))))},onServerMessage:e=>{d()&&le(e)}}},[F,G,Y,Ae,je,oe,ie,De,de,se,ne,le,ae,te,U,k,Ue]),Ge=y(async()=>{if(he.current||Me.current)return;if(!k)return;if(Me.current=!0,D.info({message:"Voice RTVI connecting",api_name:"voice-rtvi",conversation_id:k,assistant_starts:U}),U&&(Re.current=!1,F(u(!0)),D.debug({message:"Voice greeting mute-hold armed",api_name:"voice-rtvi",conversation_id:k})),z(J())&&Ce.current<d){Ce.current+=1,D.debug({message:"Voice pre-connect token refresh (freshness gate)",api_name:"voice-rtvi",conversation_id:k,freshness_refresh_count:Ce.current});const e=ye.current;try{await Promise.resolve(W()?.())}catch{}if(e!==ye.current)return}const e=++we.current;pe.current=!1,_e.current=[];const r=ye.current;re.current=null,Le.current=null,F(o(a.CONNECTING));const t=()=>r!==ye.current||e!==we.current;try{const[{PipecatClient:r,LogLevel:n,RTVIMessage:c},i,o]=await Promise.all([import("@pipecat-ai/client-js"),X(k),P()]);if(t())return;Ie.current=c;const a=new r({transport:o,enableMic:!0,enableCam:!1,callbacks:Fe(e)});if(a.setLogLevel(n.WARN),t())return void a.disconnect().catch(()=>{});he.current=a,D.debug({message:"Voice RTVI client constructed",api_name:"voice-rtvi",conversation_id:k,enable_mic:Re.current}),Ae(),Te.current=setTimeout(()=>{D.warn({message:"Voice RTVI connect timed out (no bot-ready)",api_name:"voice-rtvi"}),H("Voice connection timed out. Please try again."),F(l()),Oe.current()},T),await a.connect({webrtcRequestParams:{endpoint:j.WEBRTC_OFFER(k),headers:new Headers(Q())},iceConfig:{iceServers:i}})}catch(t){const n=(e=>{const r="object"==typeof e&&null!==e&&"status"in e?e.status:void 0;return"number"==typeof r?r:void 0})(t);if(401===n&&e===we.current&&Be.current<d)return D.warn({message:"Voice RTVI offer refused with 401 — refreshing token",api_name:"voice-rtvi",conversation_id:k}),xe.current&&(clearTimeout(xe.current),xe.current=null),Ne.current=0,je(!1),void Ue(r,"offer_401");D.error({message:"Voice RTVI connect failed",api_name:"voice-rtvi",status:n,error_message:t instanceof Error?t.message:String(t)});const c=(e=>void 0!==e&&e>=400&&e<500)(n),i=c?await(async(e,r)=>{const t=429===r?"Voice session limit reached. Please try again later.":m,n="object"==typeof e&&null!==e&&"cause"in e?e.cause:void 0;if(!(n instanceof Response))return t;try{const e=await n.json(),r=e?.data?.error_message??e?.data?.message;return"string"==typeof r?r:t}catch{return t}})(t,n):m;e===we.current&&(H(i),c&&(xe.current&&(clearTimeout(xe.current),xe.current=null),Ne.current=0,F(l())),je(!1))}},[k,F,P,Fe,Ae,je,U,Ue]),qe=y((e=!0)=>{D.info({message:"Voice RTVI disconnect (intentional teardown)",api_name:"voice-rtvi",conversation_id:k??void 0,commit_turn:e}),ye.current+=1,we.current+=1,Ce.current=0,xe.current&&(clearTimeout(xe.current),xe.current=null),Ne.current=0,Be.current=0,Le.current=null,Re.current=!0,F(R(!1));const r=he.current;r?.disconnect().catch(e=>{D.warn({message:"Voice RTVI disconnect threw",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}),je(e)},[je,F,k]),We=y(e=>{Re.current=e,he.current&&(D.debug({message:"Voice mic enable toggled",api_name:"voice-rtvi",enabled:e}),he.current.enableMic(e))},[]),He=y(e=>{F(S(e)),he.current?.updateMic(e)},[F]),Ke=y(e=>{F(E(e)),he.current?.updateSpeaker(e)},[F]),Ze=y((e,r)=>{const t=he.current;if(!t)return;const n=r?.modelContext;D.info({message:"Voice RTVI send-text",api_name:"voice-rtvi",conversation_id:k??void 0,run_immediately:!0,has_model_context:Boolean(n?.length)});try{const r=Ie.current;n?.length&&r?t.transport.sendMessage(new r("send-text",{content:e,options:{run_immediately:!0,audio_response:!0},model_context:n})):t.sendText(e,{run_immediately:!0,audio_response:!0})}catch(e){return void D.error({message:"Voice RTVI send-text failed",api_name:"voice-rtvi",error_message:e instanceof Error?e.message:String(e)})}const o=v(e,void 0,r?.documents,r?.messageType);!pe.current&&_e.current.length>0?_e.current.push({kind:"turnEnd"},{kind:"userRow",message:o}):(ne.current&&se(),te.current.setMessages(e=>[...e,o])),F(g(!0)),F(p(null)),ie(),F(c(i.PROCESSING))},[F,ie,se,ne,te,k]);M(()=>{Oe.current=qe},[qe]),M(()=>{Pe.current=Ge},[Ge]);const $e=w(k),ze=w(!1);M(()=>{const e=$e.current;if($e.current=k,e===k)return;const r=Me.current;r&&(ze.current=!0),r&&qe(!1),ze.current&&null!=k&&(ze.current=!1,Ge())},[k,qe,Ge]);const Je=w(ee);return M(()=>{const e=Je.current;Je.current=ee,e&&!ee&&Me.current&&qe(!1)},[ee,qe]),M(()=>()=>{Oe.current()},[]),{connect:Ge,disconnect:qe,sendText:Ze,setMicEnabled:We,setInputDevice:He,setOutputDevice:Ke}}(O,P,U,F);return k(G,{voiceBridge:P})};export{Y as useVoiceProviderValue};
|