@lunora/react 1.0.0-alpha.80 → 1.0.0-alpha.81
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/index.d.mts
CHANGED
|
@@ -1266,7 +1266,11 @@ interface UseVoiceAgentOptions {
|
|
|
1266
1266
|
* Audio graph stays isolated (and mockable in a non-browser test env).
|
|
1267
1267
|
*/
|
|
1268
1268
|
createMicrophone?: CreateMicrophone;
|
|
1269
|
-
/**
|
|
1269
|
+
/**
|
|
1270
|
+
* Advanced/test seam: open the transport. Defaults to the WebSocket
|
|
1271
|
+
* implementation the client was built with (`client.getWebSocketImpl()`),
|
|
1272
|
+
* NOT a raw `globalThis.WebSocket`.
|
|
1273
|
+
*/
|
|
1270
1274
|
createSocket?: CreateSocket;
|
|
1271
1275
|
/** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
|
|
1272
1276
|
createSpeaker?: CreateSpeaker;
|
package/dist/index.d.ts
CHANGED
|
@@ -1266,7 +1266,11 @@ interface UseVoiceAgentOptions {
|
|
|
1266
1266
|
* Audio graph stays isolated (and mockable in a non-browser test env).
|
|
1267
1267
|
*/
|
|
1268
1268
|
createMicrophone?: CreateMicrophone;
|
|
1269
|
-
/**
|
|
1269
|
+
/**
|
|
1270
|
+
* Advanced/test seam: open the transport. Defaults to the WebSocket
|
|
1271
|
+
* implementation the client was built with (`client.getWebSocketImpl()`),
|
|
1272
|
+
* NOT a raw `globalThis.WebSocket`.
|
|
1273
|
+
*/
|
|
1270
1274
|
createSocket?: CreateSocket;
|
|
1271
1275
|
/** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
|
|
1272
1276
|
createSpeaker?: CreateSpeaker;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import{AuthLoading as o,Authenticated as t,Unauthenticated as u}from"./packem_shared/AuthLoading-dC08yqew.mjs";import{useAuthState as a}from"./packem_shared/useAuthState-CoOG9XdT.mjs";import{LunoraProvider as f,useLunora as n}from"./packem_shared/LunoraProvider-DC06S-p1.mjs";import{lunoraQueryOptions as m}from"./packem_shared/lunoraQueryOptions-B5PEocLy.mjs";import{useAction as x}from"./packem_shared/useAction-CUSJu4yq.mjs";import{useAuthSessions as A,useAuthUsers as g,useImpersonate as h,useOrganizations as c}from"./packem_shared/useAuthSessions-Bq4-f3g-.mjs";import{useAgent as y}from"./packem_shared/useAgent-CNDRZNk_.mjs";import{useAgentChat as E}from"./packem_shared/useAgentChat-CXJldvLG.mjs";import{useAgentState as S}from"./packem_shared/useAgentState-BOIQjgxe.mjs";import{useAgentToolEvents as L}from"./packem_shared/useAgentToolEvents-B4gy1Q5z.mjs";import{default as F}from"./packem_shared/useAuth-x4boOoAR.mjs";import{default as I}from"./packem_shared/useClientQuery-DFs7trEf.mjs";import{default as v}from"./packem_shared/useConnectionStatus-XYsYGiT6.mjs";import{useFlag as O,useFlags as T}from"./packem_shared/useFlag-BDM923ke.mjs";import{useHttpStream as H}from"./packem_shared/useHttpStream-D9vum8Qx.mjs";import{default as j}from"./packem_shared/useInfiniteQuery-C7wp0W2d.mjs";import{useMutation as w}from"./packem_shared/useMutation-BnKec7bE.mjs";import{useMutator as D}from"./packem_shared/useMutator-BtUd-xEA.mjs";import{usePaginatedQuery as J}from"./packem_shared/usePaginatedQuery-DJNa8KKD.mjs";import{hydratePreloaded as N,default as W}from"./packem_shared/hydratePreloaded-CcBW4phD.mjs";import{usePresence as Y}from"./packem_shared/usePresence-BVjZyltT.mjs";import{default as _}from"./packem_shared/useQuery-BUzNP_Vb.mjs";import{useRateLimit as ee}from"./packem_shared/useRateLimit-Cckxi8m9.mjs";import{useStream as oe}from"./packem_shared/useStream-CmGOT0qj.mjs";import{default as ue}from"./packem_shared/useSubscription-BHVICtbD.mjs";import{useVoiceAgent as ae}from"./packem_shared/useVoiceAgent-
|
|
2
|
+
import{AuthLoading as o,Authenticated as t,Unauthenticated as u}from"./packem_shared/AuthLoading-dC08yqew.mjs";import{useAuthState as a}from"./packem_shared/useAuthState-CoOG9XdT.mjs";import{LunoraProvider as f,useLunora as n}from"./packem_shared/LunoraProvider-DC06S-p1.mjs";import{lunoraQueryOptions as m}from"./packem_shared/lunoraQueryOptions-B5PEocLy.mjs";import{useAction as x}from"./packem_shared/useAction-CUSJu4yq.mjs";import{useAuthSessions as A,useAuthUsers as g,useImpersonate as h,useOrganizations as c}from"./packem_shared/useAuthSessions-Bq4-f3g-.mjs";import{useAgent as y}from"./packem_shared/useAgent-CNDRZNk_.mjs";import{useAgentChat as E}from"./packem_shared/useAgentChat-CXJldvLG.mjs";import{useAgentState as S}from"./packem_shared/useAgentState-BOIQjgxe.mjs";import{useAgentToolEvents as L}from"./packem_shared/useAgentToolEvents-B4gy1Q5z.mjs";import{default as F}from"./packem_shared/useAuth-x4boOoAR.mjs";import{default as I}from"./packem_shared/useClientQuery-DFs7trEf.mjs";import{default as v}from"./packem_shared/useConnectionStatus-XYsYGiT6.mjs";import{useFlag as O,useFlags as T}from"./packem_shared/useFlag-BDM923ke.mjs";import{useHttpStream as H}from"./packem_shared/useHttpStream-D9vum8Qx.mjs";import{default as j}from"./packem_shared/useInfiniteQuery-C7wp0W2d.mjs";import{useMutation as w}from"./packem_shared/useMutation-BnKec7bE.mjs";import{useMutator as D}from"./packem_shared/useMutator-BtUd-xEA.mjs";import{usePaginatedQuery as J}from"./packem_shared/usePaginatedQuery-DJNa8KKD.mjs";import{hydratePreloaded as N,default as W}from"./packem_shared/hydratePreloaded-CcBW4phD.mjs";import{usePresence as Y}from"./packem_shared/usePresence-BVjZyltT.mjs";import{default as _}from"./packem_shared/useQuery-BUzNP_Vb.mjs";import{useRateLimit as ee}from"./packem_shared/useRateLimit-Cckxi8m9.mjs";import{useStream as oe}from"./packem_shared/useStream-CmGOT0qj.mjs";import{default as ue}from"./packem_shared/useSubscription-BHVICtbD.mjs";import{useVoiceAgent as ae}from"./packem_shared/useVoiceAgent-C9NI6Vzl.mjs";import{createClientQuery as fe,getErrorCode as ne,getRetryAfterMs as ie,isConflictError as me,isForbiddenError as de,isRateLimitedError as xe,isUnauthorizedError as le}from"@lunora/client";import{RestrictionError as ge,UploadControl as he,UploadError as ce}from"@visulima/storage-client";import{useChunkedRestUpload as ye,useFileInput as Ce,useMultipartUpload as Ee,usePasteUpload as Qe,useTusUpload as Se,useUpload as Pe}from"@visulima/storage-client/react";export{o as AuthLoading,t as Authenticated,f as LunoraProvider,ge as RestrictionError,u as Unauthenticated,he as UploadControl,ce as UploadError,fe as createClientQuery,ne as getErrorCode,ie as getRetryAfterMs,N as hydratePreloaded,me as isConflictError,de as isForbiddenError,xe as isRateLimitedError,le as isUnauthorizedError,m as lunoraQueryOptions,x as useAction,y as useAgent,E as useAgentChat,S as useAgentState,L as useAgentToolEvents,F as useAuth,A as useAuthSessions,a as useAuthState,g as useAuthUsers,ye as useChunkedRestUpload,I as useClientQuery,v as useConnectionStatus,Ce as useFileInput,O as useFlag,T as useFlags,H as useHttpStream,h as useImpersonate,j as useInfiniteQuery,n as useLunora,Ee as useMultipartUpload,w as useMutation,D as useMutator,c as useOrganizations,J as usePaginatedQuery,Qe as usePasteUpload,W as usePreloadedQuery,Y as usePresence,_ as useQuery,ee as useRateLimit,oe as useStream,ue as useSubscription,Se as useTusUpload,Pe as useUpload,ae as useVoiceAgent};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import{useState as w,useRef as N,useCallback as E,useEffect as G}from"react";import{useLunora as X}from"./LunoraProvider-DC06S-p1.mjs";const O="/_lunora/ws",j="/_lunora/voice/",z=4001,P=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,Q=e=>{const n=e.startsWith("agents:")?e.slice(7):e;return n.endsWith("Voice")?n.slice(0,-5):n},Y=(e,n)=>{if(n===void 0||n==="")return P(e);if(n.endsWith(O))return n.slice(0,-O.length);try{return new URL(n).origin}catch{return P(e)}},Z=e=>{const n=Y(e.httpUrl,e.wsUrl),s=n.endsWith("/")?n.slice(0,-1):n,i=new URLSearchParams({threadKey:e.threadKey});return`${s}${j}${encodeURIComponent(e.agent)}?${i.toString()}`},ee=(e,n)=>{if(n?.code===z)return new Error(`${e}: authentication token expired — refresh the credential and start a new call`)},te=16e3,ne=e=>{if(e.length===0)return 0;let n=0;for(const s of e)n+=s*s;return Math.sqrt(n/e.length)},re=(e,n)=>{const s=n/te,i=s>1?Math.floor(e.length/s):e.length,d=new ArrayBuffer(i*2),l=new DataView(d);for(let a=0;a<i;a+=1){const k=e[Math.floor(a*s)]??0,p=Math.max(-1,Math.min(1,k));l.setInt16(a*2,p<0?p*32768:p*32767,!0)}return new Uint8Array(d)},oe=async e=>{const n=globalThis,s=n.navigator?.mediaDevices?.getUserMedia.bind(n.navigator.mediaDevices),i=n.AudioContext??n.webkitAudioContext;if(!s||!i)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const d=await s({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),l=new i,a=l.createMediaStreamSource(d),k=l.createScriptProcessor(4096,1,1);let p=!1,g=!1,u=0,h=0;return k.onaudioprocess=o=>{const f=o.inputBuffer.getChannelData(0),m=p?0:ne(f);if(e.onLevel(m),p)return;if(e.onAudio(re(f,l.sampleRate)),e.isSpeaking()){h=m>=e.interruptThreshold?h+1:0,h>=e.interruptChunks&&(h=0,e.onInterrupt());return}h=0;const x=f.length/l.sampleRate*1e3;if(m>=e.silenceThreshold){g=!0,u=0;return}g&&(u+=x,u>=e.silenceDurationMs&&(g=!1,u=0,e.onSilence()))},a.connect(k),k.connect(l.destination),{setMuted:o=>{p=o},stop:()=>{k.disconnect(),a.disconnect();for(const o of d.getTracks())o.stop();l.close()}}},se=()=>{const e=globalThis,n=e.AudioContext??e.webkitAudioContext;if(!n)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const s=new n,i=new Set;let d=0,l=Promise.resolve(),a=0;const k=async(u,h)=>{if(h!==a)return;let o;try{o=await s.decodeAudioData(u.buffer)}catch{return}if(h!==a)return;const f=s.createBufferSource();f.buffer=o,f.connect(s.destination);const m=Math.max(s.currentTime,d);f.start(m),d=m+o.duration,i.add(f),f.onended=()=>{i.delete(f)}},p=u=>{const h=Uint8Array.from(u),o=a;l=l.then(()=>k(h,o))},g=()=>{a+=1;for(const u of i)try{u.stop()}catch{}i.clear(),d=s.currentTime};return{enqueue:p,interrupt:g,stop:()=>{g(),s.close()}}},V=1,ce=.01,ie=1200,ae=.15,ue=3,pe=e=>{const{createMicrophone:n=oe,createSpeaker:s=se,createSocket:i,interruptChunks:d=ue,interruptThreshold:l=ae,silenceDurationMs:a=ie,silenceThreshold:k=ce,threadKey:p,voice:g}=e,u=X(),[h,o]=w("idle"),[f,m]=w(!1),[x,R]=w(""),[H,v]=w(""),[$,U]=w(0),[M,L]=w(!1),[B,C]=w(void 0),c=N(void 0),_=N(!1),T=E(t=>{const r=c.current?.socket;return r?.readyState===V?(r.send(JSON.stringify(t)),!0):!1},[]),y=E(()=>{const t=c.current;if(c.current=void 0,t){t.microphone?.stop(),t.speaker?.stop(),t.socket.onmessage=null,t.socket.onerror=null,t.socket.onclose=null;try{t.socket.close()}catch{}}_.current=!1,m(!1),o("idle"),U(0)},[]),D=E(t=>{const r=c.current;switch(t.type){case"assistant_delta":{r&&(r.speaking=!0),o("speaking"),v(F=>F+t.text);break}case"assistant_done":{r&&(r.speaking=!1),v(t.text),o("listening");break}case"error":{r&&(r.speaking=!1),C(new Error(t.message)),o("listening");break}case"interrupted":{r&&(r.speaking=!1,r.suppressAudio=!1),r?.speaker?.interrupt(),o("listening");break}case"ready":{r&&(r.audioFormat=t.audioFormat,r.suppressAudio=!1),m(!0),o("listening");break}case"user_transcript":{r&&(r.suppressAudio=!1),R(t.text),v(""),o("thinking");break}}},[]),I=E(t=>{const r=c.current;!r||r.suppressAudio||(r.speaker??=s({audioFormat:r.audioFormat}),r.speaking=!0,o("speaking"),r.speaker.enqueue(t))},[s]),q=E(async()=>{if(c.current||_.current)return;_.current=!0,C(void 0),R(""),v("");let t;try{const r=Z({agent:Q(g.__lunoraRef),httpUrl:u.url,threadKey:p,wsUrl:u.wsUrl}),A=(i??(S=>{const b=u.getWebSocketImpl();if(!b)throw new Error("useVoiceAgent: no WebSocket implementation available (pass createSocket explicitly)");return new b(S)}))(r);A.binaryType="arraybuffer",t={audioFormat:"mp3",microphone:void 0,socket:A,speaker:void 0,speaking:!1,suppressAudio:!1},c.current=t,A.onmessage=S=>{if(typeof S.data=="string"){try{D(JSON.parse(S.data))}catch{}return}I(new Uint8Array(S.data))},A.onerror=()=>{C(new Error("useVoiceAgent: voice socket error"))},A.onclose=S=>{if(c.current!==t)return;const b=ee("useVoiceAgent",S);b&&C(b),y()};const W=await n({interruptChunks:d,interruptThreshold:l,isSpeaking:()=>c.current?.speaking??!1,onAudio:S=>{A.readyState===V&&A.send(S)},onInterrupt:()=>{T({type:"interrupt"}),c.current?.speaker?.interrupt(),c.current&&(c.current.speaking=!1,c.current.suppressAudio=!0),o("listening")},onLevel:U,onSilence:()=>{T({type:"commit"}),o("thinking")},silenceDurationMs:a,silenceThreshold:k});c.current===t?(t.microphone=W,L(!1),t.speaking||o("listening")):W.stop()}catch(r){c.current===t&&(C(r instanceof Error?r:new Error(String(r))),y())}finally{_.current=!1}},[u,n,i,I,D,d,l,T,a,k,y,p,g]),K=E(()=>{const t=!M;return c.current?.microphone?.setMuted(t),L(t),t},[M]),J=E(t=>{T({text:t,type:"text"})&&o("thinking")},[T]);return G(()=>y,[y]),{audioLevel:$,connected:f,endCall:y,error:B,interimTranscript:H,isMuted:M,sendText:J,startCall:q,status:h,toggleMute:K,transcript:x}};export{pe as useVoiceAgent};
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import{useState as w,useRef as W,useCallback as b,useEffect as J}from"react";import{useLunora as K}from"./LunoraProvider-DC06S-p1.mjs";const G=16e3,j=e=>{if(e.length===0)return 0;let o=0;for(const r of e)o+=r*r;return Math.sqrt(o/e.length)},z=(e,o)=>{const r=o/G,c=r>1?Math.floor(e.length/r):e.length,l=new ArrayBuffer(c*2),i=new DataView(l);for(let a=0;a<c;a+=1){const k=e[Math.floor(a*r)]??0,p=Math.max(-1,Math.min(1,k));i.setInt16(a*2,p<0?p*32768:p*32767,!0)}return new Uint8Array(l)},Q=async e=>{const o=globalThis,r=o.navigator?.mediaDevices?.getUserMedia.bind(o.navigator.mediaDevices),c=o.AudioContext??o.webkitAudioContext;if(!r||!c)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const l=await r({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),i=new c,a=i.createMediaStreamSource(l),k=i.createScriptProcessor(4096,1,1);let p=!1,g=!1,d=0,f=0;return k.onaudioprocess=s=>{const h=s.inputBuffer.getChannelData(0),m=p?0:j(h);if(e.onLevel(m),p)return;if(e.onAudio(z(h,i.sampleRate)),e.isSpeaking()){f=m>=e.interruptThreshold?f+1:0,f>=e.interruptChunks&&(f=0,e.onInterrupt());return}f=0;const x=h.length/i.sampleRate*1e3;if(m>=e.silenceThreshold){g=!0,d=0;return}g&&(d+=x,d>=e.silenceDurationMs&&(g=!1,d=0,e.onSilence()))},a.connect(k),k.connect(i.destination),{setMuted:s=>{p=s},stop:()=>{k.disconnect(),a.disconnect();for(const s of l.getTracks())s.stop();i.close()}}},X=()=>{const e=globalThis,o=e.AudioContext??e.webkitAudioContext;if(!o)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const r=new o,c=new Set;let l=0,i=Promise.resolve(),a=0;const k=async(d,f)=>{if(f!==a)return;let s;try{s=await r.decodeAudioData(d.buffer)}catch{return}if(f!==a)return;const h=r.createBufferSource();h.buffer=s,h.connect(r.destination);const m=Math.max(r.currentTime,l);h.start(m),l=m+s.duration,c.add(h),h.onended=()=>{c.delete(h)}},p=d=>{const f=Uint8Array.from(d),s=a;i=i.then(()=>k(f,s))},g=()=>{a+=1;for(const d of c)try{d.stop()}catch{}c.clear(),l=r.currentTime};return{enqueue:p,interrupt:g,stop:()=>{g(),r.close()}}},P=1,Y=.01,Z=1200,ee=.15,te=3,ne=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,re=e=>{const o=e.__lunoraRef,r=o.startsWith("agents:")?o.slice(7):o;return r.endsWith("Voice")?r.slice(0,-5):r},se=(e,o,r)=>{const c=ne(e),l=c.endsWith("/")?c.slice(0,-1):c,i=new URLSearchParams({threadKey:r});return`${l}/_lunora/voice/${encodeURIComponent(o)}?${i.toString()}`},ie=e=>{const{createMicrophone:o=Q,createSpeaker:r=X,createSocket:c,interruptChunks:l=te,interruptThreshold:i=ee,silenceDurationMs:a=Z,silenceThreshold:k=Y,threadKey:p,voice:g}=e,d=K(),[f,s]=w("idle"),[h,m]=w(!1),[x,U]=w(""),[V,C]=w(""),[H,_]=w(0),[R,L]=w(!1),[O,v]=w(void 0),u=W(void 0),E=W(!1),T=b(t=>{const n=u.current?.socket;return n?.readyState===P?(n.send(JSON.stringify(t)),!0):!1},[]),y=b(()=>{const t=u.current;if(u.current=void 0,t){t.microphone?.stop(),t.speaker?.stop();try{t.socket.close()}catch{}}E.current=!1,m(!1),s("idle"),_(0)},[]),D=b(t=>{const n=u.current;switch(t.type){case"assistant_delta":{n&&(n.speaking=!0),s("speaking"),C(S=>S+t.text);break}case"assistant_done":{n&&(n.speaking=!1),C(t.text),s("listening");break}case"error":{n&&(n.speaking=!1),v(new Error(t.message)),s("listening");break}case"interrupted":{n&&(n.speaking=!1,n.suppressAudio=!1),n?.speaker?.interrupt(),s("listening");break}case"ready":{n&&(n.audioFormat=t.audioFormat,n.suppressAudio=!1),m(!0),s("listening");break}case"user_transcript":{n&&(n.suppressAudio=!1),U(t.text),C(""),s("thinking");break}}},[]),F=b(t=>{const n=u.current;!n||n.suppressAudio||(n.speaker??=r({audioFormat:n.audioFormat}),n.speaking=!0,s("speaking"),n.speaker.enqueue(t))},[r]),q=b(async()=>{if(!(u.current||E.current)){E.current=!0,v(void 0),U(""),C("");try{const t=se(d.url,re(g),p),S=(c??(A=>{const N=d.getWebSocketImpl();if(!N)throw new Error("useVoiceAgent: no WebSocket implementation available (pass createSocket explicitly)");return new N(A)}))(t);S.binaryType="arraybuffer";const M={audioFormat:"mp3",microphone:void 0,socket:S,speaker:void 0,speaking:!1,suppressAudio:!1};u.current=M,S.onmessage=A=>{if(typeof A.data=="string"){try{D(JSON.parse(A.data))}catch{}return}F(new Uint8Array(A.data))},S.onerror=()=>{v(new Error("useVoiceAgent: voice socket error"))},S.onclose=()=>{u.current===M&&y()};const I=await o({interruptChunks:l,interruptThreshold:i,isSpeaking:()=>u.current?.speaking??!1,onAudio:A=>{S.readyState===P&&S.send(A)},onInterrupt:()=>{T({type:"interrupt"}),u.current?.speaker?.interrupt(),u.current&&(u.current.speaking=!1,u.current.suppressAudio=!0),s("listening")},onLevel:_,onSilence:()=>{T({type:"commit"}),s("thinking")},silenceDurationMs:a,silenceThreshold:k});u.current===M?(M.microphone=I,L(!1),s("listening")):I.stop()}catch(t){v(t instanceof Error?t:new Error(String(t))),y()}finally{E.current=!1}}},[d,o,c,F,D,l,i,T,a,k,y,p,g]),B=b(()=>{const t=!R;return u.current?.microphone?.setMuted(t),L(t),t},[R]),$=b(t=>{T({text:t,type:"text"})&&s("thinking")},[T]);return J(()=>y,[y]),{audioLevel:H,connected:h,endCall:y,error:O,interimTranscript:V,isMuted:R,sendText:$,startCall:q,status:f,toggleMute:B,transcript:x}};export{ie as useVoiceAgent};
|