UNPKG

@embedapi/react

Version:

🚀 Build stunning AI chat interfaces in minutes! Production-ready React components with real-time streaming, Material-UI design, and zero configuration required. Transform your app with powerful, customizable AI chat features.

3 lines (2 loc) • 7 kB
import e,{useCallback as t,useState as r,useRef as a,useEffect as n}from"react";import s from"@embedapi/core";import{Container as o,Paper as l,Box as i,Grid as c,TextField as m,Tooltip as g,IconButton as u,Typography as f,Avatar as d,CircularProgress as p}from"@mui/material";import{createTheme as h,ThemeProvider as y}from"@mui/material/styles";import E from"@mui/icons-material/Send";import b from"@mui/icons-material/SmartToy";import x from"@mui/icons-material/Person";import w from"@mui/icons-material/RestartAlt";import C from"react-markdown";AbortSignal.timeout||(AbortSignal.timeout=function(e){const t=new AbortController;return setTimeout((()=>t.abort()),e),t.signal});const v=new class{constructor(){this.buffer=""}parse(e){this.buffer+=e;try{const e=JSON.parse(this.buffer);return this.buffer="",e}catch(e){return null}}},k="embedapi_chat_",S=10;function I(e){let{agentId:o,service:l="openai",model:i="gpt-4o",initialMessages:c=[],onError:m,enableCache:g=!0,messageLimit:u=S}=e;const f=t((()=>{if(!g)return c;try{const e=sessionStorage.getItem(`${k}${o}`);return(e?JSON.parse(e):c).slice(-u)}catch(e){return console.warn("Failed to load cached messages:",e),c}}),[o,c,g,u]),[d,p]=r(f),h=a(""),[y,E]=r(!1),[b,x]=r(null);n((()=>{if(g&&o&&d.length>0)try{const e=d.slice(-u);sessionStorage.setItem(`${k}${o}`,JSON.stringify(e))}catch(e){console.warn("Failed to cache messages:",e)}}),[d,o,g,u]),n((()=>{if(!o){const e={role:"system",content:"Error: agentId is required."};p((t=>[...t,e]))}}),[o]);const w=o?new s(o,{isAgent:!0}):null,C=t((()=>{if(p(c),x(null),E(!1),g&&o)try{sessionStorage.removeItem(`${k}${o}`)}catch(e){console.warn("Failed to clear cached messages:",e)}}),[c,o,g]),I=t((e=>{Array.isArray(e)?p(e.slice(-u)):p((t=>("function"==typeof e?e(t):e).slice(-u)))}),[u]),M=t((async e=>{if(!o)return;const t={role:"user",content:e};I((e=>[...e,t])),E(!0),x(null);try{const e=await w.generate({service:l,model:i,messages:[...d,t]});p((t=>[...t,{role:"assistant",content:e.content}]))}catch(e){x(e),m?.(e)}finally{E(!1)}}),[d,w,l,i,m,o,I]),A=t((async e=>{if(!o)return;E(!0);const t={role:"user",content:e};I((e=>[...e,t])),x(null);const r=new AbortController,a=r.signal;try{const e=await w.stream({service:l,model:i,messages:[...d,t],signal:a}),r=new TextDecoder("utf-8");h.current={role:"assistant",content:""};for await(const t of e.body){if(a.aborted)break;const e=r.decode(t).split(/(\n){2}/).map((e=>e.replace(/(\n)?^data:\s*/,"").trim())).filter((e=>""!==e&&"[DONE]"!==e&&void 0!==e)).map((e=>v.parse(e)));for(const t of e)if("chunk"===t?.type)h.current.content+=t.content;else if("done"===t?.type){h.current.cost=t.cost,h.current.tokenUsage=t.tokenUsage,p((e=>[...e,{...h.current}])),E(!1),h.current={role:"assistant",content:""};break}}e.body?.cancel&&e.body.cancel()}catch(e){m?.(e),a.aborted?console.log("Stream aborted."):(x(e),m?.(e))}finally{E(!1)}return()=>r.abort()}),[d,w,l,i,m,o,I]);return{messages:d,isLoading:y,error:b,currentMessage:h.current,sendMessage:M,streamMessage:A,reset:C,clearCache:t((()=>{if(o)try{sessionStorage.removeItem(`${k}${o}`)}catch(e){console.warn("Failed to clear cached messages:",e)}}),[o]),messageCount:d.length}}const M=t=>{let{agentId:s,initialMessages:v=[],className:k="",placeholder:S="Type a message...",onError:M,style:A={},theme:$="light",containerWidth:N="100%",maxHeight:R="600px",customStyles:T={}}=t;const[B,F]=r(""),O=a(null),D=a(null),{messages:J,isLoading:L,error:W,currentMessage:j,streamMessage:z,reset:P}=I({agentId:s,initialMessages:v,onError:M}),U=h({palette:{mode:$},spacing:8}),_={container:{width:N,maxWidth:"100%",...T.container},messageContainer:{height:R,overflow:"auto",padding:U.spacing(2.5),backgroundColor:"light"===$?"#f5f5f5":"#1a1a1a",scrollBehavior:"smooth",...T.messageContainer},userMessage:{backgroundColor:"light"===$?"#e3f2fd":"#2c387e",color:"light"===$?"#000":"#fff",borderRadius:"10px",padding:U.spacing(1.25,1.875),marginBottom:U.spacing(1.25),boxShadow:"0 1px 2px rgba(0,0,0,0.1)",...T.userMessage},assistantMessage:{backgroundColor:"light"===$?"#fff":"#333",color:"light"===$?"#000":"#fff",borderRadius:"10px",padding:U.spacing(1.25,1.875),marginBottom:U.spacing(1.25),boxShadow:"0 1px 2px rgba(0,0,0,0.1)",...T.assistantMessage},inputContainer:{padding:U.spacing(2.5),backgroundColor:"light"===$?"#fff":"#262626",borderTop:"1px solid "+("light"===$?"#e0e0e0":"#404040"),...T.inputContainer},avatar:{backgroundColor:"light"===$?"#1976d2":"#90caf9",color:"light"===$?"#fff":"#000"},...A};n((()=>{O.current?.scrollIntoView({behavior:"smooth"}),D.current?.focus()}),[J]),n((()=>{D.current?.focus()}),[]);const q=(t,r)=>e.createElement(c,{item:!0,key:r,container:!0,justifyContent:"user"===t.role?"flex-end":"flex-start"},e.createElement(c,{item:!0,xs:12,sm:8,md:6},e.createElement(i,{display:"flex",alignItems:"flex-start",gap:1,style:"user"===t.role?_.userMessage:_.assistantMessage},e.createElement(d,{style:_.avatar},"user"===t.role?e.createElement(x,null):e.createElement(b,null)),e.createElement(f,{style:{wordBreak:"break-word",flex:1}},e.createElement(C,null,t?.content)))));return e.createElement(y,{theme:U},e.createElement(o,{style:_.container,className:k},e.createElement(l,{elevation:3},e.createElement(i,{style:_.messageContainer},e.createElement(c,{container:!0,direction:"column",spacing:2},(()=>{const t=J;return e.createElement(e.Fragment,null,t.map(q),L&&j&&e.createElement(c,{item:!0,container:!0,justifyContent:"flex-start"},e.createElement(c,{item:!0,xs:12,sm:8,md:6},e.createElement(i,{display:"flex",alignItems:"flex-start",gap:1,style:_.assistantMessage},e.createElement(d,{style:_.avatar},e.createElement(b,null)),e.createElement(i,{style:{flex:1}},e.createElement(f,{style:{wordBreak:"break-word"}},j.content),e.createElement(i,{display:"flex",alignItems:"center",mt:1},e.createElement(p,{size:16,style:{marginRight:8}}),e.createElement(f,{variant:"caption",color:"textSecondary"},"Is Typing...")))))),e.createElement("div",{ref:O}))})())),e.createElement(i,{component:"form",onSubmit:async e=>{if(e.preventDefault(),!B.trim()||L)return;const t=B.trim();F(""),await z(t),D.current?.focus()},style:_.inputContainer},e.createElement(c,{container:!0,spacing:2,alignItems:"center"},e.createElement(c,{item:!0,xs:!0},e.createElement(m,{fullWidth:!0,value:B,onChange:e=>F(e.target.value),placeholder:S,disabled:L,variant:"outlined",size:"small",inputRef:D,InputProps:{style:{backgroundColor:"light"===$?"#fff":"#333",color:"light"===$?"#000":"#fff"}}})),e.createElement(c,{item:!0},e.createElement(g,{title:"Reset conversation"},e.createElement(u,{onClick:()=>{P(),F(""),D.current?.focus()},color:"primary",disabled:L||0===J.length},e.createElement(w,null)))),e.createElement(c,{item:!0},e.createElement(g,{title:"Send message"},e.createElement(u,{type:"submit",disabled:L||!B.trim(),color:"primary"},e.createElement(E,null)))))),W&&e.createElement(i,{p:2},e.createElement(f,{color:"error"},W.message)))))};export{M as AgentChat,I as useChat}; //# sourceMappingURL=index.esm.js.map