codicent-app-sdk 0.3.102 → 0.3.104

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.
@@ -31,6 +31,7 @@ export interface RealtimeVoice {
31
31
  items: ItemType[];
32
32
  realtimeEvents: RealtimeEvent[];
33
33
  isConnected: boolean;
34
+ isSessionReady: boolean;
34
35
  canPushToTalk: boolean;
35
36
  isRecording: boolean;
36
37
  clientCanvasRef: React.RefObject<HTMLCanvasElement>;
@@ -1 +1 @@
1
- {"version":3,"file":"useRealtimeVoiceAI.d.ts","sourceRoot":"","sources":["../../../src/hooks/useRealtimeVoiceAI.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAK/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,CAAC;CACH;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,sBAAsB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,sBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,kBAAkB,oBACL,eAAe,UACxB,MAAM,SACP;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,EAAE,UACtD,MAAM,KACb,aAAa,GAAG,SA0pBlB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useRealtimeVoiceAI.d.ts","sourceRoot":"","sources":["../../../src/hooks/useRealtimeVoiceAI.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAK/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,CAAC;CACH;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,sBAAsB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,sBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,kBAAkB,oBACL,eAAe,UACxB,MAAM,SACP;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,EAAE,UACtD,MAAM,KACb,aAAa,GAAG,SA8qBlB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("../utils/wav_renderer.js");require("../lib/wavtools/lib/wav_packer.js"),require("../lib/wavtools/lib/analysis/audio_analysis.js");var n=require("../lib/wavtools/lib/wav_stream_player.js"),r=require("../lib/wavtools/lib/wav_recorder.js"),a=require("../config/index.js");exports.default=(o,s,i,c)=>{const l=!!a.getConfigValue("APP_CONFIG"),u=!!a.getConfigValue("APP_BUTTONS");l||console.warn("APP_CONFIG is not set. Voice AI will not be available."),u||console.warn("APP_BUTTONS is not set. Voice AI will not be available.");const d=a.getConfigValue("APP_CONFIG"),p=a.getConfigValue("APP_BUTTONS");a.getConfigValue("API_BASE_URL").replace(/\/$/,""),a.getConfigValue("USE_REALTIME_SESSION_ENDPOINT"),a.getConfigValue("REALTIME_SESSION_ENDPOINT");const f=c||a.getConfigValue("REALTIME_VOICE_MODEL")||"alloy",g=["alloy","shimmer","echo"],m=g.includes(f)?f:"alloy";f!==m&&console.warn(`[codicent-app-sdk] Voice "${f}" is not supported in the current SDK version. Supported voices: ${g.join(", ")}. Falling back to "${m}".`);const y=e.useRef(new r.WavRecorder({sampleRate:24e3})),_=e.useRef(new n.WavStreamPlayer({sampleRate:24e3})),S=e.useRef(null),w=e.useRef(null),v=e.useRef(null),h=e.useRef(null),O=e.useRef(!1),C=e.useRef(null),R=e.useRef(null),I=e.useRef(null),T=e.useRef((new Date).toISOString()),[N,E]=e.useState([]),[b,A]=e.useState([]),[P,k]=e.useState(!1),[V,D]=e.useState(!1),[M,x]=e.useState(!1),F=e.useRef(0),J=e.useRef(0),[$,L]=e.useState(""),[j,q]=e.useState("en-US"),U=e.useRef(new Map),[B,W]=e.useState((()=>l&&u&&p&&d&&d.apps&&d.apps[p]?d.apps[p].voiceInstructions||d.REALTIME_VOICE_INSTRUCTIONS||"":d&&d.REALTIME_VOICE_INSTRUCTIONS||"")),G=e.useCallback((e=>{const t=T.current,n=new Date(t).valueOf(),r=new Date(e).valueOf()-n,a=Math.floor(r/10)%100,o=Math.floor(r/1e3)%60,s=e=>{let t=e+"";for(;t.length<2;)t="0"+t;return t};return`${s(Math.floor(r/6e4)%60)}:${s(o)}.${s(a)}`}),[]),H=e.useCallback((async()=>{try{T.current=(new Date).toISOString(),k(!0),A([]),E([]),U.current.clear();const e=await o.getRealtimeSessionToken(m);if(!e)throw new Error("No ephemeral key returned from session endpoint");const t=new RTCPeerConnection;S.current=t,v.current||(v.current=new Audio,v.current.autoplay=!0),t.ontrack=e=>{v.current&&e.streams[0]&&(v.current.srcObject=e.streams[0])};const n=await navigator.mediaDevices.getUserMedia({audio:!0});h.current=n;const r=n.getTracks()[0];t.addTrack(r,n);const s=t.createDataChannel("oai-events");w.current=s,s.addEventListener("message",(e=>{try{const t=JSON.parse(e.data);if("session.created"===t.type);else if("conversation.item.created"===t.type)E((e=>[...e,t.item]));else if("conversation.item.input_audio_transcription.completed"===t.type)E((e=>{const n=[...e],r=n.findIndex((e=>e.id===t.item_id));return-1!==r&&n[r].formatted&&(n[r].formatted.transcript=t.transcript),n}));else if("response.audio_transcript.delta"===t.type);else if("response.audio_transcript.done"===t.type);else if("response.output_item.added"===t.type){const e=t.item;"function_call"===e?.type&&(console.log("[Voice AI] Function call initiated:",e.name),U.current.set(e.id,{name:e.name||"",arguments:"",call_id:e.call_id||e.id}))}else if("response.function_call_arguments.delta"===t.type){const e=t.item_id,n=t.delta,r=U.current.get(e);r&&n&&(r.arguments+=n,U.current.set(e,r))}else if("response.function_call_arguments.done"===t.type){const e=t.item_id,n=U.current.get(e);if(n){console.log(`[Voice AI] Executing tool: ${n.name}`);const t=i.find((e=>e.definition.name===n.name));if(t){let e={};try{e=n.arguments?JSON.parse(n.arguments):{}}catch(t){console.error("[Voice AI] Failed to parse tool arguments:",t),e={}}Promise.resolve(t.handler(e)).then((e=>{console.log(`[Voice AI] Tool ${n.name} completed:`,e),s&&"open"===s.readyState&&(s.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify(e)}})),s.send(JSON.stringify({type:"response.create"})))})).catch((e=>{console.error(`[Voice AI] Tool ${n.name} failed:`,e),s&&"open"===s.readyState&&(s.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify({error:e.message||"Tool execution failed"})}})),s.send(JSON.stringify({type:"response.create"})))}))}else console.error(`[Voice AI] Tool not found: ${n.name}`);U.current.delete(e)}}else"error"===t.type&&console.error("Server error:",t.error)}catch(t){console.warn("Invalid message:",e.data)}})),s.onopen=()=>{console.log("Data channel opened, configuring session"),s.send(JSON.stringify({type:"session.update",session:{instructions:B.replace("{{name}}",$).replace("{{language}}",j).replace("{{time}}",(new Date).toISOString()),modalities:["text","audio"],input_audio_transcription:{model:"whisper-1"},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:500},voice:m,temperature:.8,max_response_output_tokens:4096,input_audio_format:"pcm16",output_audio_format:"pcm16",tools:i.map((e=>({type:"function",...e.definition})))}}))},s.onclose=()=>{console.log("Data channel closed")};const c=await t.createOffer();await t.setLocalDescription(c);let l="gpt-4o-realtime-preview";try{if(a.getConfigValue("REALTIME_CONFIG_ENDPOINT")){const e=await o.getRealtimeConfig();e&&e.model&&(l=e.model)}}catch(e){console.warn("Failed to fetch realtime config, using default model:",e)}const u="https://api.openai.com/v1/realtime",d=await fetch(`${u}?model=${l}`,{method:"POST",body:c.sdp,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/sdp"}});if(!d.ok)throw new Error(`Failed to get SDP answer: ${d.statusText}`);const p={type:"answer",sdp:await d.text()};await t.setRemoteDescription(p),D(!1);const f=y.current,g=_.current;await f.begin(),await g.connect()}catch(e){throw console.error("[codicent-app-sdk] Failed to establish WebRTC connection:",e),k(!1),e}}),[o,m,B,$,j,i]),z=e.useCallback((async()=>{k(!1),A([]),E([]),U.current.clear(),w.current&&(w.current.close(),w.current=null),S.current&&(S.current.close(),S.current=null),h.current&&(h.current.getTracks().forEach((e=>e.stop())),h.current=null),v.current&&(v.current.pause(),v.current.srcObject=null);const e=y.current;await e.end();const t=_.current;await t.interrupt()}),[]),K=e.useCallback((async e=>{const t=w.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"conversation.item.delete",item_id:e}))}),[]),Q=e.useCallback((async()=>{x(!0);const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"input_audio_buffer.commit"}))}),[]),X=e.useCallback((async()=>{x(!1);const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"response.create"}))}),[]),Y=e.useCallback((async e=>{const t=w.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"session.update",session:{turn_detection:"none"===e?null:{type:"server_vad"}}})),D("none"===e)}),[]);e.useEffect((()=>{if($&&P){const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"session.update",session:{instructions:B.replace("{{name}}",$).replace("{{language}}",j).replace("{{time}}",(new Date).toISOString())}}))}}),[B,$,j,P]),e.useEffect((()=>{let e=!0;const n=y.current,r=C.current;let a=null;const o=_.current,s=R.current;let i=null;const c=()=>{if(e){if(r&&(r.width&&r.height||(r.width=r.offsetWidth,r.height=r.offsetHeight),a=a||r.getContext("2d"),a)){a.clearRect(0,0,r.width,r.height);const e=n.recording?n.getFrequencies("voice"):{values:new Float32Array([0])},o=1-Math.max(...e.values);F.current=o,t.WavRenderer.drawCircularBars(r,a,e.values,"#0099ff",20,0,8)}if(s&&(s.width&&s.height||(s.width=s.offsetWidth,s.height=s.offsetHeight),i=i||s.getContext("2d"),i)){i.clearRect(0,0,s.width,s.height);const e=o.analyser?o.getFrequencies("voice"):{values:new Float32Array([0])},n=1-Math.max(...e.values);J.current=n,t.WavRenderer.drawCircularBars(s,i,e.values,"#009900",20,0,8)}window.requestAnimationFrame(c)}};return c(),()=>{e=!1}}),[]),e.useEffect((()=>{!O.current&&i&&B&&(O.current=!0)}),[i,B]);const Z=e.useCallback((e=>{const t=e.replace("{{name}}",$).replace("{{language}}",j).replace("{{time}}",(new Date).toISOString()),n=w.current;n&&"open"===n.readyState&&n.send(JSON.stringify({type:"session.update",session:{instructions:t}})),W(e)}),[$,j]);return e.useMemo((()=>{if(l&&u)return{items:N,realtimeEvents:b,isConnected:P,canPushToTalk:V,isRecording:M,clientCanvasRef:C,serverCanvasRef:R,eventsScrollRef:I,formatTime:G,connectConversation:H,disconnectConversation:z,deleteConversationItem:K,startRecording:Q,stopRecording:X,changeTurnEndType:Y,getRecorderLevel:()=>F.current,getStreamLevel:()=>J.current,setUsername:L,updateInstructions:Z,setLanguage:q}}),[l,u,N,b,P,V,M,C,R,I])};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("../utils/wav_renderer.js");require("../lib/wavtools/lib/wav_packer.js"),require("../lib/wavtools/lib/analysis/audio_analysis.js");var n=require("../lib/wavtools/lib/wav_stream_player.js"),r=require("../lib/wavtools/lib/wav_recorder.js"),a=require("../config/index.js");exports.default=(s,o,i,c)=>{const l=!!a.getConfigValue("APP_CONFIG"),u=!!a.getConfigValue("APP_BUTTONS");l||console.warn("APP_CONFIG is not set. Voice AI will not be available."),u||console.warn("APP_BUTTONS is not set. Voice AI will not be available.");const d=a.getConfigValue("APP_CONFIG"),p=a.getConfigValue("APP_BUTTONS");a.getConfigValue("API_BASE_URL").replace(/\/$/,""),a.getConfigValue("USE_REALTIME_SESSION_ENDPOINT"),a.getConfigValue("REALTIME_SESSION_ENDPOINT");const f=c||a.getConfigValue("REALTIME_VOICE_MODEL")||"alloy",g=["alloy","shimmer","echo"],m=g.includes(f)?f:"alloy";f!==m&&console.warn(`[codicent-app-sdk] Voice "${f}" is not supported in the current SDK version. Supported voices: ${g.join(", ")}. Falling back to "${m}".`);const y=e.useRef(new r.WavRecorder({sampleRate:24e3})),_=e.useRef(new n.WavStreamPlayer({sampleRate:24e3})),S=e.useRef(null),w=e.useRef(null),v=e.useRef(null),h=e.useRef(null),O=e.useRef(!1),R=e.useRef(null),C=e.useRef(null),I=e.useRef(null),T=e.useRef((new Date).toISOString()),[N,E]=e.useState([]),[b,A]=e.useState([]),[P,k]=e.useState(!1),[V,D]=e.useState(!1),[M,x]=e.useState(!1),[F,J]=e.useState(!1),$=e.useRef(0),L=e.useRef(0),[j,q]=e.useState(""),[U,B]=e.useState("en-US"),W=e.useRef(new Map),G=e.useRef(null),[H,z]=e.useState((()=>l&&u&&p&&d&&d.apps&&d.apps[p]?d.apps[p].voiceInstructions||d.REALTIME_VOICE_INSTRUCTIONS||"":d&&d.REALTIME_VOICE_INSTRUCTIONS||"")),K=e.useCallback((e=>{const t=T.current,n=new Date(t).valueOf(),r=new Date(e).valueOf()-n,a=Math.floor(r/10)%100,s=Math.floor(r/1e3)%60,o=e=>{let t=e+"";for(;t.length<2;)t="0"+t;return t};return`${o(Math.floor(r/6e4)%60)}:${o(s)}.${o(a)}`}),[]),Q=e.useCallback((async()=>{try{T.current=(new Date).toISOString(),k(!0),A([]),E([]),W.current.clear();const e=await s.getRealtimeSessionToken(m);if(!e)throw new Error("No ephemeral key returned from session endpoint");const t=new RTCPeerConnection;S.current=t,v.current||(v.current=new Audio,v.current.autoplay=!0),t.ontrack=e=>{v.current&&e.streams[0]&&(v.current.srcObject=e.streams[0])};const n=await navigator.mediaDevices.getUserMedia({audio:!0});h.current=n;const r=n.getTracks()[0];t.addTrack(r,n);const o=t.createDataChannel("oai-events");w.current=o,o.addEventListener("message",(e=>{try{const t=JSON.parse(e.data);if("session.created"===t.type);else if("conversation.item.created"===t.type)E((e=>[...e,t.item]));else if("conversation.item.input_audio_transcription.completed"===t.type)E((e=>{const n=[...e],r=n.findIndex((e=>e.id===t.item_id));return-1!==r&&n[r].formatted&&(n[r].formatted.transcript=t.transcript),n}));else if("response.audio_transcript.delta"===t.type);else if("response.audio_transcript.done"===t.type);else if("response.output_item.added"===t.type){const e=t.item;"function_call"===e?.type&&(console.log("[Voice AI] Function call initiated:",e.name),W.current.set(e.id,{name:e.name||"",arguments:"",call_id:e.call_id||e.id}))}else if("response.function_call_arguments.delta"===t.type){const e=t.item_id,n=t.delta,r=W.current.get(e);r&&n&&(r.arguments+=n,W.current.set(e,r))}else if("response.function_call_arguments.done"===t.type){const e=t.item_id,n=W.current.get(e);if(n){console.log(`[Voice AI] Executing tool: ${n.name}`);const t=i.find((e=>e.definition.name===n.name));if(t){let e={};try{e=n.arguments?JSON.parse(n.arguments):{}}catch(t){console.error("[Voice AI] Failed to parse tool arguments:",t),e={}}Promise.resolve(t.handler(e)).then((e=>{console.log(`[Voice AI] Tool ${n.name} completed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify(e)}})),o.send(JSON.stringify({type:"response.create"})))})).catch((e=>{console.error(`[Voice AI] Tool ${n.name} failed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify({error:e.message||"Tool execution failed"})}})),o.send(JSON.stringify({type:"response.create"})))}))}else console.error(`[Voice AI] Tool not found: ${n.name}`);W.current.delete(e)}}else"error"===t.type&&console.error("Server error:",t.error)}catch(t){console.warn("Invalid message:",e.data)}})),o.onopen=()=>{console.log("Data channel opened, configuring session");const e=G.current||H;G.current=null,o.send(JSON.stringify({type:"session.update",session:{instructions:e.replace("{{name}}",j).replace("{{language}}",U).replace("{{time}}",(new Date).toISOString()),modalities:["text","audio"],input_audio_transcription:{model:"whisper-1"},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:500},voice:m,temperature:.8,max_response_output_tokens:4096,input_audio_format:"pcm16",output_audio_format:"pcm16",tools:i.map((e=>({type:"function",...e.definition})))}})),D(!0)},o.onclose=()=>{console.log("Data channel closed"),D(!1)};const c=await t.createOffer();await t.setLocalDescription(c);let l="gpt-4o-realtime-preview";try{if(a.getConfigValue("REALTIME_CONFIG_ENDPOINT")){const e=await s.getRealtimeConfig();e&&e.model&&(l=e.model)}}catch(e){console.warn("Failed to fetch realtime config, using default model:",e)}const u="https://api.openai.com/v1/realtime",d=await fetch(`${u}?model=${l}`,{method:"POST",body:c.sdp,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/sdp"}});if(!d.ok)throw new Error(`Failed to get SDP answer: ${d.statusText}`);const p={type:"answer",sdp:await d.text()};await t.setRemoteDescription(p),x(!1);const f=y.current,g=_.current;await f.begin(),await g.connect()}catch(e){throw console.error("[codicent-app-sdk] Failed to establish WebRTC connection:",e),k(!1),e}}),[s,m,H,j,U,i]),X=e.useCallback((async()=>{k(!1),D(!1),A([]),E([]),W.current.clear(),G.current=null,w.current&&(w.current.close(),w.current=null),S.current&&(S.current.close(),S.current=null),h.current&&(h.current.getTracks().forEach((e=>e.stop())),h.current=null),v.current&&(v.current.pause(),v.current.srcObject=null);const e=y.current;await e.end();const t=_.current;await t.interrupt()}),[]),Y=e.useCallback((async e=>{const t=w.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"conversation.item.delete",item_id:e}))}),[]),Z=e.useCallback((async()=>{J(!0);const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"input_audio_buffer.commit"}))}),[]),ee=e.useCallback((async()=>{J(!1);const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"response.create"}))}),[]),te=e.useCallback((async e=>{const t=w.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"session.update",session:{turn_detection:"none"===e?null:{type:"server_vad"}}})),x("none"===e)}),[]);e.useEffect((()=>{if(j&&V){const e=w.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"session.update",session:{instructions:H.replace("{{name}}",j).replace("{{language}}",U).replace("{{time}}",(new Date).toISOString())}}))}}),[H,j,U,V]),e.useEffect((()=>{let e=!0;const n=y.current,r=R.current;let a=null;const s=_.current,o=C.current;let i=null;const c=()=>{if(e){if(r&&(r.width&&r.height||(r.width=r.offsetWidth,r.height=r.offsetHeight),a=a||r.getContext("2d"),a)){a.clearRect(0,0,r.width,r.height);const e=n.recording?n.getFrequencies("voice"):{values:new Float32Array([0])},s=1-Math.max(...e.values);$.current=s,t.WavRenderer.drawCircularBars(r,a,e.values,"#0099ff",20,0,8)}if(o&&(o.width&&o.height||(o.width=o.offsetWidth,o.height=o.offsetHeight),i=i||o.getContext("2d"),i)){i.clearRect(0,0,o.width,o.height);const e=s.analyser?s.getFrequencies("voice"):{values:new Float32Array([0])},n=1-Math.max(...e.values);L.current=n,t.WavRenderer.drawCircularBars(o,i,e.values,"#009900",20,0,8)}window.requestAnimationFrame(c)}};return c(),()=>{e=!1}}),[]),e.useEffect((()=>{!O.current&&i&&H&&(O.current=!0)}),[i,H]);const ne=e.useCallback((e=>{z(e);const t=w.current;if(t&&"open"===t.readyState){const n=e.replace("{{name}}",j).replace("{{language}}",U).replace("{{time}}",(new Date).toISOString());t.send(JSON.stringify({type:"session.update",session:{instructions:n}}))}else G.current=e}),[j,U]);return e.useMemo((()=>{if(l&&u)return{items:N,realtimeEvents:b,isConnected:P,isSessionReady:V,canPushToTalk:M,isRecording:F,clientCanvasRef:R,serverCanvasRef:C,eventsScrollRef:I,formatTime:K,connectConversation:Q,disconnectConversation:X,deleteConversationItem:Y,startRecording:Z,stopRecording:ee,changeTurnEndType:te,getRecorderLevel:()=>$.current,getStreamLevel:()=>L.current,setUsername:q,updateInstructions:ne,setLanguage:B}}),[l,u,N,b,P,V,M,F,R,C,I])};
@@ -0,0 +1 @@
1
+ "use strict";var e=require("./event_handler.js"),t=require("./utils.js");class s extends e.RealtimeEventHandler{constructor({url:e,apiKey:t,dangerouslyAllowAPIKeyInBrowser:s,debug:r}={}){if(super(),this.defaultUrl="wss://api.openai.com/v1/realtime",this.url=e||this.defaultUrl,this.apiKey=t||null,this.debug=!!r,this.ws=null,globalThis.document&&this.apiKey&&!s)throw new Error('Can not provide API key in the browser without "dangerouslyAllowAPIKeyInBrowser" set to true')}isConnected(){return!!this.ws}log(...e){const t=[`[Websocket/${(new Date).toISOString()}]`].concat(e).map((e=>"object"==typeof e&&null!==e?JSON.stringify(e,null,2):e));return this.debug&&console.log(...t),!0}async connect({model:e}={model:"gpt-4o-realtime-preview-2024-10-01"}){if(this.apiKey||this.url!==this.defaultUrl||console.warn(`No apiKey provided for connection to "${this.url}"`),this.isConnected())throw new Error("Already connected");if(globalThis.WebSocket){globalThis.document&&this.apiKey&&console.warn("Warning: Connecting using API key in the browser, this is not recommended");const e=new(0,globalThis.WebSocket)(`${this.url}`,["realtime",`websocket.api_key.${this.apiKey}`]);return e.addEventListener("message",(e=>{const t=JSON.parse(e.data);this.receive(t.type,t)})),new Promise(((t,s)=>{const r=()=>{this.disconnect(e),s(new Error(`Could not connect to "${this.url}"`))};e.addEventListener("error",r),e.addEventListener("open",(()=>{this.log(`Connected to "${this.url}"`),e.removeEventListener("error",r),e.addEventListener("error",(()=>{this.disconnect(e),this.log(`Error, disconnected from "${this.url}"`),this.dispatch("close",{error:!0})})),e.addEventListener("close",(()=>{this.disconnect(e),this.log(`Disconnected from "${this.url}"`),this.dispatch("close",{error:!1})})),this.ws=e,t(!0)}))}))}{const t="ws",s=new(0,(await import(t)).default)(`${this.url||"wss://api.openai.com/v1/realtime"}${e?`?model=${e}`:""}`,[],{finishRequest:e=>{e.setHeader("Authorization",`Bearer ${this.apiKey}`),e.setHeader("OpenAI-Beta","realtime=v1"),e.setHeader("api-key",this.apiKey),e.end()}});return s.on("message",(e=>{const t=JSON.parse(e.toString());this.receive(t.type,t)})),new Promise(((e,t)=>{const r=()=>{this.disconnect(s),t(new Error(`Could not connect to "${this.url}"`))};s.on("error",r),s.on("open",(()=>{this.log(`Connected to "${this.url}"`),s.removeListener("error",r),s.on("error",(()=>{this.disconnect(s),this.log(`Error, disconnected from "${this.url}"`),this.dispatch("close",{error:!0})})),s.on("close",(()=>{this.disconnect(s),this.log(`Disconnected from "${this.url}"`),this.dispatch("close",{error:!1})})),this.ws=s,e(!0)}))}))}}disconnect(e){if(!e||this.ws===e)return this.ws&&this.ws.close(),this.ws=null,!0}receive(e,t){return this.log("received:",e,t),this.dispatch(`server.${e}`,t),this.dispatch("server.*",t),!0}send(e,s){if(!this.isConnected())throw new Error("RealtimeAPI is not connected");if("object"!=typeof(s=s||{}))throw new Error("data must be an object");const r={event_id:t.RealtimeUtils.generateId("evt_"),type:e,...s};return this.dispatch(`client.${e}`,r),this.dispatch("client.*",r),this.log("sent:",e,r),this.ws.send(JSON.stringify(r)),!0}}exports.RealtimeAPI=s;
@@ -0,0 +1 @@
1
+ "use strict";var e=require("./event_handler.js"),t=require("./api.js"),i=require("./conversation.js"),s=require("./utils.js");class n extends e.RealtimeEventHandler{constructor({url:e,apiKey:s,dangerouslyAllowAPIKeyInBrowser:n,debug:o}={}){super(),this.defaultSessionConfig={modalities:["text","audio"],instructions:"",voice:"alloy",input_audio_format:"pcm16",output_audio_format:"pcm16",input_audio_transcription:null,turn_detection:null,tools:[],tool_choice:"auto",temperature:.8,max_response_output_tokens:4096},this.sessionConfig={},this.transcriptionModels=[{model:"whisper-1"}],this.defaultServerVadConfig={type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:200},this.realtime=new t.RealtimeAPI({url:e,apiKey:s,dangerouslyAllowAPIKeyInBrowser:n,debug:o}),this.conversation=new i.RealtimeConversation,this._resetConfig(),this._addAPIEventHandlers()}_resetConfig(){return this.sessionCreated=!1,this.tools={},this.sessionConfig=JSON.parse(JSON.stringify(this.defaultSessionConfig)),this.inputAudioBuffer=new Int16Array(0),!0}_addAPIEventHandlers(){this.realtime.on("client.*",(e=>{const t={time:(new Date).toISOString(),source:"client",event:e};this.dispatch("realtime.event",t)})),this.realtime.on("server.*",(e=>{const t={time:(new Date).toISOString(),source:"server",event:e};this.dispatch("realtime.event",t)})),this.realtime.on("server.session.created",(()=>this.sessionCreated=!0));const e=(e,...t)=>{const{item:i,delta:s}=this.conversation.processEvent(e,...t);return{item:i,delta:s}},t=(t,...i)=>{const{item:s,delta:n}=e(t,...i);return s&&this.dispatch("conversation.updated",{item:s,delta:n}),{item:s,delta:n}},i=async e=>{try{const t=JSON.parse(e.arguments),i=this.tools[e.name];if(!i)throw new Error(`Tool "${e.name}" has not been added`);const s=await i.handler(t);this.realtime.send("conversation.item.create",{item:{type:"function_call_output",call_id:e.call_id,output:JSON.stringify(s)}})}catch(t){this.realtime.send("conversation.item.create",{item:{type:"function_call_output",call_id:e.call_id,output:JSON.stringify({error:t.message})}})}this.createResponse()};return this.realtime.on("server.response.created",e),this.realtime.on("server.response.output_item.added",e),this.realtime.on("server.response.content_part.added",e),this.realtime.on("server.input_audio_buffer.speech_started",(t=>{e(t),this.dispatch("conversation.interrupted")})),this.realtime.on("server.input_audio_buffer.speech_stopped",(t=>e(t,this.inputAudioBuffer))),this.realtime.on("server.conversation.item.created",(e=>{const{item:i}=t(e);this.dispatch("conversation.item.appended",{item:i}),"completed"===i.status&&this.dispatch("conversation.item.completed",{item:i})})),this.realtime.on("server.conversation.item.truncated",t),this.realtime.on("server.conversation.item.deleted",t),this.realtime.on("server.conversation.item.input_audio_transcription.completed",t),this.realtime.on("server.response.audio_transcript.delta",t),this.realtime.on("server.response.audio.delta",t),this.realtime.on("server.response.text.delta",t),this.realtime.on("server.response.function_call_arguments.delta",t),this.realtime.on("server.response.output_item.done",(async e=>{const{item:s}=t(e);"completed"===s.status&&this.dispatch("conversation.item.completed",{item:s}),s.formatted.tool&&i(s.formatted.tool)})),!0}isConnected(){return this.realtime.isConnected()}reset(){return this.disconnect(),this.clearEventHandlers(),this.realtime.clearEventHandlers(),this._resetConfig(),this._addAPIEventHandlers(),!0}async connect(){if(this.isConnected())throw new Error("Already connected, use .disconnect() first");return await this.realtime.connect(),this.updateSession(),!0}async waitForSessionCreated(){if(!this.isConnected())throw new Error("Not connected, use .connect() first");for(;!this.sessionCreated;)await new Promise((e=>setTimeout((()=>e()),1)));return!0}disconnect(){this.sessionCreated=!1,this.realtime.isConnected()&&this.realtime.disconnect(),this.conversation.clear()}getTurnDetectionType(){return this.sessionConfig.turn_detection?.type||null}addTool(e,t){if(!e?.name)throw new Error("Missing tool name in definition");const i=e?.name;if(this.tools[i])throw new Error(`Tool "${i}" already added. Please use .removeTool("${i}") before trying to add again.`);if("function"!=typeof t)throw new Error(`Tool "${i}" handler must be a function`);return this.tools[i]={definition:e,handler:t},this.updateSession(),this.tools[i]}removeTool(e){if(!this.tools[e])throw new Error(`Tool "${e}" does not exist, can not be removed.`);return delete this.tools[e],!0}deleteItem(e){return this.realtime.send("conversation.item.delete",{item_id:e}),!0}updateSession({modalities:e,instructions:t,voice:i,input_audio_format:s,output_audio_format:n,input_audio_transcription:o,turn_detection:r,tools:a,tool_choice:d,temperature:u,max_response_output_tokens:l}={}){void 0!==e&&(this.sessionConfig.modalities=e),void 0!==t&&(this.sessionConfig.instructions=t),void 0!==i&&(this.sessionConfig.voice=i),void 0!==s&&(this.sessionConfig.input_audio_format=s),void 0!==n&&(this.sessionConfig.output_audio_format=n),void 0!==o&&(this.sessionConfig.input_audio_transcription=o),void 0!==r&&(this.sessionConfig.turn_detection=r),void 0!==a&&(this.sessionConfig.tools=a),void 0!==d&&(this.sessionConfig.tool_choice=d),void 0!==u&&(this.sessionConfig.temperature=u),void 0!==l&&(this.sessionConfig.max_response_output_tokens=l);const c=[].concat((a||[]).map((e=>{const t={type:"function",...e};if(this.tools[t?.name])throw new Error(`Tool "${t?.name}" has already been defined`);return t})),Object.keys(this.tools).map((e=>({type:"function",...this.tools[e].definition})))),m={...this.sessionConfig};return m.tools=c,this.realtime.isConnected()&&this.realtime.send("session.update",{session:m}),!0}sendUserMessageContent(e=[]){if(e.length){for(const t of e)"input_audio"===t.type&&(t.audio instanceof ArrayBuffer||t.audio instanceof Int16Array)&&(t.audio=s.RealtimeUtils.arrayBufferToBase64(t.audio));this.realtime.send("conversation.item.create",{item:{type:"message",role:"user",content:e}})}return this.createResponse(),!0}appendInputAudio(e){return e.byteLength>0&&(this.realtime.send("input_audio_buffer.append",{audio:s.RealtimeUtils.arrayBufferToBase64(e)}),this.inputAudioBuffer=s.RealtimeUtils.mergeInt16Arrays(this.inputAudioBuffer,e)),!0}createResponse(){return null===this.getTurnDetectionType()&&this.inputAudioBuffer.byteLength>0&&(this.realtime.send("input_audio_buffer.commit"),this.conversation.queueInputAudio(this.inputAudioBuffer),this.inputAudioBuffer=new Int16Array(0)),this.realtime.send("response.create"),!0}cancelResponse(e,t=0){if(!e)return this.realtime.send("response.cancel"),{item:null};if(e){const i=this.conversation.getItem(e);if(!i)throw new Error(`Could not find item "${e}"`);if("message"!==i.type)throw new Error('Can only cancelResponse messages with type "message"');if("assistant"!==i.role)throw new Error('Can only cancelResponse messages with role "assistant"');this.realtime.send("response.cancel");const s=i.content.findIndex((e=>"audio"===e.type));if(-1===s)throw new Error("Could not find audio on item to cancel");return this.realtime.send("conversation.item.truncate",{item_id:e,content_index:s,audio_end_ms:Math.floor(t/this.conversation.defaultFrequency*1e3)}),{item:i}}}async waitForNextItem(){const e=await this.waitForNext("conversation.item.appended"),{item:t}=e;return{item:t}}async waitForNextCompletedItem(){const e=await this.waitForNext("conversation.item.completed"),{item:t}=e;return{item:t}}}exports.RealtimeClient=n;
@@ -0,0 +1 @@
1
+ "use strict";var t=require("./utils.js");exports.RealtimeConversation=class{defaultFrequency=24e3;EventProcessors={"conversation.item.created":t=>{const{item:e}=t,o=JSON.parse(JSON.stringify(e));if(this.itemLookup[o.id]||(this.itemLookup[o.id]=o,this.items.push(o)),o.formatted={},o.formatted.audio=new Int16Array(0),o.formatted.text="",o.formatted.transcript="",this.queuedSpeechItems[o.id]&&(o.formatted.audio=this.queuedSpeechItems[o.id].audio,delete this.queuedSpeechItems[o.id]),o.content){const t=o.content.filter((t=>["text","input_text"].includes(t.type)));for(const e of t)o.formatted.text+=e.text}return this.queuedTranscriptItems[o.id]&&(o.formatted.transcript=this.queuedTranscriptItems.transcript,delete this.queuedTranscriptItems[o.id]),"message"===o.type?"user"===o.role?(o.status="completed",this.queuedInputAudio&&(o.formatted.audio=this.queuedInputAudio,this.queuedInputAudio=null)):o.status="in_progress":"function_call"===o.type?(o.formatted.tool={type:"function",name:o.name,call_id:o.call_id,arguments:""},o.status="in_progress"):"function_call_output"===o.type&&(o.status="completed",o.formatted.output=o.output),{item:o,delta:null}},"conversation.item.truncated":t=>{const{item_id:e,audio_end_ms:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`item.truncated: Item "${e}" not found`);const i=Math.floor(o*this.defaultFrequency/1e3);return s.formatted.transcript="",s.formatted.audio=s.formatted.audio.slice(0,i),{item:s,delta:null}},"conversation.item.deleted":t=>{const{item_id:e}=t,o=this.itemLookup[e];if(!o)throw new Error(`item.deleted: Item "${e}" not found`);delete this.itemLookup[o.id];const s=this.items.indexOf(o);return s>-1&&this.items.splice(s,1),{item:o,delta:null}},"conversation.item.input_audio_transcription.completed":t=>{const{item_id:e,content_index:o,transcript:s}=t,i=this.itemLookup[e],r=s||" ";return i?(i.content[o].transcript=s,i.formatted.transcript=r,{item:i,delta:{transcript:s}}):(this.queuedTranscriptItems[e]={transcript:r},{item:null,delta:null})},"input_audio_buffer.speech_started":t=>{const{item_id:e,audio_start_ms:o}=t;return this.queuedSpeechItems[e]={audio_start_ms:o},{item:null,delta:null}},"input_audio_buffer.speech_stopped":(t,e)=>{const{item_id:o,audio_end_ms:s}=t;this.queuedSpeechItems[o]||(this.queuedSpeechItems[o]={audio_start_ms:s});const i=this.queuedSpeechItems[o];if(i.audio_end_ms=s,e){const t=Math.floor(i.audio_start_ms*this.defaultFrequency/1e3),o=Math.floor(i.audio_end_ms*this.defaultFrequency/1e3);i.audio=e.slice(t,o)}return{item:null,delta:null}},"response.created":t=>{const{response:e}=t;return this.responseLookup[e.id]||(this.responseLookup[e.id]=e,this.responses.push(e)),{item:null,delta:null}},"response.output_item.added":t=>{const{response_id:e,item:o}=t,s=this.responseLookup[e];if(!s)throw new Error(`response.output_item.added: Response "${e}" not found`);return s.output.push(o.id),{item:null,delta:null}},"response.output_item.done":t=>{const{item:e}=t;if(!e)throw new Error('response.output_item.done: Missing "item"');const o=this.itemLookup[e.id];if(!o)throw new Error(`response.output_item.done: Item "${e.id}" not found`);return o.status=e.status,{item:o,delta:null}},"response.content_part.added":t=>{const{item_id:e,part:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`response.content_part.added: Item "${e}" not found`);return s.content.push(o),{item:s,delta:null}},"response.audio_transcript.delta":t=>{const{item_id:e,content_index:o,delta:s}=t,i=this.itemLookup[e];if(!i)throw new Error(`response.audio_transcript.delta: Item "${e}" not found`);return i.content[o].transcript+=s,i.formatted.transcript+=s,{item:i,delta:{transcript:s}}},"response.audio.delta":e=>{const{item_id:o,content_index:s,delta:i}=e,r=this.itemLookup[o];if(!r)throw new Error(`response.audio.delta: Item "${o}" not found`);const n=t.RealtimeUtils.base64ToArrayBuffer(i),u=new Int16Array(n);return r.formatted.audio=t.RealtimeUtils.mergeInt16Arrays(r.formatted.audio,u),{item:r,delta:{audio:u}}},"response.text.delta":t=>{const{item_id:e,content_index:o,delta:s}=t,i=this.itemLookup[e];if(!i)throw new Error(`response.text.delta: Item "${e}" not found`);return i.content[o].text+=s,i.formatted.text+=s,{item:i,delta:{text:s}}},"response.function_call_arguments.delta":t=>{const{item_id:e,delta:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`response.function_call_arguments.delta: Item "${e}" not found`);return s.arguments+=o,s.formatted.tool.arguments+=o,{item:s,delta:{arguments:o}}}};constructor(){this.clear()}clear(){return this.itemLookup={},this.items=[],this.responseLookup={},this.responses=[],this.queuedSpeechItems={},this.queuedTranscriptItems={},this.queuedInputAudio=null,!0}queueInputAudio(t){return this.queuedInputAudio=t,t}processEvent(t,...e){if(!t.event_id)throw console.error(t),new Error('Missing "event_id" on event');if(!t.type)throw console.error(t),new Error('Missing "type" on event');const o=this.EventProcessors[t.type];if(!o)throw new Error(`Missing conversation event processor for "${t.type}"`);return o.call(this,t,...e)}getItem(t){return this.itemLookup[t]||null}getItems(){return this.items.slice()}};
@@ -0,0 +1 @@
1
+ "use strict";const e=e=>new Promise((t=>setTimeout((()=>t()),e)));exports.RealtimeEventHandler=class{constructor(){this.eventHandlers={},this.nextEventHandlers={}}clearEventHandlers(){return this.eventHandlers={},this.nextEventHandlers={},!0}on(e,t){return this.eventHandlers[e]=this.eventHandlers[e]||[],this.eventHandlers[e].push(t),t}onNext(e,t){return this.nextEventHandlers[e]=this.nextEventHandlers[e]||[],this.nextEventHandlers[e].push(t),t}off(e,t){const n=this.eventHandlers[e]||[];if(t){const s=n.indexOf(t);if(-1===s)throw new Error(`Could not turn off specified event listener for "${e}": not found as a listener`);n.splice(s,1)}else delete this.eventHandlers[e];return!0}offNext(e,t){const n=this.nextEventHandlers[e]||[];if(t){const s=n.indexOf(t);if(-1===s)throw new Error(`Could not turn off specified next event listener for "${e}": not found as a listener`);n.splice(s,1)}else delete this.nextEventHandlers[e];return!0}async waitForNext(t,n=null){const s=Date.now();let r;for(this.onNext(t,(e=>r=e));!r;){if(n){if(Date.now()-s>n)return null}await e(1)}return r}dispatch(e,t){const n=[].concat(this.eventHandlers[e]||[]);for(const e of n)e(t);const s=[].concat(this.nextEventHandlers[e]||[]);for(const e of s)e(t);return delete this.nextEventHandlers[e],!0}};
@@ -0,0 +1 @@
1
+ "use strict";const t=globalThis.atob,r=globalThis.btoa;exports.RealtimeUtils=class{static floatTo16BitPCM(t){const r=new ArrayBuffer(2*t.length),e=new DataView(r);let n=0;for(let r=0;r<t.length;r++,n+=2){let a=Math.max(-1,Math.min(1,t[r]));e.setInt16(n,a<0?32768*a:32767*a,!0)}return r}static base64ToArrayBuffer(r){const e=t(r),n=e.length,a=new Uint8Array(n);for(let t=0;t<n;t++)a[t]=e.charCodeAt(t);return a.buffer}static arrayBufferToBase64(t){t instanceof Float32Array?t=this.floatTo16BitPCM(t):t instanceof Int16Array&&(t=t.buffer);let e="",n=new Uint8Array(t);for(let t=0;t<n.length;t+=32768){let r=n.subarray(t,t+32768);e+=String.fromCharCode.apply(null,r)}return r(e)}static mergeInt16Arrays(t,r){if(t instanceof ArrayBuffer&&(t=new Int16Array(t)),r instanceof ArrayBuffer&&(r=new Int16Array(r)),!(t instanceof Int16Array&&r instanceof Int16Array))throw new Error("Both items must be Int16Array");const e=new Int16Array(t.length+r.length);for(let r=0;r<t.length;r++)e[r]=t[r];for(let n=0;n<r.length;n++)e[t.length+n]=r[n];return e}static generateId(t,r=21){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";return`${t}${Array(r-t.length).fill(0).map((t=>e[Math.floor(58*Math.random())])).join("")}`}};
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { CodicentAppState } from "../hooks";
3
3
  declare const Chat: React.FC<{
4
4
  state: CodicentAppState;
5
+ hideFooter?: boolean;
5
6
  }>;
6
7
  export default Chat;
7
8
  //# sourceMappingURL=Chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/pages/Chat.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,OAAO,EACL,gBAAgB,EAOjB,MAAM,UAAU,CAAC;AAsClB,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAA;CAAE,CAmR/C,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/pages/Chat.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,OAAO,EACL,gBAAgB,EAOjB,MAAM,UAAU,CAAC;AAsClB,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAmRrE,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("react"),r=require("react-router-dom");require("../components/Markdown.js"),require("../components/Textarea.js"),require("../components/Button.js"),require("../components/CompoundButton.js"),require("../components/Spinner.js");var s=require("../components/TextHeader.js"),n=require("../components/TypingIndicator.js"),o=require("../components/Prompt.js"),a=require("../components/ChatInput.js"),i=require("../components/CombinedPlaceholderDialog.js"),l=require("../components/ChatMessage.js");require("../components/Header.js");var u=require("../hooks/useStateWithLocalStorage.js"),c=require("../services/codicent.js");require("../utils/MessageContent.js"),require("../node_modules/tinycolor2/esm/tinycolor.js"),require("../_virtual/index.js");var d=require("../config/index.js");require("../utils/cacheManager.js"),require("../lib/wavtools/lib/wav_packer.js"),require("../lib/wavtools/lib/analysis/audio_analysis.js"),require("../lib/wavtools/lib/wav_stream_player.js"),require("../lib/wavtools/lib/wav_recorder.js"),require("./AppFrame.js"),require("./Compose.js"),require("./Snap.js"),require("./Search.js"),require("./Login.js"),require("./CrmPage.js"),require("./CrmPagePersistent.js"),require("./ImageView.js"),require("./FormInvite.js"),require("./FormAccept.js"),require("./Sales.js"),require("./Purchase.js");var p=require("../hooks/useChat.js"),m=require("../hooks/useLocalization.js"),g=require("../hooks/useTemplateVariables.js");require("../hooks/useAppStyles.js"),require("../components/FileThumbnail.js"),require("../components/MessageInput.js"),require("../components/UploadFile.js"),require("../components/SnapFooter.js"),require("../components/Profile.js"),require("../components/MessageItem.js"),require("../components/Content.js"),require("../components/AiInput.js"),require("../components/SearchBox.js"),require("../components/HtmlView.js"),require("../components/Footer.js");var h=require("../components/Page.js"),j=require("../node_modules/@griffel/react/makeStyles.esm.js"),f=require("../node_modules/@fluentui/react-icons/lib/sizedIcons/chunk-3.js");const q=j.makeStyles({chatContainer:{width:"100%",flex:1,maxWidth:"800px",height:"100%",display:"flex",flexDirection:"column",margin:"0 auto",overflow:"hidden","@media (max-width: 768px)":{maxWidth:"100%",borderRadius:"0"},"@media (max-width: 480px)":{},backgroundPosition:"calc(50%) center",backgroundRepeat:"no-repeat",backgroundSize:"contain",touchAction:"pan-y"},chatMessages:{display:"flex",flexDirection:"column",flex:1,padding:"16px",overflowY:"auto",touchAction:"pan-y"}});exports.default=({state:j})=>{const x=q(),{t:C,tAsync:b,getLanguageInfo:S}=m.default(),v=r.useNavigate(),{messages:y,isBotTyping:w,handleSend:P,newChat:k,openChat:_}=p.useChat(j.stateMachine),T=t.useRef(null),[F,I]=r.useSearchParams(),[A,V]=t.useState(null),[M,E]=t.useState(!1),[H,R]=t.useState(!1),{service:N,currentStateName:B}=j,[L,O]=t.useState([]),[W,D]=t.useState([]),[U,z]=t.useState(!1),[G,Y]=t.useState(),[$,K]=u.default(j.context.selectedApp+"_prompts",[]),[J,Q]=t.useState(!1),{extractTemplateVariables:X,replaceTemplateVariables:Z,extractFilePlaceholders:ee,replaceFilePlaceholders:te,handleSelectFiles:re}=g.useTemplateVariables(N.uploadFile),se=t.useRef(!1),ne=t.useRef(null);t.useEffect((()=>{const e=window.history.length>1,t=Boolean(document.referrer&&document.referrer!==window.location.href);R(e||t)}),[]);t.useEffect((()=>{d.getConfigValue("SHOW_CHAT_PROMPTS")&&0===y.length&&j.service.getAppPrompts().then(K).catch((e=>{console.error("Failed to get prompts",e)}))}),[j.service,K,y.length]),t.useEffect((()=>{setTimeout((()=>{T.current?.scrollIntoView({behavior:"smooth"})}),10)}),[y]),t.useEffect((()=>{if(se.current)return;se.current=!0;const e=F.get("text"),t=F.get("id"),r=F.get("fid");if(e){(async()=>{const t=ee(e),r=X(e);k(),t.length>0||r.length>0?(D(t),O(r),z(!0),V(C(e))):V(C(e))})()}else t?_(t):r&&Y(r)}),[]),t.useEffect((()=>{G&&"anonymous"===B&&ne.current!==G&&(ne.current=G,N.getFormById(G).then((async e=>{k();const t=S().language,r=e.prompt.replace("{{fid}}",G).replace("{{fname}}",e.name).replace("{{language}}",t);Q(!0);const s=await b(r);Q(!1),V(s),R(!1)})))}),[G,k,N,V,B,b,S,Q]),t.useEffect((()=>{if(A&&!U){let e=A;W.length>0&&(e=te(e,W)),L.length>0&&(e=Z(e,L)),P(e),I({},{replace:!0}),V(null),O([]),D([])}}),[A,U,P,I,te,Z,W,L]);const oe=e=>{const t=F.get("append");t&&(e+="\n---\n"+t),P(e)};return e.jsxs(h.Page,{hideHeader:!0,hideFooter:!!G,children:[e.jsxs("div",{id:"chat-container",className:x.chatContainer,style:{backgroundImage:`url(${d.getConfigValue("CHAT_BACKGROUND_IMAGE_URL")})`},children:[H&&e.jsx("button",{onClick:()=>{H&&v(-1)},"aria-label":C("Tillbaka"),style:{position:"absolute",top:12,left:12,zIndex:100,background:"var(--colorNeutralBackground1)",border:"none",cursor:"pointer",padding:8,borderRadius:8,boxShadow:"0 2px 8px rgba(0,0,0,0.08)",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsx(f.CalendarWeekStart24Regular,{style:{transform:"rotate(180deg)"}})}),e.jsx("div",{style:{marginLeft:H?40:0},children:e.jsx(s.default,{title:C(d.getConfigValue("APP_CHAT_TITLE")||"Chatt"),onNewChat:G?void 0:()=>E(!0)})}),e.jsxs("div",{className:x.chatMessages,children:[d.getConfigValue("SHOW_CHAT_PROMPTS")&&$.length>0&&0===y.length&&e.jsx(l.default,{sender:"bot",content:C(d.getConfigValue("CHAT_WELCOME")||"Välkommen!"),children:$.map(((t,r)=>e.jsxs("div",{children:["> ",e.jsx("a",{href:"./#/chat",onClick:()=>{oe(t.title+"\n---\n"+t.prompt)},children:C(t.title)}),e.jsx("br",{})]},`prompt-${r}`)))}),y.map(((t,r)=>e.jsxs("div",{children:[(w&&r===y.length-1||!w&&r===y.length-2)&&e.jsx("div",{ref:T}),e.jsx(l.default,{sender:t.sender,content:t.content,isNew:t.isNew,showSuggestions:r===y.length-1,onSuggestionClicked:P},t.id)]},t.id))),J&&e.jsx(l.default,{sender:"user",content:C("Översätter...")}),w&&e.jsx(n.default,{})]}),e.jsx(a.default,{codicent:j.context.selectedApp,disableSend:w,onSend:oe,onSelectFiles:re,getImageUrl:c.CodicentService.getImageUrl,getFileInfo:e=>N.getFileInfo(e)})]}),e.jsx(o.default,{open:M,title:C("Vill du rensa chatten?"),content:C("En ny startas, men chatten kommer först sparas."),onYes:()=>{E(!1),k()},onNo:()=>E(!1)}),e.jsx(i.CombinedPlaceholderDialog,{open:U,templateVariables:L,filePlaceholders:W,onTemplateVariablesChange:O,onFilePlaceholdersChange:D,onConfirm:()=>z(!1),onCancel:()=>{z(!1),V(null),O([]),D([])},uploadFile:N.uploadFile})]})};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("react"),r=require("react-router-dom");require("../components/Markdown.js"),require("../components/Textarea.js"),require("../components/Button.js"),require("../components/CompoundButton.js"),require("../components/Spinner.js");var s=require("../components/TextHeader.js"),n=require("../components/TypingIndicator.js"),o=require("../components/Prompt.js"),a=require("../components/ChatInput.js"),i=require("../components/CombinedPlaceholderDialog.js"),l=require("../components/ChatMessage.js");require("../components/Header.js");var u=require("../hooks/useStateWithLocalStorage.js"),c=require("../services/codicent.js");require("../utils/MessageContent.js"),require("../node_modules/tinycolor2/esm/tinycolor.js"),require("../_virtual/index.js");var d=require("../config/index.js");require("../utils/cacheManager.js"),require("../lib/wavtools/lib/wav_packer.js"),require("../lib/wavtools/lib/analysis/audio_analysis.js"),require("../lib/wavtools/lib/wav_stream_player.js"),require("../lib/wavtools/lib/wav_recorder.js"),require("./AppFrame.js"),require("./Compose.js"),require("./Snap.js"),require("./Search.js"),require("./Login.js"),require("./CrmPage.js"),require("./CrmPagePersistent.js"),require("./ImageView.js"),require("./FormInvite.js"),require("./FormAccept.js"),require("./Sales.js"),require("./Purchase.js");var p=require("../hooks/useChat.js"),m=require("../hooks/useLocalization.js"),g=require("../hooks/useTemplateVariables.js");require("../hooks/useAppStyles.js"),require("../components/FileThumbnail.js"),require("../components/MessageInput.js"),require("../components/UploadFile.js"),require("../components/SnapFooter.js"),require("../components/Profile.js"),require("../components/MessageItem.js"),require("../components/Content.js"),require("../components/AiInput.js"),require("../components/SearchBox.js"),require("../components/HtmlView.js"),require("../components/Footer.js");var h=require("../components/Page.js"),j=require("../node_modules/@griffel/react/makeStyles.esm.js"),f=require("../node_modules/@fluentui/react-icons/lib/sizedIcons/chunk-3.js");const q=j.makeStyles({chatContainer:{width:"100%",flex:1,maxWidth:"800px",height:"100%",display:"flex",flexDirection:"column",margin:"0 auto",overflow:"hidden","@media (max-width: 768px)":{maxWidth:"100%",borderRadius:"0"},"@media (max-width: 480px)":{},backgroundPosition:"calc(50%) center",backgroundRepeat:"no-repeat",backgroundSize:"contain",touchAction:"pan-y"},chatMessages:{display:"flex",flexDirection:"column",flex:1,padding:"16px",overflowY:"auto",touchAction:"pan-y"}});exports.default=({state:j,hideFooter:x})=>{const C=q(),{t:b,tAsync:S,getLanguageInfo:v}=m.default(),y=r.useNavigate(),{messages:w,isBotTyping:P,handleSend:k,newChat:_,openChat:F}=p.useChat(j.stateMachine),T=t.useRef(null),[I,A]=r.useSearchParams(),[V,M]=t.useState(null),[E,H]=t.useState(!1),[R,N]=t.useState(!1),{service:B,currentStateName:L}=j,[O,W]=t.useState([]),[D,U]=t.useState([]),[z,G]=t.useState(!1),[Y,$]=t.useState(),[K,J]=u.default(j.context.selectedApp+"_prompts",[]),[Q,X]=t.useState(!1),{extractTemplateVariables:Z,replaceTemplateVariables:ee,extractFilePlaceholders:te,replaceFilePlaceholders:re,handleSelectFiles:se}=g.useTemplateVariables(B.uploadFile),ne=t.useRef(!1),oe=t.useRef(null);t.useEffect((()=>{const e=window.history.length>1,t=Boolean(document.referrer&&document.referrer!==window.location.href);N(e||t)}),[]);t.useEffect((()=>{d.getConfigValue("SHOW_CHAT_PROMPTS")&&0===w.length&&j.service.getAppPrompts().then(J).catch((e=>{console.error("Failed to get prompts",e)}))}),[j.service,J,w.length]),t.useEffect((()=>{setTimeout((()=>{T.current?.scrollIntoView({behavior:"smooth"})}),10)}),[w]),t.useEffect((()=>{if(ne.current)return;ne.current=!0;const e=I.get("text"),t=I.get("id"),r=I.get("fid");if(e){(async()=>{const t=te(e),r=Z(e);_(),t.length>0||r.length>0?(U(t),W(r),G(!0),M(b(e))):M(b(e))})()}else t?F(t):r&&$(r)}),[]),t.useEffect((()=>{Y&&"anonymous"===L&&oe.current!==Y&&(oe.current=Y,B.getFormById(Y).then((async e=>{_();const t=v().language,r=e.prompt.replace("{{fid}}",Y).replace("{{fname}}",e.name).replace("{{language}}",t);X(!0);const s=await S(r);X(!1),M(s),N(!1)})))}),[Y,_,B,M,L,S,v,X]),t.useEffect((()=>{if(V&&!z){let e=V;D.length>0&&(e=re(e,D)),O.length>0&&(e=ee(e,O)),k(e),A({},{replace:!0}),M(null),W([]),U([])}}),[V,z,k,A,re,ee,D,O]);const ae=e=>{const t=I.get("append");t&&(e+="\n---\n"+t),k(e)};return e.jsxs(h.Page,{hideHeader:!0,hideFooter:!!Y||x,children:[e.jsxs("div",{id:"chat-container",className:C.chatContainer,style:{backgroundImage:`url(${d.getConfigValue("CHAT_BACKGROUND_IMAGE_URL")})`},children:[R&&e.jsx("button",{onClick:()=>{R&&y(-1)},"aria-label":b("Tillbaka"),style:{position:"absolute",top:12,left:12,zIndex:100,background:"var(--colorNeutralBackground1)",border:"none",cursor:"pointer",padding:8,borderRadius:8,boxShadow:"0 2px 8px rgba(0,0,0,0.08)",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsx(f.CalendarWeekStart24Regular,{style:{transform:"rotate(180deg)"}})}),e.jsx("div",{style:{marginLeft:R?40:0},children:e.jsx(s.default,{title:b(d.getConfigValue("APP_CHAT_TITLE")||"Chatt"),onNewChat:Y?void 0:()=>H(!0)})}),e.jsxs("div",{className:C.chatMessages,children:[d.getConfigValue("SHOW_CHAT_PROMPTS")&&K.length>0&&0===w.length&&e.jsx(l.default,{sender:"bot",content:b(d.getConfigValue("CHAT_WELCOME")||"Välkommen!"),children:K.map(((t,r)=>e.jsxs("div",{children:["> ",e.jsx("a",{href:"./#/chat",onClick:()=>{ae(t.title+"\n---\n"+t.prompt)},children:b(t.title)}),e.jsx("br",{})]},`prompt-${r}`)))}),w.map(((t,r)=>e.jsxs("div",{children:[(P&&r===w.length-1||!P&&r===w.length-2)&&e.jsx("div",{ref:T}),e.jsx(l.default,{sender:t.sender,content:t.content,isNew:t.isNew,showSuggestions:r===w.length-1,onSuggestionClicked:k},t.id)]},t.id))),Q&&e.jsx(l.default,{sender:"user",content:b("Översätter...")}),P&&e.jsx(n.default,{})]}),e.jsx(a.default,{codicent:j.context.selectedApp,disableSend:P,onSend:ae,onSelectFiles:se,getImageUrl:c.CodicentService.getImageUrl,getFileInfo:e=>B.getFileInfo(e)})]}),e.jsx(o.default,{open:E,title:b("Vill du rensa chatten?"),content:b("En ny startas, men chatten kommer först sparas."),onYes:()=>{H(!1),_()},onNo:()=>H(!1)}),e.jsx(i.CombinedPlaceholderDialog,{open:z,templateVariables:O,filePlaceholders:D,onTemplateVariablesChange:W,onFilePlaceholdersChange:U,onConfirm:()=>G(!1),onCancel:()=>{G(!1),M(null),W([]),U([])},uploadFile:B.uploadFile})]})};
@@ -31,6 +31,7 @@ export interface RealtimeVoice {
31
31
  items: ItemType[];
32
32
  realtimeEvents: RealtimeEvent[];
33
33
  isConnected: boolean;
34
+ isSessionReady: boolean;
34
35
  canPushToTalk: boolean;
35
36
  isRecording: boolean;
36
37
  clientCanvasRef: React.RefObject<HTMLCanvasElement>;
@@ -1 +1 @@
1
- {"version":3,"file":"useRealtimeVoiceAI.d.ts","sourceRoot":"","sources":["../../../src/hooks/useRealtimeVoiceAI.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAK/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,CAAC;CACH;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,sBAAsB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,sBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,kBAAkB,oBACL,eAAe,UACxB,MAAM,SACP;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,EAAE,UACtD,MAAM,KACb,aAAa,GAAG,SA0pBlB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useRealtimeVoiceAI.d.ts","sourceRoot":"","sources":["../../../src/hooks/useRealtimeVoiceAI.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAK/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,CAAC;CACH;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,sBAAsB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,sBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,kBAAkB,oBACL,eAAe,UACxB,MAAM,SACP;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,EAAE,UACtD,MAAM,KACb,aAAa,GAAG,SA8qBlB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- import{useRef as e,useState as t,useCallback as n,useEffect as r,useMemo as o}from"react";import{WavRenderer as a}from"../utils/wav_renderer.js";import"../lib/wavtools/lib/wav_packer.js";import"../lib/wavtools/lib/analysis/audio_analysis.js";import{WavStreamPlayer as i}from"../lib/wavtools/lib/wav_stream_player.js";import{WavRecorder as s}from"../lib/wavtools/lib/wav_recorder.js";import{getConfigValue as c}from"../config/index.js";const l=(l,u,d,p)=>{const m=!!c("APP_CONFIG"),f=!!c("APP_BUTTONS");m||console.warn("APP_CONFIG is not set. Voice AI will not be available."),f||console.warn("APP_BUTTONS is not set. Voice AI will not be available.");const g=c("APP_CONFIG"),y=c("APP_BUTTONS");c("API_BASE_URL").replace(/\/$/,""),c("USE_REALTIME_SESSION_ENDPOINT"),c("REALTIME_SESSION_ENDPOINT");const _=p||c("REALTIME_VOICE_MODEL")||"alloy",w=["alloy","shimmer","echo"],h=w.includes(_)?_:"alloy";_!==h&&console.warn(`[codicent-app-sdk] Voice "${_}" is not supported in the current SDK version. Supported voices: ${w.join(", ")}. Falling back to "${h}".`);const S=e(new s({sampleRate:24e3})),v=e(new i({sampleRate:24e3})),O=e(null),I=e(null),T=e(null),N=e(null),E=e(!1),A=e(null),C=e(null),R=e(null),P=e((new Date).toISOString()),[b,D]=t([]),[F,J]=t([]),[$,x]=t(!1),[M,k]=t(!1),[L,V]=t(!1),U=e(0),j=e(0),[B,G]=t(""),[q,W]=t("en-US"),H=e(new Map),[z,K]=t((()=>m&&f&&y&&g&&g.apps&&g.apps[y]?g.apps[y].voiceInstructions||g.REALTIME_VOICE_INSTRUCTIONS||"":g&&g.REALTIME_VOICE_INSTRUCTIONS||"")),Q=n((e=>{const t=P.current,n=new Date(t).valueOf(),r=new Date(e).valueOf()-n,o=Math.floor(r/10)%100,a=Math.floor(r/1e3)%60,i=e=>{let t=e+"";for(;t.length<2;)t="0"+t;return t};return`${i(Math.floor(r/6e4)%60)}:${i(a)}.${i(o)}`}),[]),X=n((async()=>{try{P.current=(new Date).toISOString(),x(!0),J([]),D([]),H.current.clear();const e=await l.getRealtimeSessionToken(h);if(!e)throw new Error("No ephemeral key returned from session endpoint");const t=new RTCPeerConnection;O.current=t,T.current||(T.current=new Audio,T.current.autoplay=!0),t.ontrack=e=>{T.current&&e.streams[0]&&(T.current.srcObject=e.streams[0])};const n=await navigator.mediaDevices.getUserMedia({audio:!0});N.current=n;const r=n.getTracks()[0];t.addTrack(r,n);const o=t.createDataChannel("oai-events");I.current=o,o.addEventListener("message",(e=>{try{const t=JSON.parse(e.data);if("session.created"===t.type);else if("conversation.item.created"===t.type)D((e=>[...e,t.item]));else if("conversation.item.input_audio_transcription.completed"===t.type)D((e=>{const n=[...e],r=n.findIndex((e=>e.id===t.item_id));return-1!==r&&n[r].formatted&&(n[r].formatted.transcript=t.transcript),n}));else if("response.audio_transcript.delta"===t.type);else if("response.audio_transcript.done"===t.type);else if("response.output_item.added"===t.type){const e=t.item;"function_call"===e?.type&&(console.log("[Voice AI] Function call initiated:",e.name),H.current.set(e.id,{name:e.name||"",arguments:"",call_id:e.call_id||e.id}))}else if("response.function_call_arguments.delta"===t.type){const e=t.item_id,n=t.delta,r=H.current.get(e);r&&n&&(r.arguments+=n,H.current.set(e,r))}else if("response.function_call_arguments.done"===t.type){const e=t.item_id,n=H.current.get(e);if(n){console.log(`[Voice AI] Executing tool: ${n.name}`);const t=d.find((e=>e.definition.name===n.name));if(t){let e={};try{e=n.arguments?JSON.parse(n.arguments):{}}catch(t){console.error("[Voice AI] Failed to parse tool arguments:",t),e={}}Promise.resolve(t.handler(e)).then((e=>{console.log(`[Voice AI] Tool ${n.name} completed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify(e)}})),o.send(JSON.stringify({type:"response.create"})))})).catch((e=>{console.error(`[Voice AI] Tool ${n.name} failed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify({error:e.message||"Tool execution failed"})}})),o.send(JSON.stringify({type:"response.create"})))}))}else console.error(`[Voice AI] Tool not found: ${n.name}`);H.current.delete(e)}}else"error"===t.type&&console.error("Server error:",t.error)}catch(t){console.warn("Invalid message:",e.data)}})),o.onopen=()=>{console.log("Data channel opened, configuring session"),o.send(JSON.stringify({type:"session.update",session:{instructions:z.replace("{{name}}",B).replace("{{language}}",q).replace("{{time}}",(new Date).toISOString()),modalities:["text","audio"],input_audio_transcription:{model:"whisper-1"},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:500},voice:h,temperature:.8,max_response_output_tokens:4096,input_audio_format:"pcm16",output_audio_format:"pcm16",tools:d.map((e=>({type:"function",...e.definition})))}}))},o.onclose=()=>{console.log("Data channel closed")};const a=await t.createOffer();await t.setLocalDescription(a);let i="gpt-4o-realtime-preview";try{if(c("REALTIME_CONFIG_ENDPOINT")){const e=await l.getRealtimeConfig();e&&e.model&&(i=e.model)}}catch(e){console.warn("Failed to fetch realtime config, using default model:",e)}const s="https://api.openai.com/v1/realtime",u=await fetch(`${s}?model=${i}`,{method:"POST",body:a.sdp,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/sdp"}});if(!u.ok)throw new Error(`Failed to get SDP answer: ${u.statusText}`);const p={type:"answer",sdp:await u.text()};await t.setRemoteDescription(p),k(!1);const m=S.current,f=v.current;await m.begin(),await f.connect()}catch(e){throw console.error("[codicent-app-sdk] Failed to establish WebRTC connection:",e),x(!1),e}}),[l,h,z,B,q,d]),Y=n((async()=>{x(!1),J([]),D([]),H.current.clear(),I.current&&(I.current.close(),I.current=null),O.current&&(O.current.close(),O.current=null),N.current&&(N.current.getTracks().forEach((e=>e.stop())),N.current=null),T.current&&(T.current.pause(),T.current.srcObject=null);const e=S.current;await e.end();const t=v.current;await t.interrupt()}),[]),Z=n((async e=>{const t=I.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"conversation.item.delete",item_id:e}))}),[]),ee=n((async()=>{V(!0);const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"input_audio_buffer.commit"}))}),[]),te=n((async()=>{V(!1);const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"response.create"}))}),[]),ne=n((async e=>{const t=I.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"session.update",session:{turn_detection:"none"===e?null:{type:"server_vad"}}})),k("none"===e)}),[]);r((()=>{if(B&&$){const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"session.update",session:{instructions:z.replace("{{name}}",B).replace("{{language}}",q).replace("{{time}}",(new Date).toISOString())}}))}}),[z,B,q,$]),r((()=>{let e=!0;const t=S.current,n=A.current;let r=null;const o=v.current,i=C.current;let s=null;const c=()=>{if(e){if(n&&(n.width&&n.height||(n.width=n.offsetWidth,n.height=n.offsetHeight),r=r||n.getContext("2d"),r)){r.clearRect(0,0,n.width,n.height);const e=t.recording?t.getFrequencies("voice"):{values:new Float32Array([0])},o=1-Math.max(...e.values);U.current=o,a.drawCircularBars(n,r,e.values,"#0099ff",20,0,8)}if(i&&(i.width&&i.height||(i.width=i.offsetWidth,i.height=i.offsetHeight),s=s||i.getContext("2d"),s)){s.clearRect(0,0,i.width,i.height);const e=o.analyser?o.getFrequencies("voice"):{values:new Float32Array([0])},t=1-Math.max(...e.values);j.current=t,a.drawCircularBars(i,s,e.values,"#009900",20,0,8)}window.requestAnimationFrame(c)}};return c(),()=>{e=!1}}),[]),r((()=>{!E.current&&d&&z&&(E.current=!0)}),[d,z]);const re=n((e=>{const t=e.replace("{{name}}",B).replace("{{language}}",q).replace("{{time}}",(new Date).toISOString()),n=I.current;n&&"open"===n.readyState&&n.send(JSON.stringify({type:"session.update",session:{instructions:t}})),K(e)}),[B,q]);return o((()=>{if(m&&f)return{items:b,realtimeEvents:F,isConnected:$,canPushToTalk:M,isRecording:L,clientCanvasRef:A,serverCanvasRef:C,eventsScrollRef:R,formatTime:Q,connectConversation:X,disconnectConversation:Y,deleteConversationItem:Z,startRecording:ee,stopRecording:te,changeTurnEndType:ne,getRecorderLevel:()=>U.current,getStreamLevel:()=>j.current,setUsername:G,updateInstructions:re,setLanguage:W}}),[m,f,b,F,$,M,L,A,C,R])};export{l as default};
1
+ import{useRef as e,useState as t,useCallback as n,useEffect as r,useMemo as o}from"react";import{WavRenderer as a}from"../utils/wav_renderer.js";import"../lib/wavtools/lib/wav_packer.js";import"../lib/wavtools/lib/analysis/audio_analysis.js";import{WavStreamPlayer as s}from"../lib/wavtools/lib/wav_stream_player.js";import{WavRecorder as i}from"../lib/wavtools/lib/wav_recorder.js";import{getConfigValue as c}from"../config/index.js";const l=(l,u,d,p)=>{const m=!!c("APP_CONFIG"),f=!!c("APP_BUTTONS");m||console.warn("APP_CONFIG is not set. Voice AI will not be available."),f||console.warn("APP_BUTTONS is not set. Voice AI will not be available.");const g=c("APP_CONFIG"),y=c("APP_BUTTONS");c("API_BASE_URL").replace(/\/$/,""),c("USE_REALTIME_SESSION_ENDPOINT"),c("REALTIME_SESSION_ENDPOINT");const _=p||c("REALTIME_VOICE_MODEL")||"alloy",w=["alloy","shimmer","echo"],h=w.includes(_)?_:"alloy";_!==h&&console.warn(`[codicent-app-sdk] Voice "${_}" is not supported in the current SDK version. Supported voices: ${w.join(", ")}. Falling back to "${h}".`);const S=e(new i({sampleRate:24e3})),v=e(new s({sampleRate:24e3})),O=e(null),I=e(null),T=e(null),N=e(null),E=e(!1),A=e(null),R=e(null),C=e(null),P=e((new Date).toISOString()),[b,D]=t([]),[F,J]=t([]),[$,x]=t(!1),[M,k]=t(!1),[L,V]=t(!1),[U,j]=t(!1),B=e(0),G=e(0),[q,W]=t(""),[H,z]=t("en-US"),K=e(new Map),Q=e(null),[X,Y]=t((()=>m&&f&&y&&g&&g.apps&&g.apps[y]?g.apps[y].voiceInstructions||g.REALTIME_VOICE_INSTRUCTIONS||"":g&&g.REALTIME_VOICE_INSTRUCTIONS||"")),Z=n((e=>{const t=P.current,n=new Date(t).valueOf(),r=new Date(e).valueOf()-n,o=Math.floor(r/10)%100,a=Math.floor(r/1e3)%60,s=e=>{let t=e+"";for(;t.length<2;)t="0"+t;return t};return`${s(Math.floor(r/6e4)%60)}:${s(a)}.${s(o)}`}),[]),ee=n((async()=>{try{P.current=(new Date).toISOString(),x(!0),J([]),D([]),K.current.clear();const e=await l.getRealtimeSessionToken(h);if(!e)throw new Error("No ephemeral key returned from session endpoint");const t=new RTCPeerConnection;O.current=t,T.current||(T.current=new Audio,T.current.autoplay=!0),t.ontrack=e=>{T.current&&e.streams[0]&&(T.current.srcObject=e.streams[0])};const n=await navigator.mediaDevices.getUserMedia({audio:!0});N.current=n;const r=n.getTracks()[0];t.addTrack(r,n);const o=t.createDataChannel("oai-events");I.current=o,o.addEventListener("message",(e=>{try{const t=JSON.parse(e.data);if("session.created"===t.type);else if("conversation.item.created"===t.type)D((e=>[...e,t.item]));else if("conversation.item.input_audio_transcription.completed"===t.type)D((e=>{const n=[...e],r=n.findIndex((e=>e.id===t.item_id));return-1!==r&&n[r].formatted&&(n[r].formatted.transcript=t.transcript),n}));else if("response.audio_transcript.delta"===t.type);else if("response.audio_transcript.done"===t.type);else if("response.output_item.added"===t.type){const e=t.item;"function_call"===e?.type&&(console.log("[Voice AI] Function call initiated:",e.name),K.current.set(e.id,{name:e.name||"",arguments:"",call_id:e.call_id||e.id}))}else if("response.function_call_arguments.delta"===t.type){const e=t.item_id,n=t.delta,r=K.current.get(e);r&&n&&(r.arguments+=n,K.current.set(e,r))}else if("response.function_call_arguments.done"===t.type){const e=t.item_id,n=K.current.get(e);if(n){console.log(`[Voice AI] Executing tool: ${n.name}`);const t=d.find((e=>e.definition.name===n.name));if(t){let e={};try{e=n.arguments?JSON.parse(n.arguments):{}}catch(t){console.error("[Voice AI] Failed to parse tool arguments:",t),e={}}Promise.resolve(t.handler(e)).then((e=>{console.log(`[Voice AI] Tool ${n.name} completed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify(e)}})),o.send(JSON.stringify({type:"response.create"})))})).catch((e=>{console.error(`[Voice AI] Tool ${n.name} failed:`,e),o&&"open"===o.readyState&&(o.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:n.call_id,output:JSON.stringify({error:e.message||"Tool execution failed"})}})),o.send(JSON.stringify({type:"response.create"})))}))}else console.error(`[Voice AI] Tool not found: ${n.name}`);K.current.delete(e)}}else"error"===t.type&&console.error("Server error:",t.error)}catch(t){console.warn("Invalid message:",e.data)}})),o.onopen=()=>{console.log("Data channel opened, configuring session");const e=Q.current||X;Q.current=null,o.send(JSON.stringify({type:"session.update",session:{instructions:e.replace("{{name}}",q).replace("{{language}}",H).replace("{{time}}",(new Date).toISOString()),modalities:["text","audio"],input_audio_transcription:{model:"whisper-1"},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:500},voice:h,temperature:.8,max_response_output_tokens:4096,input_audio_format:"pcm16",output_audio_format:"pcm16",tools:d.map((e=>({type:"function",...e.definition})))}})),k(!0)},o.onclose=()=>{console.log("Data channel closed"),k(!1)};const a=await t.createOffer();await t.setLocalDescription(a);let s="gpt-4o-realtime-preview";try{if(c("REALTIME_CONFIG_ENDPOINT")){const e=await l.getRealtimeConfig();e&&e.model&&(s=e.model)}}catch(e){console.warn("Failed to fetch realtime config, using default model:",e)}const i="https://api.openai.com/v1/realtime",u=await fetch(`${i}?model=${s}`,{method:"POST",body:a.sdp,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/sdp"}});if(!u.ok)throw new Error(`Failed to get SDP answer: ${u.statusText}`);const p={type:"answer",sdp:await u.text()};await t.setRemoteDescription(p),V(!1);const m=S.current,f=v.current;await m.begin(),await f.connect()}catch(e){throw console.error("[codicent-app-sdk] Failed to establish WebRTC connection:",e),x(!1),e}}),[l,h,X,q,H,d]),te=n((async()=>{x(!1),k(!1),J([]),D([]),K.current.clear(),Q.current=null,I.current&&(I.current.close(),I.current=null),O.current&&(O.current.close(),O.current=null),N.current&&(N.current.getTracks().forEach((e=>e.stop())),N.current=null),T.current&&(T.current.pause(),T.current.srcObject=null);const e=S.current;await e.end();const t=v.current;await t.interrupt()}),[]),ne=n((async e=>{const t=I.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"conversation.item.delete",item_id:e}))}),[]),re=n((async()=>{j(!0);const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"input_audio_buffer.commit"}))}),[]),oe=n((async()=>{j(!1);const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"response.create"}))}),[]),ae=n((async e=>{const t=I.current;t&&"open"===t.readyState&&t.send(JSON.stringify({type:"session.update",session:{turn_detection:"none"===e?null:{type:"server_vad"}}})),V("none"===e)}),[]);r((()=>{if(q&&M){const e=I.current;e&&"open"===e.readyState&&e.send(JSON.stringify({type:"session.update",session:{instructions:X.replace("{{name}}",q).replace("{{language}}",H).replace("{{time}}",(new Date).toISOString())}}))}}),[X,q,H,M]),r((()=>{let e=!0;const t=S.current,n=A.current;let r=null;const o=v.current,s=R.current;let i=null;const c=()=>{if(e){if(n&&(n.width&&n.height||(n.width=n.offsetWidth,n.height=n.offsetHeight),r=r||n.getContext("2d"),r)){r.clearRect(0,0,n.width,n.height);const e=t.recording?t.getFrequencies("voice"):{values:new Float32Array([0])},o=1-Math.max(...e.values);B.current=o,a.drawCircularBars(n,r,e.values,"#0099ff",20,0,8)}if(s&&(s.width&&s.height||(s.width=s.offsetWidth,s.height=s.offsetHeight),i=i||s.getContext("2d"),i)){i.clearRect(0,0,s.width,s.height);const e=o.analyser?o.getFrequencies("voice"):{values:new Float32Array([0])},t=1-Math.max(...e.values);G.current=t,a.drawCircularBars(s,i,e.values,"#009900",20,0,8)}window.requestAnimationFrame(c)}};return c(),()=>{e=!1}}),[]),r((()=>{!E.current&&d&&X&&(E.current=!0)}),[d,X]);const se=n((e=>{Y(e);const t=I.current;if(t&&"open"===t.readyState){const n=e.replace("{{name}}",q).replace("{{language}}",H).replace("{{time}}",(new Date).toISOString());t.send(JSON.stringify({type:"session.update",session:{instructions:n}}))}else Q.current=e}),[q,H]);return o((()=>{if(m&&f)return{items:b,realtimeEvents:F,isConnected:$,isSessionReady:M,canPushToTalk:L,isRecording:U,clientCanvasRef:A,serverCanvasRef:R,eventsScrollRef:C,formatTime:Z,connectConversation:ee,disconnectConversation:te,deleteConversationItem:ne,startRecording:re,stopRecording:oe,changeTurnEndType:ae,getRecorderLevel:()=>B.current,getStreamLevel:()=>G.current,setUsername:W,updateInstructions:se,setLanguage:z}}),[m,f,b,F,$,M,L,U,A,R,C])};export{l as default};
@@ -0,0 +1 @@
1
+ import{RealtimeEventHandler as e}from"./event_handler.js";import{RealtimeUtils as t}from"./utils.js";class s extends e{constructor({url:e,apiKey:t,dangerouslyAllowAPIKeyInBrowser:s,debug:r}={}){if(super(),this.defaultUrl="wss://api.openai.com/v1/realtime",this.url=e||this.defaultUrl,this.apiKey=t||null,this.debug=!!r,this.ws=null,globalThis.document&&this.apiKey&&!s)throw new Error('Can not provide API key in the browser without "dangerouslyAllowAPIKeyInBrowser" set to true')}isConnected(){return!!this.ws}log(...e){const t=[`[Websocket/${(new Date).toISOString()}]`].concat(e).map((e=>"object"==typeof e&&null!==e?JSON.stringify(e,null,2):e));return this.debug&&console.log(...t),!0}async connect({model:e}={model:"gpt-4o-realtime-preview-2024-10-01"}){if(this.apiKey||this.url!==this.defaultUrl||console.warn(`No apiKey provided for connection to "${this.url}"`),this.isConnected())throw new Error("Already connected");if(globalThis.WebSocket){globalThis.document&&this.apiKey&&console.warn("Warning: Connecting using API key in the browser, this is not recommended");const e=new(0,globalThis.WebSocket)(`${this.url}`,["realtime",`websocket.api_key.${this.apiKey}`]);return e.addEventListener("message",(e=>{const t=JSON.parse(e.data);this.receive(t.type,t)})),new Promise(((t,s)=>{const r=()=>{this.disconnect(e),s(new Error(`Could not connect to "${this.url}"`))};e.addEventListener("error",r),e.addEventListener("open",(()=>{this.log(`Connected to "${this.url}"`),e.removeEventListener("error",r),e.addEventListener("error",(()=>{this.disconnect(e),this.log(`Error, disconnected from "${this.url}"`),this.dispatch("close",{error:!0})})),e.addEventListener("close",(()=>{this.disconnect(e),this.log(`Disconnected from "${this.url}"`),this.dispatch("close",{error:!1})})),this.ws=e,t(!0)}))}))}{const t="ws",s=new(0,(await import(t)).default)(`${this.url||"wss://api.openai.com/v1/realtime"}${e?`?model=${e}`:""}`,[],{finishRequest:e=>{e.setHeader("Authorization",`Bearer ${this.apiKey}`),e.setHeader("OpenAI-Beta","realtime=v1"),e.setHeader("api-key",this.apiKey),e.end()}});return s.on("message",(e=>{const t=JSON.parse(e.toString());this.receive(t.type,t)})),new Promise(((e,t)=>{const r=()=>{this.disconnect(s),t(new Error(`Could not connect to "${this.url}"`))};s.on("error",r),s.on("open",(()=>{this.log(`Connected to "${this.url}"`),s.removeListener("error",r),s.on("error",(()=>{this.disconnect(s),this.log(`Error, disconnected from "${this.url}"`),this.dispatch("close",{error:!0})})),s.on("close",(()=>{this.disconnect(s),this.log(`Disconnected from "${this.url}"`),this.dispatch("close",{error:!1})})),this.ws=s,e(!0)}))}))}}disconnect(e){if(!e||this.ws===e)return this.ws&&this.ws.close(),this.ws=null,!0}receive(e,t){return this.log("received:",e,t),this.dispatch(`server.${e}`,t),this.dispatch("server.*",t),!0}send(e,s){if(!this.isConnected())throw new Error("RealtimeAPI is not connected");if("object"!=typeof(s=s||{}))throw new Error("data must be an object");const r={event_id:t.generateId("evt_"),type:e,...s};return this.dispatch(`client.${e}`,r),this.dispatch("client.*",r),this.log("sent:",e,r),this.ws.send(JSON.stringify(r)),!0}}export{s as RealtimeAPI};
@@ -0,0 +1 @@
1
+ import{RealtimeEventHandler as e}from"./event_handler.js";import{RealtimeAPI as t}from"./api.js";import{RealtimeConversation as i}from"./conversation.js";import{RealtimeUtils as s}from"./utils.js";class o extends e{constructor({url:e,apiKey:s,dangerouslyAllowAPIKeyInBrowser:o,debug:n}={}){super(),this.defaultSessionConfig={modalities:["text","audio"],instructions:"",voice:"alloy",input_audio_format:"pcm16",output_audio_format:"pcm16",input_audio_transcription:null,turn_detection:null,tools:[],tool_choice:"auto",temperature:.8,max_response_output_tokens:4096},this.sessionConfig={},this.transcriptionModels=[{model:"whisper-1"}],this.defaultServerVadConfig={type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:200},this.realtime=new t({url:e,apiKey:s,dangerouslyAllowAPIKeyInBrowser:o,debug:n}),this.conversation=new i,this._resetConfig(),this._addAPIEventHandlers()}_resetConfig(){return this.sessionCreated=!1,this.tools={},this.sessionConfig=JSON.parse(JSON.stringify(this.defaultSessionConfig)),this.inputAudioBuffer=new Int16Array(0),!0}_addAPIEventHandlers(){this.realtime.on("client.*",(e=>{const t={time:(new Date).toISOString(),source:"client",event:e};this.dispatch("realtime.event",t)})),this.realtime.on("server.*",(e=>{const t={time:(new Date).toISOString(),source:"server",event:e};this.dispatch("realtime.event",t)})),this.realtime.on("server.session.created",(()=>this.sessionCreated=!0));const e=(e,...t)=>{const{item:i,delta:s}=this.conversation.processEvent(e,...t);return{item:i,delta:s}},t=(t,...i)=>{const{item:s,delta:o}=e(t,...i);return s&&this.dispatch("conversation.updated",{item:s,delta:o}),{item:s,delta:o}},i=async e=>{try{const t=JSON.parse(e.arguments),i=this.tools[e.name];if(!i)throw new Error(`Tool "${e.name}" has not been added`);const s=await i.handler(t);this.realtime.send("conversation.item.create",{item:{type:"function_call_output",call_id:e.call_id,output:JSON.stringify(s)}})}catch(t){this.realtime.send("conversation.item.create",{item:{type:"function_call_output",call_id:e.call_id,output:JSON.stringify({error:t.message})}})}this.createResponse()};return this.realtime.on("server.response.created",e),this.realtime.on("server.response.output_item.added",e),this.realtime.on("server.response.content_part.added",e),this.realtime.on("server.input_audio_buffer.speech_started",(t=>{e(t),this.dispatch("conversation.interrupted")})),this.realtime.on("server.input_audio_buffer.speech_stopped",(t=>e(t,this.inputAudioBuffer))),this.realtime.on("server.conversation.item.created",(e=>{const{item:i}=t(e);this.dispatch("conversation.item.appended",{item:i}),"completed"===i.status&&this.dispatch("conversation.item.completed",{item:i})})),this.realtime.on("server.conversation.item.truncated",t),this.realtime.on("server.conversation.item.deleted",t),this.realtime.on("server.conversation.item.input_audio_transcription.completed",t),this.realtime.on("server.response.audio_transcript.delta",t),this.realtime.on("server.response.audio.delta",t),this.realtime.on("server.response.text.delta",t),this.realtime.on("server.response.function_call_arguments.delta",t),this.realtime.on("server.response.output_item.done",(async e=>{const{item:s}=t(e);"completed"===s.status&&this.dispatch("conversation.item.completed",{item:s}),s.formatted.tool&&i(s.formatted.tool)})),!0}isConnected(){return this.realtime.isConnected()}reset(){return this.disconnect(),this.clearEventHandlers(),this.realtime.clearEventHandlers(),this._resetConfig(),this._addAPIEventHandlers(),!0}async connect(){if(this.isConnected())throw new Error("Already connected, use .disconnect() first");return await this.realtime.connect(),this.updateSession(),!0}async waitForSessionCreated(){if(!this.isConnected())throw new Error("Not connected, use .connect() first");for(;!this.sessionCreated;)await new Promise((e=>setTimeout((()=>e()),1)));return!0}disconnect(){this.sessionCreated=!1,this.realtime.isConnected()&&this.realtime.disconnect(),this.conversation.clear()}getTurnDetectionType(){return this.sessionConfig.turn_detection?.type||null}addTool(e,t){if(!e?.name)throw new Error("Missing tool name in definition");const i=e?.name;if(this.tools[i])throw new Error(`Tool "${i}" already added. Please use .removeTool("${i}") before trying to add again.`);if("function"!=typeof t)throw new Error(`Tool "${i}" handler must be a function`);return this.tools[i]={definition:e,handler:t},this.updateSession(),this.tools[i]}removeTool(e){if(!this.tools[e])throw new Error(`Tool "${e}" does not exist, can not be removed.`);return delete this.tools[e],!0}deleteItem(e){return this.realtime.send("conversation.item.delete",{item_id:e}),!0}updateSession({modalities:e,instructions:t,voice:i,input_audio_format:s,output_audio_format:o,input_audio_transcription:n,turn_detection:r,tools:a,tool_choice:d,temperature:u,max_response_output_tokens:c}={}){void 0!==e&&(this.sessionConfig.modalities=e),void 0!==t&&(this.sessionConfig.instructions=t),void 0!==i&&(this.sessionConfig.voice=i),void 0!==s&&(this.sessionConfig.input_audio_format=s),void 0!==o&&(this.sessionConfig.output_audio_format=o),void 0!==n&&(this.sessionConfig.input_audio_transcription=n),void 0!==r&&(this.sessionConfig.turn_detection=r),void 0!==a&&(this.sessionConfig.tools=a),void 0!==d&&(this.sessionConfig.tool_choice=d),void 0!==u&&(this.sessionConfig.temperature=u),void 0!==c&&(this.sessionConfig.max_response_output_tokens=c);const l=[].concat((a||[]).map((e=>{const t={type:"function",...e};if(this.tools[t?.name])throw new Error(`Tool "${t?.name}" has already been defined`);return t})),Object.keys(this.tools).map((e=>({type:"function",...this.tools[e].definition})))),m={...this.sessionConfig};return m.tools=l,this.realtime.isConnected()&&this.realtime.send("session.update",{session:m}),!0}sendUserMessageContent(e=[]){if(e.length){for(const t of e)"input_audio"===t.type&&(t.audio instanceof ArrayBuffer||t.audio instanceof Int16Array)&&(t.audio=s.arrayBufferToBase64(t.audio));this.realtime.send("conversation.item.create",{item:{type:"message",role:"user",content:e}})}return this.createResponse(),!0}appendInputAudio(e){return e.byteLength>0&&(this.realtime.send("input_audio_buffer.append",{audio:s.arrayBufferToBase64(e)}),this.inputAudioBuffer=s.mergeInt16Arrays(this.inputAudioBuffer,e)),!0}createResponse(){return null===this.getTurnDetectionType()&&this.inputAudioBuffer.byteLength>0&&(this.realtime.send("input_audio_buffer.commit"),this.conversation.queueInputAudio(this.inputAudioBuffer),this.inputAudioBuffer=new Int16Array(0)),this.realtime.send("response.create"),!0}cancelResponse(e,t=0){if(!e)return this.realtime.send("response.cancel"),{item:null};if(e){const i=this.conversation.getItem(e);if(!i)throw new Error(`Could not find item "${e}"`);if("message"!==i.type)throw new Error('Can only cancelResponse messages with type "message"');if("assistant"!==i.role)throw new Error('Can only cancelResponse messages with role "assistant"');this.realtime.send("response.cancel");const s=i.content.findIndex((e=>"audio"===e.type));if(-1===s)throw new Error("Could not find audio on item to cancel");return this.realtime.send("conversation.item.truncate",{item_id:e,content_index:s,audio_end_ms:Math.floor(t/this.conversation.defaultFrequency*1e3)}),{item:i}}}async waitForNextItem(){const e=await this.waitForNext("conversation.item.appended"),{item:t}=e;return{item:t}}async waitForNextCompletedItem(){const e=await this.waitForNext("conversation.item.completed"),{item:t}=e;return{item:t}}}export{o as RealtimeClient};
@@ -0,0 +1 @@
1
+ import{RealtimeUtils as t}from"./utils.js";class e{defaultFrequency=24e3;EventProcessors={"conversation.item.created":t=>{const{item:e}=t,o=JSON.parse(JSON.stringify(e));if(this.itemLookup[o.id]||(this.itemLookup[o.id]=o,this.items.push(o)),o.formatted={},o.formatted.audio=new Int16Array(0),o.formatted.text="",o.formatted.transcript="",this.queuedSpeechItems[o.id]&&(o.formatted.audio=this.queuedSpeechItems[o.id].audio,delete this.queuedSpeechItems[o.id]),o.content){const t=o.content.filter((t=>["text","input_text"].includes(t.type)));for(const e of t)o.formatted.text+=e.text}return this.queuedTranscriptItems[o.id]&&(o.formatted.transcript=this.queuedTranscriptItems.transcript,delete this.queuedTranscriptItems[o.id]),"message"===o.type?"user"===o.role?(o.status="completed",this.queuedInputAudio&&(o.formatted.audio=this.queuedInputAudio,this.queuedInputAudio=null)):o.status="in_progress":"function_call"===o.type?(o.formatted.tool={type:"function",name:o.name,call_id:o.call_id,arguments:""},o.status="in_progress"):"function_call_output"===o.type&&(o.status="completed",o.formatted.output=o.output),{item:o,delta:null}},"conversation.item.truncated":t=>{const{item_id:e,audio_end_ms:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`item.truncated: Item "${e}" not found`);const r=Math.floor(o*this.defaultFrequency/1e3);return s.formatted.transcript="",s.formatted.audio=s.formatted.audio.slice(0,r),{item:s,delta:null}},"conversation.item.deleted":t=>{const{item_id:e}=t,o=this.itemLookup[e];if(!o)throw new Error(`item.deleted: Item "${e}" not found`);delete this.itemLookup[o.id];const s=this.items.indexOf(o);return s>-1&&this.items.splice(s,1),{item:o,delta:null}},"conversation.item.input_audio_transcription.completed":t=>{const{item_id:e,content_index:o,transcript:s}=t,r=this.itemLookup[e],n=s||" ";return r?(r.content[o].transcript=s,r.formatted.transcript=n,{item:r,delta:{transcript:s}}):(this.queuedTranscriptItems[e]={transcript:n},{item:null,delta:null})},"input_audio_buffer.speech_started":t=>{const{item_id:e,audio_start_ms:o}=t;return this.queuedSpeechItems[e]={audio_start_ms:o},{item:null,delta:null}},"input_audio_buffer.speech_stopped":(t,e)=>{const{item_id:o,audio_end_ms:s}=t;this.queuedSpeechItems[o]||(this.queuedSpeechItems[o]={audio_start_ms:s});const r=this.queuedSpeechItems[o];if(r.audio_end_ms=s,e){const t=Math.floor(r.audio_start_ms*this.defaultFrequency/1e3),o=Math.floor(r.audio_end_ms*this.defaultFrequency/1e3);r.audio=e.slice(t,o)}return{item:null,delta:null}},"response.created":t=>{const{response:e}=t;return this.responseLookup[e.id]||(this.responseLookup[e.id]=e,this.responses.push(e)),{item:null,delta:null}},"response.output_item.added":t=>{const{response_id:e,item:o}=t,s=this.responseLookup[e];if(!s)throw new Error(`response.output_item.added: Response "${e}" not found`);return s.output.push(o.id),{item:null,delta:null}},"response.output_item.done":t=>{const{item:e}=t;if(!e)throw new Error('response.output_item.done: Missing "item"');const o=this.itemLookup[e.id];if(!o)throw new Error(`response.output_item.done: Item "${e.id}" not found`);return o.status=e.status,{item:o,delta:null}},"response.content_part.added":t=>{const{item_id:e,part:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`response.content_part.added: Item "${e}" not found`);return s.content.push(o),{item:s,delta:null}},"response.audio_transcript.delta":t=>{const{item_id:e,content_index:o,delta:s}=t,r=this.itemLookup[e];if(!r)throw new Error(`response.audio_transcript.delta: Item "${e}" not found`);return r.content[o].transcript+=s,r.formatted.transcript+=s,{item:r,delta:{transcript:s}}},"response.audio.delta":e=>{const{item_id:o,content_index:s,delta:r}=e,n=this.itemLookup[o];if(!n)throw new Error(`response.audio.delta: Item "${o}" not found`);const i=t.base64ToArrayBuffer(r),u=new Int16Array(i);return n.formatted.audio=t.mergeInt16Arrays(n.formatted.audio,u),{item:n,delta:{audio:u}}},"response.text.delta":t=>{const{item_id:e,content_index:o,delta:s}=t,r=this.itemLookup[e];if(!r)throw new Error(`response.text.delta: Item "${e}" not found`);return r.content[o].text+=s,r.formatted.text+=s,{item:r,delta:{text:s}}},"response.function_call_arguments.delta":t=>{const{item_id:e,delta:o}=t,s=this.itemLookup[e];if(!s)throw new Error(`response.function_call_arguments.delta: Item "${e}" not found`);return s.arguments+=o,s.formatted.tool.arguments+=o,{item:s,delta:{arguments:o}}}};constructor(){this.clear()}clear(){return this.itemLookup={},this.items=[],this.responseLookup={},this.responses=[],this.queuedSpeechItems={},this.queuedTranscriptItems={},this.queuedInputAudio=null,!0}queueInputAudio(t){return this.queuedInputAudio=t,t}processEvent(t,...e){if(!t.event_id)throw console.error(t),new Error('Missing "event_id" on event');if(!t.type)throw console.error(t),new Error('Missing "type" on event');const o=this.EventProcessors[t.type];if(!o)throw new Error(`Missing conversation event processor for "${t.type}"`);return o.call(this,t,...e)}getItem(t){return this.itemLookup[t]||null}getItems(){return this.items.slice()}}export{e as RealtimeConversation};
@@ -0,0 +1 @@
1
+ const e=e=>new Promise((t=>setTimeout((()=>t()),e)));class t{constructor(){this.eventHandlers={},this.nextEventHandlers={}}clearEventHandlers(){return this.eventHandlers={},this.nextEventHandlers={},!0}on(e,t){return this.eventHandlers[e]=this.eventHandlers[e]||[],this.eventHandlers[e].push(t),t}onNext(e,t){return this.nextEventHandlers[e]=this.nextEventHandlers[e]||[],this.nextEventHandlers[e].push(t),t}off(e,t){const n=this.eventHandlers[e]||[];if(t){const s=n.indexOf(t);if(-1===s)throw new Error(`Could not turn off specified event listener for "${e}": not found as a listener`);n.splice(s,1)}else delete this.eventHandlers[e];return!0}offNext(e,t){const n=this.nextEventHandlers[e]||[];if(t){const s=n.indexOf(t);if(-1===s)throw new Error(`Could not turn off specified next event listener for "${e}": not found as a listener`);n.splice(s,1)}else delete this.nextEventHandlers[e];return!0}async waitForNext(t,n=null){const s=Date.now();let r;for(this.onNext(t,(e=>r=e));!r;){if(n){if(Date.now()-s>n)return null}await e(1)}return r}dispatch(e,t){const n=[].concat(this.eventHandlers[e]||[]);for(const e of n)e(t);const s=[].concat(this.nextEventHandlers[e]||[]);for(const e of s)e(t);return delete this.nextEventHandlers[e],!0}}export{t as RealtimeEventHandler};
@@ -0,0 +1 @@
1
+ const t=globalThis.atob,r=globalThis.btoa;class e{static floatTo16BitPCM(t){const r=new ArrayBuffer(2*t.length),e=new DataView(r);let n=0;for(let r=0;r<t.length;r++,n+=2){let a=Math.max(-1,Math.min(1,t[r]));e.setInt16(n,a<0?32768*a:32767*a,!0)}return r}static base64ToArrayBuffer(r){const e=t(r),n=e.length,a=new Uint8Array(n);for(let t=0;t<n;t++)a[t]=e.charCodeAt(t);return a.buffer}static arrayBufferToBase64(t){t instanceof Float32Array?t=this.floatTo16BitPCM(t):t instanceof Int16Array&&(t=t.buffer);let e="",n=new Uint8Array(t);for(let t=0;t<n.length;t+=32768){let r=n.subarray(t,t+32768);e+=String.fromCharCode.apply(null,r)}return r(e)}static mergeInt16Arrays(t,r){if(t instanceof ArrayBuffer&&(t=new Int16Array(t)),r instanceof ArrayBuffer&&(r=new Int16Array(r)),!(t instanceof Int16Array&&r instanceof Int16Array))throw new Error("Both items must be Int16Array");const e=new Int16Array(t.length+r.length);for(let r=0;r<t.length;r++)e[r]=t[r];for(let n=0;n<r.length;n++)e[t.length+n]=r[n];return e}static generateId(t,r=21){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";return`${t}${Array(r-t.length).fill(0).map((t=>e[Math.floor(58*Math.random())])).join("")}`}}export{e as RealtimeUtils};
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { CodicentAppState } from "../hooks";
3
3
  declare const Chat: React.FC<{
4
4
  state: CodicentAppState;
5
+ hideFooter?: boolean;
5
6
  }>;
6
7
  export default Chat;
7
8
  //# sourceMappingURL=Chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/pages/Chat.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,OAAO,EACL,gBAAgB,EAOjB,MAAM,UAAU,CAAC;AAsClB,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAA;CAAE,CAmR/C,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/pages/Chat.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,OAAO,EACL,gBAAgB,EAOjB,MAAM,UAAU,CAAC;AAsClB,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAmRrE,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -1 +1 @@
1
- import{jsxs as e,jsx as o}from"react/jsx-runtime";import{useRef as t,useState as n,useEffect as r}from"react";import{useNavigate as s,useSearchParams as i}from"react-router-dom";import"../components/Markdown.js";import"../components/Textarea.js";import"../components/Button.js";import"../components/CompoundButton.js";import"../components/Spinner.js";import a from"../components/TextHeader.js";import l from"../components/TypingIndicator.js";import m from"../components/Prompt.js";import p from"../components/ChatInput.js";import{CombinedPlaceholderDialog as c}from"../components/CombinedPlaceholderDialog.js";import d from"../components/ChatMessage.js";import"../components/Header.js";import h from"../hooks/useStateWithLocalStorage.js";import{CodicentService as u}from"../services/codicent.js";import"../utils/MessageContent.js";import"../node_modules/tinycolor2/esm/tinycolor.js";import"../_virtual/index.js";import{getConfigValue as g}from"../config/index.js";import"../utils/cacheManager.js";import"../lib/wavtools/lib/wav_packer.js";import"../lib/wavtools/lib/analysis/audio_analysis.js";import"../lib/wavtools/lib/wav_stream_player.js";import"../lib/wavtools/lib/wav_recorder.js";import"./AppFrame.js";import"./Compose.js";import"./Snap.js";import"./Search.js";import"./Login.js";import"./CrmPage.js";import"./CrmPagePersistent.js";import"./ImageView.js";import"./FormInvite.js";import"./FormAccept.js";import"./Sales.js";import"./Purchase.js";import{useChat as j}from"../hooks/useChat.js";import f from"../hooks/useLocalization.js";import{useTemplateVariables as b}from"../hooks/useTemplateVariables.js";import"../hooks/useAppStyles.js";import"../components/FileThumbnail.js";import"../components/MessageInput.js";import"../components/UploadFile.js";import"../components/SnapFooter.js";import"../components/Profile.js";import"../components/MessageItem.js";import"../components/Content.js";import"../components/AiInput.js";import"../components/SearchBox.js";import"../components/HtmlView.js";import"../components/Footer.js";import{Page as x}from"../components/Page.js";import{makeStyles as C}from"../node_modules/@griffel/react/makeStyles.esm.js";import{CalendarWeekStart24Regular as w}from"../node_modules/@fluentui/react-icons/lib/sizedIcons/chunk-3.js";const y=C({chatContainer:{width:"100%",flex:1,maxWidth:"800px",height:"100%",display:"flex",flexDirection:"column",margin:"0 auto",overflow:"hidden","@media (max-width: 768px)":{maxWidth:"100%",borderRadius:"0"},"@media (max-width: 480px)":{},backgroundPosition:"calc(50%) center",backgroundRepeat:"no-repeat",backgroundSize:"contain",touchAction:"pan-y"},chatMessages:{display:"flex",flexDirection:"column",flex:1,padding:"16px",overflowY:"auto",touchAction:"pan-y"}}),v=({state:C})=>{const v=y(),{t:S,tAsync:k,getLanguageInfo:F}=f(),I=s(),{messages:P,isBotTyping:T,handleSend:_,newChat:A,openChat:M}=j(C.stateMachine),H=t(null),[V,N]=i(),[B,L]=n(null),[R,O]=n(!1),[W,E]=n(!1),{service:U,currentStateName:z}=C,[D,G]=n([]),[Y,$]=n([]),[K,q]=n(!1),[J,Q]=n(),[X,Z]=h(C.context.selectedApp+"_prompts",[]),[ee,oe]=n(!1),{extractTemplateVariables:te,replaceTemplateVariables:ne,extractFilePlaceholders:re,replaceFilePlaceholders:se,handleSelectFiles:ie}=b(U.uploadFile),ae=t(!1),le=t(null);r((()=>{const e=window.history.length>1,o=Boolean(document.referrer&&document.referrer!==window.location.href);E(e||o)}),[]);r((()=>{g("SHOW_CHAT_PROMPTS")&&0===P.length&&C.service.getAppPrompts().then(Z).catch((e=>{console.error("Failed to get prompts",e)}))}),[C.service,Z,P.length]),r((()=>{setTimeout((()=>{H.current?.scrollIntoView({behavior:"smooth"})}),10)}),[P]),r((()=>{if(ae.current)return;ae.current=!0;const e=V.get("text"),o=V.get("id"),t=V.get("fid");if(e){(async()=>{const o=re(e),t=te(e);A(),o.length>0||t.length>0?($(o),G(t),q(!0),L(S(e))):L(S(e))})()}else o?M(o):t&&Q(t)}),[]),r((()=>{J&&"anonymous"===z&&le.current!==J&&(le.current=J,U.getFormById(J).then((async e=>{A();const o=F().language,t=e.prompt.replace("{{fid}}",J).replace("{{fname}}",e.name).replace("{{language}}",o);oe(!0);const n=await k(t);oe(!1),L(n),E(!1)})))}),[J,A,U,L,z,k,F,oe]),r((()=>{if(B&&!K){let e=B;Y.length>0&&(e=se(e,Y)),D.length>0&&(e=ne(e,D)),_(e),N({},{replace:!0}),L(null),G([]),$([])}}),[B,K,_,N,se,ne,Y,D]);const me=e=>{const o=V.get("append");o&&(e+="\n---\n"+o),_(e)};return e(x,{hideHeader:!0,hideFooter:!!J,children:[e("div",{id:"chat-container",className:v.chatContainer,style:{backgroundImage:`url(${g("CHAT_BACKGROUND_IMAGE_URL")})`},children:[W&&o("button",{onClick:()=>{W&&I(-1)},"aria-label":S("Tillbaka"),style:{position:"absolute",top:12,left:12,zIndex:100,background:"var(--colorNeutralBackground1)",border:"none",cursor:"pointer",padding:8,borderRadius:8,boxShadow:"0 2px 8px rgba(0,0,0,0.08)",display:"flex",alignItems:"center",justifyContent:"center"},children:o(w,{style:{transform:"rotate(180deg)"}})}),o("div",{style:{marginLeft:W?40:0},children:o(a,{title:S(g("APP_CHAT_TITLE")||"Chatt"),onNewChat:J?void 0:()=>O(!0)})}),e("div",{className:v.chatMessages,children:[g("SHOW_CHAT_PROMPTS")&&X.length>0&&0===P.length&&o(d,{sender:"bot",content:S(g("CHAT_WELCOME")||"Välkommen!"),children:X.map(((t,n)=>e("div",{children:["> ",o("a",{href:"./#/chat",onClick:()=>{me(t.title+"\n---\n"+t.prompt)},children:S(t.title)}),o("br",{})]},`prompt-${n}`)))}),P.map(((t,n)=>e("div",{children:[(T&&n===P.length-1||!T&&n===P.length-2)&&o("div",{ref:H}),o(d,{sender:t.sender,content:t.content,isNew:t.isNew,showSuggestions:n===P.length-1,onSuggestionClicked:_},t.id)]},t.id))),ee&&o(d,{sender:"user",content:S("Översätter...")}),T&&o(l,{})]}),o(p,{codicent:C.context.selectedApp,disableSend:T,onSend:me,onSelectFiles:ie,getImageUrl:u.getImageUrl,getFileInfo:e=>U.getFileInfo(e)})]}),o(m,{open:R,title:S("Vill du rensa chatten?"),content:S("En ny startas, men chatten kommer först sparas."),onYes:()=>{O(!1),A()},onNo:()=>O(!1)}),o(c,{open:K,templateVariables:D,filePlaceholders:Y,onTemplateVariablesChange:G,onFilePlaceholdersChange:$,onConfirm:()=>q(!1),onCancel:()=>{q(!1),L(null),G([]),$([])},uploadFile:U.uploadFile})]})};export{v as default};
1
+ import{jsxs as e,jsx as o}from"react/jsx-runtime";import{useRef as t,useState as n,useEffect as r}from"react";import{useNavigate as s,useSearchParams as i}from"react-router-dom";import"../components/Markdown.js";import"../components/Textarea.js";import"../components/Button.js";import"../components/CompoundButton.js";import"../components/Spinner.js";import a from"../components/TextHeader.js";import l from"../components/TypingIndicator.js";import m from"../components/Prompt.js";import p from"../components/ChatInput.js";import{CombinedPlaceholderDialog as c}from"../components/CombinedPlaceholderDialog.js";import d from"../components/ChatMessage.js";import"../components/Header.js";import h from"../hooks/useStateWithLocalStorage.js";import{CodicentService as u}from"../services/codicent.js";import"../utils/MessageContent.js";import"../node_modules/tinycolor2/esm/tinycolor.js";import"../_virtual/index.js";import{getConfigValue as g}from"../config/index.js";import"../utils/cacheManager.js";import"../lib/wavtools/lib/wav_packer.js";import"../lib/wavtools/lib/analysis/audio_analysis.js";import"../lib/wavtools/lib/wav_stream_player.js";import"../lib/wavtools/lib/wav_recorder.js";import"./AppFrame.js";import"./Compose.js";import"./Snap.js";import"./Search.js";import"./Login.js";import"./CrmPage.js";import"./CrmPagePersistent.js";import"./ImageView.js";import"./FormInvite.js";import"./FormAccept.js";import"./Sales.js";import"./Purchase.js";import{useChat as j}from"../hooks/useChat.js";import f from"../hooks/useLocalization.js";import{useTemplateVariables as b}from"../hooks/useTemplateVariables.js";import"../hooks/useAppStyles.js";import"../components/FileThumbnail.js";import"../components/MessageInput.js";import"../components/UploadFile.js";import"../components/SnapFooter.js";import"../components/Profile.js";import"../components/MessageItem.js";import"../components/Content.js";import"../components/AiInput.js";import"../components/SearchBox.js";import"../components/HtmlView.js";import"../components/Footer.js";import{Page as x}from"../components/Page.js";import{makeStyles as C}from"../node_modules/@griffel/react/makeStyles.esm.js";import{CalendarWeekStart24Regular as w}from"../node_modules/@fluentui/react-icons/lib/sizedIcons/chunk-3.js";const y=C({chatContainer:{width:"100%",flex:1,maxWidth:"800px",height:"100%",display:"flex",flexDirection:"column",margin:"0 auto",overflow:"hidden","@media (max-width: 768px)":{maxWidth:"100%",borderRadius:"0"},"@media (max-width: 480px)":{},backgroundPosition:"calc(50%) center",backgroundRepeat:"no-repeat",backgroundSize:"contain",touchAction:"pan-y"},chatMessages:{display:"flex",flexDirection:"column",flex:1,padding:"16px",overflowY:"auto",touchAction:"pan-y"}}),v=({state:C,hideFooter:v})=>{const S=y(),{t:k,tAsync:F,getLanguageInfo:I}=f(),P=s(),{messages:T,isBotTyping:_,handleSend:A,newChat:M,openChat:H}=j(C.stateMachine),V=t(null),[N,B]=i(),[L,R]=n(null),[O,W]=n(!1),[E,U]=n(!1),{service:z,currentStateName:D}=C,[G,Y]=n([]),[$,K]=n([]),[q,J]=n(!1),[Q,X]=n(),[Z,ee]=h(C.context.selectedApp+"_prompts",[]),[oe,te]=n(!1),{extractTemplateVariables:ne,replaceTemplateVariables:re,extractFilePlaceholders:se,replaceFilePlaceholders:ie,handleSelectFiles:ae}=b(z.uploadFile),le=t(!1),me=t(null);r((()=>{const e=window.history.length>1,o=Boolean(document.referrer&&document.referrer!==window.location.href);U(e||o)}),[]);r((()=>{g("SHOW_CHAT_PROMPTS")&&0===T.length&&C.service.getAppPrompts().then(ee).catch((e=>{console.error("Failed to get prompts",e)}))}),[C.service,ee,T.length]),r((()=>{setTimeout((()=>{V.current?.scrollIntoView({behavior:"smooth"})}),10)}),[T]),r((()=>{if(le.current)return;le.current=!0;const e=N.get("text"),o=N.get("id"),t=N.get("fid");if(e){(async()=>{const o=se(e),t=ne(e);M(),o.length>0||t.length>0?(K(o),Y(t),J(!0),R(k(e))):R(k(e))})()}else o?H(o):t&&X(t)}),[]),r((()=>{Q&&"anonymous"===D&&me.current!==Q&&(me.current=Q,z.getFormById(Q).then((async e=>{M();const o=I().language,t=e.prompt.replace("{{fid}}",Q).replace("{{fname}}",e.name).replace("{{language}}",o);te(!0);const n=await F(t);te(!1),R(n),U(!1)})))}),[Q,M,z,R,D,F,I,te]),r((()=>{if(L&&!q){let e=L;$.length>0&&(e=ie(e,$)),G.length>0&&(e=re(e,G)),A(e),B({},{replace:!0}),R(null),Y([]),K([])}}),[L,q,A,B,ie,re,$,G]);const pe=e=>{const o=N.get("append");o&&(e+="\n---\n"+o),A(e)};return e(x,{hideHeader:!0,hideFooter:!!Q||v,children:[e("div",{id:"chat-container",className:S.chatContainer,style:{backgroundImage:`url(${g("CHAT_BACKGROUND_IMAGE_URL")})`},children:[E&&o("button",{onClick:()=>{E&&P(-1)},"aria-label":k("Tillbaka"),style:{position:"absolute",top:12,left:12,zIndex:100,background:"var(--colorNeutralBackground1)",border:"none",cursor:"pointer",padding:8,borderRadius:8,boxShadow:"0 2px 8px rgba(0,0,0,0.08)",display:"flex",alignItems:"center",justifyContent:"center"},children:o(w,{style:{transform:"rotate(180deg)"}})}),o("div",{style:{marginLeft:E?40:0},children:o(a,{title:k(g("APP_CHAT_TITLE")||"Chatt"),onNewChat:Q?void 0:()=>W(!0)})}),e("div",{className:S.chatMessages,children:[g("SHOW_CHAT_PROMPTS")&&Z.length>0&&0===T.length&&o(d,{sender:"bot",content:k(g("CHAT_WELCOME")||"Välkommen!"),children:Z.map(((t,n)=>e("div",{children:["> ",o("a",{href:"./#/chat",onClick:()=>{pe(t.title+"\n---\n"+t.prompt)},children:k(t.title)}),o("br",{})]},`prompt-${n}`)))}),T.map(((t,n)=>e("div",{children:[(_&&n===T.length-1||!_&&n===T.length-2)&&o("div",{ref:V}),o(d,{sender:t.sender,content:t.content,isNew:t.isNew,showSuggestions:n===T.length-1,onSuggestionClicked:A},t.id)]},t.id))),oe&&o(d,{sender:"user",content:k("Översätter...")}),_&&o(l,{})]}),o(p,{codicent:C.context.selectedApp,disableSend:_,onSend:pe,onSelectFiles:ae,getImageUrl:u.getImageUrl,getFileInfo:e=>z.getFileInfo(e)})]}),o(m,{open:O,title:k("Vill du rensa chatten?"),content:k("En ny startas, men chatten kommer först sparas."),onYes:()=>{W(!1),M()},onNo:()=>W(!1)}),o(c,{open:q,templateVariables:G,filePlaceholders:$,onTemplateVariablesChange:Y,onFilePlaceholdersChange:K,onConfirm:()=>J(!1),onCancel:()=>{J(!1),R(null),Y([]),K([])},uploadFile:z.uploadFile})]})};export{v as default};
package/dist/index.d.ts CHANGED
@@ -662,6 +662,7 @@ interface RealtimeVoice {
662
662
  items: ItemType[];
663
663
  realtimeEvents: RealtimeEvent[];
664
664
  isConnected: boolean;
665
+ isSessionReady: boolean;
665
666
  canPushToTalk: boolean;
666
667
  isRecording: boolean;
667
668
  clientCanvasRef: React.RefObject<HTMLCanvasElement>;
@@ -1082,6 +1083,7 @@ declare const AppFrame: ({ src, showFooter, title, }: {
1082
1083
 
1083
1084
  declare const Chat: React__default.FC<{
1084
1085
  state: CodicentAppState;
1086
+ hideFooter?: boolean;
1085
1087
  }>;
1086
1088
 
1087
1089
  declare const Compose: React__default.FC<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codicent-app-sdk",
3
- "version": "0.3.102",
3
+ "version": "0.3.104",
4
4
  "description": "SDK for building AI-powered applications with Codicent",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/index.js",