@inkeep/agents-ui 0.17.5 → 0.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/primitives/components/embedded-chat/chat-error-helpers.cjs +2 -2
- package/dist/primitives/components/embedded-chat/chat-error-helpers.d.ts +37 -0
- package/dist/primitives/components/embedded-chat/chat-error-helpers.js +36 -17
- package/dist/primitives/components/embedded-chat/use-inkeep-chat.cjs +2 -2
- package/dist/primitives/components/embedded-chat/use-inkeep-chat.js +274 -254
- package/dist/primitives/hooks/use-initial-conversation.cjs +1 -1
- package/dist/primitives/hooks/use-initial-conversation.d.ts +7 -1
- package/dist/primitives/hooks/use-initial-conversation.js +31 -20
- package/dist/primitives/providers/base-events-provider.cjs +1 -1
- package/dist/primitives/providers/base-events-provider.js +1 -1
- package/dist/primitives/providers/chat-base-events-provider.cjs +1 -1
- package/dist/primitives/providers/chat-base-events-provider.js +1 -1
- package/dist/types/config/ai.d.ts +8 -0
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("../../hooks/use-inkeep-api-client.cjs");function n(t){const e=Number(t.code)||Number(t.statusCode);if(e&&!Number.isNaN(e))return e;try{const r=Number(JSON.parse(t.message??"").status);return r&&!Number.isNaN(r)?r:null}catch{return null}}function E(t){const e=n(t),r=o.parseAuthError(e??0,{detail:t.message??""});return r!==null?r:e===401?"session":e===403?"captcha":null}function a(t){const e=n(t);return e===400||e===413||e===429}function i(t){if(n(t)!==null)return!1;const e=(t.message??"").toLowerCase();return e.includes("failed to fetch")||e.includes("load failed")||e.includes("networkerror")||e.includes("network request failed")}const u="Something went wrong sending your message. Check your connection and try again.",c="Your message could not be sent. If you attached large files, try removing one. Otherwise, check your connection and try again.",_=`Hmm..
|
|
2
2
|
|
|
3
|
-
It seems I might be having some issues right now. Please clear the chat and try again.`,
|
|
3
|
+
It seems I might be having some issues right now. Please clear the chat and try again.`,l="Please try again.",R="You're sending requests too quickly. Please wait a moment and try again.",A="Your message or attachments are too large. Remove a file or shorten your message and try again.",S=43e5,s=4/3;function d(t){return new TextEncoder().encode(t.text).length+Math.ceil(t.rawFileBytes*s)+t.encodedPartsBytes}const O=8e3;exports.BASE64_SIZE_FACTOR=s;exports.DEFAULT_ERROR_MESSAGE=_;exports.MAX_REQUEST_BODY_BYTES=S;exports.NETWORK_ERROR_MESSAGE=u;exports.NETWORK_ERROR_WITH_FILES_MESSAGE=c;exports.PAYLOAD_TOO_LARGE_MESSAGE=A;exports.RATE_LIMIT_MESSAGE=R;exports.RECOVERABLE_FALLBACK_MESSAGE=l;exports.RECOVERABLE_NOTIFICATION_DURATION_MS=O;exports.estimateRequestBodyBytes=d;exports.isNetworkError=i;exports.isRecoverableError=a;exports.resolveHttpStatusCode=n;exports.resolveStreamingAuthError=E;
|
|
@@ -18,10 +18,47 @@ export declare function resolveStreamingAuthError(error: Error): 'captcha' | 'se
|
|
|
18
18
|
* Recoverable errors are input-validation failures where the user can fix
|
|
19
19
|
* their input and retry without clearing the conversation.
|
|
20
20
|
*
|
|
21
|
+
* 413 (payload too large) is recoverable: the conversation is intact and the
|
|
22
|
+
* user can remove an attachment or shorten their message and resend.
|
|
23
|
+
*
|
|
21
24
|
* Allowlist approach: unknown/unparseable errors default to non-recoverable (blocking).
|
|
22
25
|
*/
|
|
23
26
|
export declare function isRecoverableError(error: Error): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Detects an opaque transport failure that arrived with no readable HTTP status —
|
|
29
|
+
* the fetch rejected before (or without) a usable response.
|
|
30
|
+
*
|
|
31
|
+
* The most common production cause is a platform-level 413: Vercel rejects any
|
|
32
|
+
* request body over ~4.5MB at the edge (base64-encoded file attachments inflate
|
|
33
|
+
* ~33%, so this is easy to hit), and that platform 413 carries no CORS headers
|
|
34
|
+
* because the app's cors() middleware never runs on it. The browser therefore
|
|
35
|
+
* blocks JS from reading the response and surfaces `TypeError: Failed to fetch`
|
|
36
|
+
* with no status. Genuine offline/DNS failures land here too — in both cases the
|
|
37
|
+
* conversation is intact and the user should get a non-blocking retry prompt.
|
|
38
|
+
*
|
|
39
|
+
* Matched on message signature (not error.name) so we don't swallow genuine
|
|
40
|
+
* programming TypeErrors as recoverable network failures.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isNetworkError(error: Error): boolean;
|
|
43
|
+
export declare const NETWORK_ERROR_MESSAGE = "Something went wrong sending your message. Check your connection and try again.";
|
|
44
|
+
export declare const NETWORK_ERROR_WITH_FILES_MESSAGE = "Your message could not be sent. If you attached large files, try removing one. Otherwise, check your connection and try again.";
|
|
24
45
|
export declare const DEFAULT_ERROR_MESSAGE = "Hmm.. \n\nIt seems I might be having some issues right now. Please clear the chat and try again.";
|
|
25
46
|
export declare const RECOVERABLE_FALLBACK_MESSAGE = "Please try again.";
|
|
26
47
|
export declare const RATE_LIMIT_MESSAGE = "You're sending requests too quickly. Please wait a moment and try again.";
|
|
48
|
+
export declare const PAYLOAD_TOO_LARGE_MESSAGE = "Your message or attachments are too large. Remove a file or shorten your message and try again.";
|
|
49
|
+
export declare const MAX_REQUEST_BODY_BYTES = 4300000;
|
|
50
|
+
export declare const BASE64_SIZE_FACTOR: number;
|
|
51
|
+
/**
|
|
52
|
+
* Estimates the encoded byte size of the outgoing chat request body. Only the latest
|
|
53
|
+
* message is ever sent (the backend tracks history via conversationId), so file
|
|
54
|
+
* attachments in the current turn dominate — conversation history is irrelevant here.
|
|
55
|
+
*/
|
|
56
|
+
export declare function estimateRequestBodyBytes(input: {
|
|
57
|
+
/** Message text. */
|
|
58
|
+
text: string;
|
|
59
|
+
/** Sum of `File.size` for freshly attached files (raw, pre-base64). */
|
|
60
|
+
rawFileBytes: number;
|
|
61
|
+
/** Sum of `.url.length` for already-encoded FileUIParts (data URLs). */
|
|
62
|
+
encodedPartsBytes: number;
|
|
63
|
+
}): number;
|
|
27
64
|
export declare const RECOVERABLE_NOTIFICATION_DURATION_MS = 8000;
|
|
@@ -1,32 +1,51 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { parseAuthError as r } from "../../hooks/use-inkeep-api-client.js";
|
|
3
|
-
function
|
|
3
|
+
function s(t) {
|
|
4
4
|
const e = Number(t.code) || Number(t.statusCode);
|
|
5
5
|
if (e && !Number.isNaN(e)) return e;
|
|
6
6
|
try {
|
|
7
|
-
const
|
|
8
|
-
return
|
|
7
|
+
const n = Number(JSON.parse(t.message ?? "").status);
|
|
8
|
+
return n && !Number.isNaN(n) ? n : null;
|
|
9
9
|
} catch {
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
function
|
|
14
|
-
const e =
|
|
15
|
-
return
|
|
13
|
+
function c(t) {
|
|
14
|
+
const e = s(t), n = r(e ?? 0, { detail: t.message ?? "" });
|
|
15
|
+
return n !== null ? n : e === 401 ? "session" : e === 403 ? "captcha" : null;
|
|
16
|
+
}
|
|
17
|
+
function i(t) {
|
|
18
|
+
const e = s(t);
|
|
19
|
+
return e === 400 || e === 413 || e === 429;
|
|
16
20
|
}
|
|
17
21
|
function u(t) {
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
if (s(t) !== null) return !1;
|
|
23
|
+
const e = (t.message ?? "").toLowerCase();
|
|
24
|
+
return e.includes("failed to fetch") || // Chromium
|
|
25
|
+
e.includes("load failed") || // Safari
|
|
26
|
+
e.includes("networkerror") || // Firefox
|
|
27
|
+
e.includes("network request failed");
|
|
20
28
|
}
|
|
21
|
-
const
|
|
29
|
+
const l = "Something went wrong sending your message. Check your connection and try again.", E = "Your message could not be sent. If you attached large files, try removing one. Otherwise, check your connection and try again.", d = `Hmm..
|
|
22
30
|
|
|
23
|
-
It seems I might be having some issues right now. Please clear the chat and try again.`,
|
|
31
|
+
It seems I might be having some issues right now. Please clear the chat and try again.`, m = "Please try again.", g = "You're sending requests too quickly. Please wait a moment and try again.", R = "Your message or attachments are too large. Remove a file or shorten your message and try again.", _ = 43e5, o = 4 / 3;
|
|
32
|
+
function A(t) {
|
|
33
|
+
return new TextEncoder().encode(t.text).length + Math.ceil(t.rawFileBytes * o) + t.encodedPartsBytes;
|
|
34
|
+
}
|
|
35
|
+
const S = 8e3;
|
|
24
36
|
export {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
o as BASE64_SIZE_FACTOR,
|
|
38
|
+
d as DEFAULT_ERROR_MESSAGE,
|
|
39
|
+
_ as MAX_REQUEST_BODY_BYTES,
|
|
40
|
+
l as NETWORK_ERROR_MESSAGE,
|
|
41
|
+
E as NETWORK_ERROR_WITH_FILES_MESSAGE,
|
|
42
|
+
R as PAYLOAD_TOO_LARGE_MESSAGE,
|
|
43
|
+
g as RATE_LIMIT_MESSAGE,
|
|
44
|
+
m as RECOVERABLE_FALLBACK_MESSAGE,
|
|
45
|
+
S as RECOVERABLE_NOTIFICATION_DURATION_MS,
|
|
46
|
+
A as estimateRequestBodyBytes,
|
|
47
|
+
u as isNetworkError,
|
|
48
|
+
i as isRecoverableError,
|
|
49
|
+
s as resolveHttpStatusCode,
|
|
50
|
+
c as resolveStreamingAuthError
|
|
32
51
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`)??"";t.useEffect(()=>{
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Xe=require("@ai-sdk/react"),Ze=require("./file-upload-input.cjs"),et=require("ai"),t=require("react"),tt=require("../../providers/config-provider.cjs"),st=require("../../providers/chat-auth-provider.cjs"),rt=require("../../hooks/use-media-query.cjs"),nt=require("../../hooks/use-conversation-loader.cjs"),at=require("../../hooks/use-initial-conversation.cjs"),O=require("../../utils/generate-uid.cjs"),ot=require("../../providers/base-events-provider.cjs"),it=require("../../providers/chat-form-provider.cjs"),ct=require("../../providers/widget-provider.cjs"),ut=require("@radix-ui/react-use-controllable-state"),lt=require("../../hooks/use-streaming-events.cjs"),dt=require("../../hooks/use-input-notification.cjs"),s=require("./chat-error-helpers.cjs"),ft=()=>{const{baseSettings:se,aiChatSettings:R}=tt.useInkeepConfig(),[u="",v]=ut.useControllableState({prop:R.conversationIdOverride,defaultProp:R.conversationIdOverride??""}),{logEvent:m}=ot.useBaseEvents(),{setConversationId:we,emitToParent:k}=lt.useStreamingEvents(),re=t.useRef(u);t.useEffect(()=>{const e=re.current;re.current=u,e!==u&&m({eventName:"chat_conversation_changed",properties:{conversationId:u,previousConversationId:e}})},[u,m]);const[_,C]=t.useState(""),Oe=e=>C(e.target.value),{shouldBypassCaptcha:ke,filters:ne,userProperties:B,analyticsProperties:D}=se,{authToken:ae,isAuthenticated:S,isAuthConfigured:oe,refreshAuthToken:ie,sessionToken:ce,refreshSession:ue,getCaptchaHeader:F,invalidateCaptcha:f,effectiveAuthToken:le,applicableRefreshSession:Fe}=st.useChatAuth(),{onInputMessageChange:Le,filters:de,baseUrl:fe,agentUrl:Ne,context:pe,headers:ge,appId:L,apiKey:U,files:T}=R,he=t.useRef(F);he.current=F;const me=Ne||`${fe}/run/api/chat`,{loadConversation:Ee}=nt.useConversationLoader({baseUrl:fe,appId:L,authToken:le,getCaptchaHeader:F,invalidateCaptcha:f,refreshSession:Fe}),[Pe,Re]=t.useState(!1),G=t.useRef(null);G.current=ce;const $=t.useRef(null);$.current=ae;const K=t.useRef(void 0);K.current=B&&Object.keys(B).length>0?B:void 0;const H=t.useRef(void 0);H.current=D&&Object.keys(D).length>0?D:void 0;const y=t.useRef(0),N=t.useRef(null),W=t.useRef(null),E=t.useRef(void 0),xe=T?.map(e=>`${e.filename??""}:${e.mediaType}:${e.url.length}:${e.url.slice(0,64)}:${e.url.slice(-32)}`).join(`
|
|
2
|
+
`)??"";t.useEffect(()=>{E.current=T?.length?T:void 0},[xe]);const ve=t.useRef(ge);ve.current=ge;const z=t.useRef(void 0);z.current=ne||de?JSON.stringify({...ne,...de}):void 0;const qe=e=>{if(s.isNetworkError(e))return N.current?.files?.length?s.NETWORK_ERROR_WITH_FILES_MESSAGE:s.NETWORK_ERROR_MESSAGE;switch(s.resolveHttpStatusCode(e)){case 400:try{const r=JSON.parse(e.message??"");return r.detail??r.error?.message??s.RECOVERABLE_FALLBACK_MESSAGE}catch{return e.message?.trim()||s.RECOVERABLE_FALLBACK_MESSAGE}case 401:return oe?"Authentication failed. Please try again.":s.DEFAULT_ERROR_MESSAGE;case 403:return`There seems to be a configuration error. Please contact ${se.organizationDisplayName??"Administrator"}`;case 413:return s.PAYLOAD_TOO_LARGE_MESSAGE;case 429:return s.RATE_LIMIT_MESSAGE;default:return s.DEFAULT_ERROR_MESSAGE}},[A,V]=t.useState([]),Be=t.useMemo(()=>new et.DefaultChatTransport({api:me,headers:()=>{const e=U??$.current??G.current;return{"x-inkeep-client-timezone":Intl.DateTimeFormat().resolvedOptions().timeZone,"x-inkeep-client-timestamp":new Date().toISOString(),"x-inkeep-invocation-type":"chat_widget",...L?{"x-inkeep-app-id":L}:{},...e?{Authorization:`Bearer ${e}`}:{},...z.current?{"inkeep-filters":z.current}:{},...ve.current}},prepareSendMessagesRequest:async e=>{const l=await he.current(),r=e.messages[e.messages.length-1];return r||console.warn("[useInkeepChat] prepareSendMessagesRequest called with empty messages array"),{body:{...e.body,id:e.id,messages:r?[r]:[],trigger:e.trigger,messageId:e.messageId,...K.current?{userProperties:K.current}:{},...H.current?{properties:H.current}:{}},headers:{...e.headers,...l}}},body:{requestContext:pe}}),[me,pe,L,U]),{messages:M,sendMessage:Y,addToolApprovalResponse:j,status:Se,setMessages:h,stop:P,error:x}=Xe.useChat({transport:Be,onData(e){k(e.type,e.data)},async onFinish({message:e}){k("completion",{conversationId:u}),await m({eventName:"assistant_message_received",properties:{conversationId:u,messageId:e.id}}),m({eventName:"assistant_answer_displayed",properties:{conversationId:u,messageId:e.id}})},onError(e){console.error("onError",{code:e.code,message:e.message});const l=ke||U?null:s.resolveStreamingAuthError(e);if(l!==null&&y.current<1){y.current++;const i=W.current,a=N.current;(async()=>{if(l==="session"&&oe){const o=await ie();if(!o)throw new Error("Auth token refresh failed");$.current=o}else if(l==="session"){const o=await ue();o&&(G.current=o)}else f();if(i){j(i);return}a&&(h(o=>{let c=[...o];return c.at(-1)?.role==="assistant"&&(c=c.slice(0,-1)),c.at(-1)?.role==="user"&&(c=c.slice(0,-1)),c}),Y({id:a.messageId,parts:[...a.content.trim()?[{type:"text",text:a.content}]:[],...a.files??[]]},{body:a.body}))})().catch(()=>{y.current=0,f(),h(o=>{const c=[...o],g=c[c.length-1];if(!g)return c;const d=s.DEFAULT_ERROR_MESSAGE;return g.role==="user"?c.push({id:O.generateUid(16),role:"assistant",parts:[{type:"text",text:d}]}):g.parts=[{type:"text",text:d}],c})});return}y.current=0,l!==null&&f();const r=s.isRecoverableError(e)||s.isNetworkError(e),n=qe(e);if(m({eventName:"chat_error",properties:{conversationId:u,messageId:M.at(-1)?.id,error:e.message}}),r){const i=n===s.PAYLOAD_TOO_LARGE_MESSAGE?"Message too large":n===s.RATE_LIMIT_MESSAGE?"Rate limit reached":n===s.NETWORK_ERROR_MESSAGE||n===s.NETWORK_ERROR_WITH_FILES_MESSAGE?"Connection error":"Request failed";q({title:i,message:n},s.RECOVERABLE_NOTIFICATION_DURATION_MS),h(p=>{let o=[...p];return o.at(-1)?.role==="assistant"&&(o=o.slice(0,-1)),o.at(-1)?.role==="user"&&(o=o.slice(0,-1)),o});const a=N.current?.content;a&&C(a),Z.current=e;return}h(i=>{const a=[...i],p=a[a.length-1];return p&&(p.role==="user"?a.push({id:O.generateUid(16),role:"assistant",parts:[{type:"text",text:n}]}):p.parts=[{type:"text",text:n}]),a})}}),ye=t.useRef(S);t.useEffect(()=>{const e=ye.current;ye.current=S,e!==S&&(P(),b(null),h([]),v(""),C(""),V([]),f())},[S,P,h,v,f]);const Ae=Se==="submitted",Q=Se==="streaming",De=t.useMemo(()=>{const e=i=>{if(!i||typeof i!="object")return!1;const a=i;return typeof a.type=="string"&&a.type.startsWith("tool-")},r=[...M??[]].reverse().find(i=>i.role==="assistant");if(!r)return!1;const n=r.parts?.at(-1);return!(!e(n)||n.state!=="output-available"||!n.approval?.id||Q)},[M,Q]),[Ue,J]=t.useState(!1),Ie=Q||De&&!Ue,_e=Ae||Ie,Ge=M.length===0,X=!_.trim()&&A.length===0||_e,$e=rt.useMediaQuery("(max-width: 768px)"),[Ke,b]=t.useState(null),Z=t.useRef(null);t.useEffect(()=>{if(x){if(Z.current===x){Z.current=null;return}b(x)}},[x]);const He=()=>b(null),{inputNotification:We,showInputNotification:q,clearInputNotification:ze}=dt.useInputNotification(),Ce=t.useRef(null);t.useEffect(()=>{Le?.(_)},[_]);const Ve=e=>{e.key==="Enter"&&!e.shiftKey&&!X&&!e.nativeEvent.isComposing&&(e.preventDefault(),ee())},ee=async(e=_)=>{if(X&&(!e||e.trim().length===0)&&A.length===0||!e.trim()&&!A.length&&!E.current?.length)return;if(s.estimateRequestBodyBytes({text:e,rawFileBytes:A.reduce((g,d)=>g+d.size,0),encodedPartsBytes:(E.current??[]).reduce((g,d)=>g+d.url.length,0)})>s.MAX_REQUEST_BODY_BYTES){q({title:"Message too large",message:s.PAYLOAD_TOO_LARGE_MESSAGE},s.RECOVERABLE_NOTIFICATION_DURATION_MS);return}const r=A;V([]),C(""),y.current=0,W.current=null,J(!1);let n=u;n||(n=`conv_${O.generateUid(16)}`,v(n));const i=O.generateUid(21);we(n),await m({eventName:"user_message_submitted",properties:{conversationId:n,messageId:i}});const a=E.current;E.current=void 0;let p=[];if(r.length>0)try{p=await Promise.all(r.map(g=>{const d=Ze.normalizeFileType(g);return new Promise((Je,be)=>{const w=new FileReader;w.onload=()=>{if(typeof w.result!="string"){be(new Error(`Failed to read file "${d.name}"`));return}Je({type:"file",url:w.result,mediaType:d.type,filename:d.name})},w.onerror=()=>be(new Error(`Failed to read file "${d.name}"`)),w.readAsDataURL(d)})}))}catch{q({title:"Failed to attach files",message:"Could not read one or more files. Please try again."});return}const o=p.length||a?.length?[...p,...a??[]]:void 0,c=[...e.trim()?[{type:"text",text:e}]:[],...o??[]];N.current={content:e,messageId:i,body:{conversationId:n},files:o},Y({id:i,parts:c},{body:{conversationId:n}})},Ye=t.useCallback(e=>{y.current=0,W.current=e,J(!1),j(e)},[j]),te=t.useCallback(()=>{J(!0),P().then(()=>{k("aborted",{conversationId:u})})},[P,u,k]),Te=()=>{He(),h([]),v(""),f(),E.current=T?.length?T:void 0,m({eventName:"chat_clear_button_clicked",properties:{conversationId:u}})},I=t.useCallback((e,l)=>{b(null),h(l),v(e),f(),E.current=void 0},[h,v,f]),Me=t.useCallback(async(e,l)=>{te(),I(e,[]),Re(!0);try{const r=await Ee(e,l);if(r===null)return!1;const i=r[r.length-1]?.role==="user"?[...r,{id:O.generateUid(16),role:"assistant",parts:[{type:"text",text:"This session was interrupted. Please check back in a few minutes or start a new conversation."}]}]:r;return I(e,i),!0}finally{l?.aborted||Re(!1)}},[I,Ee,te]);at.useInitialConversation({conversationId:R.conversationId,fetchedConversation:R.fetchedConversation,effectiveAuthToken:le,restoreSession:I,loadAndRestoreSession:Me,onLoadFailed:()=>I("",[])});const{openForm:je}=it.useChatForm(),Qe=ct.useWidget();return t.useImperativeHandle(R.chatFunctionsRef,()=>({submitMessage:ee,updateInputMessage(e){C(e)},clearChat:Te,openForm:e=>{Qe?.setView("chat"),je(e,void 0)},focusInput:()=>{Ce.current?.focus()}})),{messages:M,sendMessage:Y,addToolApprovalResponse:Ye,isLoading:Ae,isStreaming:Ie,isBusy:_e,error:Ke,setError:b,isSubmitDisabled:X,input:_,handleInputChange:Oe,handleInputKeyDown:Ve,handleSubmit:ee,stop:te,clear:Te,inputRef:Ce,isMobile:$e,files:A,setFiles:V,isNewChat:Ge,conversationId:u,restoreSession:I,loadAndRestoreSession:Me,isSessionLoading:Pe,authToken:S?ae:ce,refreshSession:S?ie:ue,getCaptchaHeader:F,invalidateCaptcha:f,inputNotification:We,showInputNotification:q,clearInputNotification:ze}};exports.useInkeepChat=ft;
|
|
@@ -1,113 +1,117 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useChat as
|
|
3
|
-
import { normalizeFileType as
|
|
4
|
-
import { DefaultChatTransport as
|
|
5
|
-
import { useRef as i, useEffect as
|
|
6
|
-
import { useInkeepConfig as
|
|
7
|
-
import { useChatAuth as
|
|
8
|
-
import { useMediaQuery as
|
|
9
|
-
import { useConversationLoader as
|
|
10
|
-
import { useInitialConversation as
|
|
2
|
+
import { useChat as lt } from "@ai-sdk/react";
|
|
3
|
+
import { normalizeFileType as ut } from "./file-upload-input.js";
|
|
4
|
+
import { DefaultChatTransport as dt } from "ai";
|
|
5
|
+
import { useRef as i, useEffect as _, useState as k, useMemo as Oe, useCallback as $, useImperativeHandle as pt } from "react";
|
|
6
|
+
import { useInkeepConfig as ft } from "../../providers/config-provider.js";
|
|
7
|
+
import { useChatAuth as mt } from "../../providers/chat-auth-provider.js";
|
|
8
|
+
import { useMediaQuery as gt } from "../../hooks/use-media-query.js";
|
|
9
|
+
import { useConversationLoader as ht } from "../../hooks/use-conversation-loader.js";
|
|
10
|
+
import { useInitialConversation as vt } from "../../hooks/use-initial-conversation.js";
|
|
11
11
|
import { generateUid as F } from "../../utils/generate-uid.js";
|
|
12
|
-
import { useBaseEvents as
|
|
13
|
-
import { useChatForm as
|
|
14
|
-
import { useWidget as
|
|
15
|
-
import { useControllableState as
|
|
16
|
-
import { useStreamingEvents as
|
|
17
|
-
import { useInputNotification as
|
|
18
|
-
import { resolveStreamingAuthError as
|
|
19
|
-
const
|
|
20
|
-
const { baseSettings:
|
|
21
|
-
prop:
|
|
22
|
-
defaultProp:
|
|
23
|
-
}), { logEvent:
|
|
24
|
-
|
|
25
|
-
const e =
|
|
26
|
-
|
|
12
|
+
import { useBaseEvents as yt } from "../../providers/base-events-provider.js";
|
|
13
|
+
import { useChatForm as Rt } from "../../providers/chat-form-provider.js";
|
|
14
|
+
import { useWidget as Et } from "../../providers/widget-provider.js";
|
|
15
|
+
import { useControllableState as It } from "@radix-ui/react-use-controllable-state";
|
|
16
|
+
import { useStreamingEvents as St } from "../../hooks/use-streaming-events.js";
|
|
17
|
+
import { useInputNotification as Ct } from "../../hooks/use-input-notification.js";
|
|
18
|
+
import { resolveStreamingAuthError as At, DEFAULT_ERROR_MESSAGE as ne, isRecoverableError as Tt, isNetworkError as Ne, RECOVERABLE_NOTIFICATION_DURATION_MS as Pe, PAYLOAD_TOO_LARGE_MESSAGE as oe, RATE_LIMIT_MESSAGE as Be, NETWORK_ERROR_MESSAGE as De, NETWORK_ERROR_WITH_FILES_MESSAGE as Le, resolveHttpStatusCode as wt, RECOVERABLE_FALLBACK_MESSAGE as $e, estimateRequestBodyBytes as bt, MAX_REQUEST_BODY_BYTES as Mt } from "./chat-error-helpers.js";
|
|
19
|
+
const Wt = () => {
|
|
20
|
+
const { baseSettings: ae, aiChatSettings: v } = ft(), [c = "", y] = It({
|
|
21
|
+
prop: v.conversationIdOverride,
|
|
22
|
+
defaultProp: v.conversationIdOverride ?? ""
|
|
23
|
+
}), { logEvent: g } = yt(), { setConversationId: Ue, emitToParent: x } = St(), ie = i(c);
|
|
24
|
+
_(() => {
|
|
25
|
+
const e = ie.current;
|
|
26
|
+
ie.current = c, e !== c && g({
|
|
27
27
|
eventName: "chat_conversation_changed",
|
|
28
28
|
properties: {
|
|
29
29
|
conversationId: c,
|
|
30
30
|
previousConversationId: e
|
|
31
31
|
}
|
|
32
32
|
});
|
|
33
|
-
}, [c,
|
|
34
|
-
const [C,
|
|
35
|
-
shouldBypassCaptcha:
|
|
36
|
-
filters:
|
|
37
|
-
userProperties:
|
|
38
|
-
analyticsProperties:
|
|
39
|
-
} =
|
|
40
|
-
authToken:
|
|
41
|
-
isAuthenticated:
|
|
42
|
-
isAuthConfigured:
|
|
43
|
-
refreshAuthToken:
|
|
33
|
+
}, [c, g]);
|
|
34
|
+
const [C, A] = k(""), Ge = (e) => A(e.target.value), {
|
|
35
|
+
shouldBypassCaptcha: Ke,
|
|
36
|
+
filters: ce,
|
|
37
|
+
userProperties: U,
|
|
38
|
+
analyticsProperties: G
|
|
39
|
+
} = ae, {
|
|
40
|
+
authToken: le,
|
|
41
|
+
isAuthenticated: R,
|
|
42
|
+
isAuthConfigured: ue,
|
|
43
|
+
refreshAuthToken: de,
|
|
44
44
|
sessionToken: pe,
|
|
45
|
-
refreshSession:
|
|
46
|
-
getCaptchaHeader:
|
|
47
|
-
invalidateCaptcha:
|
|
48
|
-
effectiveAuthToken:
|
|
49
|
-
applicableRefreshSession:
|
|
50
|
-
} =
|
|
51
|
-
onInputMessageChange:
|
|
52
|
-
filters:
|
|
53
|
-
baseUrl:
|
|
54
|
-
agentUrl:
|
|
55
|
-
context:
|
|
56
|
-
headers:
|
|
45
|
+
refreshSession: fe,
|
|
46
|
+
getCaptchaHeader: O,
|
|
47
|
+
invalidateCaptcha: d,
|
|
48
|
+
effectiveAuthToken: me,
|
|
49
|
+
applicableRefreshSession: ze
|
|
50
|
+
} = mt(), {
|
|
51
|
+
onInputMessageChange: He,
|
|
52
|
+
filters: ge,
|
|
53
|
+
baseUrl: he,
|
|
54
|
+
agentUrl: qe,
|
|
55
|
+
context: ve,
|
|
56
|
+
headers: ye,
|
|
57
57
|
appId: N,
|
|
58
|
-
apiKey:
|
|
59
|
-
files:
|
|
60
|
-
} =
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
baseUrl:
|
|
58
|
+
apiKey: K,
|
|
59
|
+
files: T
|
|
60
|
+
} = v, Re = i(O);
|
|
61
|
+
Re.current = O;
|
|
62
|
+
const Ee = qe || `${he}/run/api/chat`, { loadConversation: Ie } = ht({
|
|
63
|
+
baseUrl: he,
|
|
64
64
|
appId: N,
|
|
65
|
-
authToken:
|
|
66
|
-
getCaptchaHeader:
|
|
67
|
-
invalidateCaptcha:
|
|
68
|
-
refreshSession:
|
|
69
|
-
}), [
|
|
65
|
+
authToken: me,
|
|
66
|
+
getCaptchaHeader: O,
|
|
67
|
+
invalidateCaptcha: d,
|
|
68
|
+
refreshSession: ze
|
|
69
|
+
}), [We, Se] = k(!1), z = i(null);
|
|
70
70
|
z.current = pe;
|
|
71
71
|
const H = i(null);
|
|
72
|
-
H.current =
|
|
73
|
-
const K = i(void 0);
|
|
74
|
-
K.current = $ && Object.keys($).length > 0 ? $ : void 0;
|
|
72
|
+
H.current = le;
|
|
75
73
|
const q = i(void 0);
|
|
76
|
-
q.current =
|
|
77
|
-
const
|
|
74
|
+
q.current = U && Object.keys(U).length > 0 ? U : void 0;
|
|
75
|
+
const W = i(void 0);
|
|
76
|
+
W.current = G && Object.keys(G).length > 0 ? G : void 0;
|
|
77
|
+
const E = i(0), P = i(null), j = i(null), h = i(void 0), je = T?.map(
|
|
78
78
|
(e) => `${e.filename ?? ""}:${e.mediaType}:${e.url.length}:${e.url.slice(0, 64)}:${e.url.slice(-32)}`
|
|
79
79
|
).join(`
|
|
80
80
|
`) ?? "";
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}, [
|
|
84
|
-
const
|
|
85
|
-
|
|
81
|
+
_(() => {
|
|
82
|
+
h.current = T?.length ? T : void 0;
|
|
83
|
+
}, [je]);
|
|
84
|
+
const Ce = i(ye);
|
|
85
|
+
Ce.current = ye;
|
|
86
86
|
const V = i(void 0);
|
|
87
|
-
V.current =
|
|
88
|
-
const
|
|
89
|
-
|
|
87
|
+
V.current = ce || ge ? JSON.stringify({ ...ce, ...ge }) : void 0;
|
|
88
|
+
const Ve = (e) => {
|
|
89
|
+
if (Ne(e))
|
|
90
|
+
return P.current?.files?.length ? Le : De;
|
|
91
|
+
switch (wt(e)) {
|
|
90
92
|
case 400:
|
|
91
93
|
try {
|
|
92
94
|
const t = JSON.parse(e.message ?? "");
|
|
93
|
-
return t.detail ?? t.error?.message ??
|
|
95
|
+
return t.detail ?? t.error?.message ?? $e;
|
|
94
96
|
} catch {
|
|
95
|
-
return e.message?.trim() ||
|
|
97
|
+
return e.message?.trim() || $e;
|
|
96
98
|
}
|
|
97
99
|
case 401:
|
|
98
|
-
return
|
|
100
|
+
return ue ? "Authentication failed. Please try again." : ne;
|
|
99
101
|
case 403:
|
|
100
|
-
return `There seems to be a configuration error. Please contact ${
|
|
102
|
+
return `There seems to be a configuration error. Please contact ${ae.organizationDisplayName ?? "Administrator"}`;
|
|
103
|
+
case 413:
|
|
104
|
+
return oe;
|
|
101
105
|
case 429:
|
|
102
|
-
return
|
|
106
|
+
return Be;
|
|
103
107
|
default:
|
|
104
108
|
return ne;
|
|
105
109
|
}
|
|
106
|
-
}, [
|
|
107
|
-
() => new
|
|
108
|
-
api:
|
|
110
|
+
}, [I, Y] = k([]), Ye = Oe(
|
|
111
|
+
() => new dt({
|
|
112
|
+
api: Ee,
|
|
109
113
|
headers: () => {
|
|
110
|
-
const e =
|
|
114
|
+
const e = K ?? H.current ?? z.current;
|
|
111
115
|
return {
|
|
112
116
|
"x-inkeep-client-timezone": Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
113
117
|
"x-inkeep-client-timestamp": (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -115,11 +119,11 @@ const Bt = () => {
|
|
|
115
119
|
...N ? { "x-inkeep-app-id": N } : {},
|
|
116
120
|
...e ? { Authorization: `Bearer ${e}` } : {},
|
|
117
121
|
...V.current ? { "inkeep-filters": V.current } : {},
|
|
118
|
-
...
|
|
122
|
+
...Ce.current
|
|
119
123
|
};
|
|
120
124
|
},
|
|
121
125
|
prepareSendMessagesRequest: async (e) => {
|
|
122
|
-
const
|
|
126
|
+
const l = await Re.current(), t = e.messages[e.messages.length - 1];
|
|
123
127
|
return t || console.warn(
|
|
124
128
|
"[useInkeepChat] prepareSendMessagesRequest called with empty messages array"
|
|
125
129
|
), {
|
|
@@ -129,41 +133,41 @@ const Bt = () => {
|
|
|
129
133
|
messages: t ? [t] : [],
|
|
130
134
|
trigger: e.trigger,
|
|
131
135
|
messageId: e.messageId,
|
|
132
|
-
...
|
|
133
|
-
...
|
|
136
|
+
...q.current ? { userProperties: q.current } : {},
|
|
137
|
+
...W.current ? { properties: W.current } : {}
|
|
134
138
|
},
|
|
135
139
|
headers: {
|
|
136
140
|
...e.headers,
|
|
137
|
-
...
|
|
141
|
+
...l
|
|
138
142
|
}
|
|
139
143
|
};
|
|
140
144
|
},
|
|
141
145
|
body: {
|
|
142
|
-
requestContext:
|
|
146
|
+
requestContext: ve
|
|
143
147
|
}
|
|
144
148
|
}),
|
|
145
|
-
[
|
|
149
|
+
[Ee, ve, N, K]
|
|
146
150
|
), {
|
|
147
|
-
messages:
|
|
148
|
-
sendMessage:
|
|
151
|
+
messages: w,
|
|
152
|
+
sendMessage: J,
|
|
149
153
|
addToolApprovalResponse: Q,
|
|
150
|
-
status:
|
|
151
|
-
setMessages:
|
|
152
|
-
stop:
|
|
153
|
-
error:
|
|
154
|
-
} =
|
|
155
|
-
transport:
|
|
154
|
+
status: Ae,
|
|
155
|
+
setMessages: m,
|
|
156
|
+
stop: B,
|
|
157
|
+
error: D
|
|
158
|
+
} = lt({
|
|
159
|
+
transport: Ye,
|
|
156
160
|
onData(e) {
|
|
157
161
|
x(e.type, e.data);
|
|
158
162
|
},
|
|
159
163
|
async onFinish({ message: e }) {
|
|
160
|
-
x("completion", { conversationId: c }), await
|
|
164
|
+
x("completion", { conversationId: c }), await g({
|
|
161
165
|
eventName: "assistant_message_received",
|
|
162
166
|
properties: {
|
|
163
167
|
conversationId: c,
|
|
164
168
|
messageId: e.id
|
|
165
169
|
}
|
|
166
|
-
}),
|
|
170
|
+
}), g({
|
|
167
171
|
eventName: "assistant_answer_displayed",
|
|
168
172
|
properties: {
|
|
169
173
|
conversationId: c,
|
|
@@ -173,196 +177,210 @@ const Bt = () => {
|
|
|
173
177
|
},
|
|
174
178
|
onError(e) {
|
|
175
179
|
console.error("onError", { code: e.code, message: e.message });
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
+
const l = Ke || K ? null : At(e);
|
|
181
|
+
if (l !== null && E.current < 1) {
|
|
182
|
+
E.current++;
|
|
183
|
+
const o = j.current, r = P.current;
|
|
180
184
|
(async () => {
|
|
181
|
-
if (
|
|
182
|
-
const
|
|
183
|
-
if (!
|
|
184
|
-
H.current =
|
|
185
|
-
} else if (
|
|
186
|
-
const
|
|
187
|
-
|
|
185
|
+
if (l === "session" && ue) {
|
|
186
|
+
const n = await de();
|
|
187
|
+
if (!n) throw new Error("Auth token refresh failed");
|
|
188
|
+
H.current = n;
|
|
189
|
+
} else if (l === "session") {
|
|
190
|
+
const n = await fe();
|
|
191
|
+
n && (z.current = n);
|
|
188
192
|
} else
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
Q(
|
|
193
|
+
d();
|
|
194
|
+
if (o) {
|
|
195
|
+
Q(o);
|
|
192
196
|
return;
|
|
193
197
|
}
|
|
194
|
-
|
|
195
|
-
let
|
|
196
|
-
return
|
|
197
|
-
}),
|
|
198
|
+
r && (m((n) => {
|
|
199
|
+
let a = [...n];
|
|
200
|
+
return a.at(-1)?.role === "assistant" && (a = a.slice(0, -1)), a.at(-1)?.role === "user" && (a = a.slice(0, -1)), a;
|
|
201
|
+
}), J(
|
|
198
202
|
{
|
|
199
|
-
id:
|
|
203
|
+
id: r.messageId,
|
|
200
204
|
parts: [
|
|
201
|
-
...
|
|
202
|
-
...
|
|
205
|
+
...r.content.trim() ? [{ type: "text", text: r.content }] : [],
|
|
206
|
+
...r.files ?? []
|
|
203
207
|
]
|
|
204
208
|
},
|
|
205
|
-
{ body:
|
|
209
|
+
{ body: r.body }
|
|
206
210
|
));
|
|
207
211
|
})().catch(() => {
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
if (!f) return
|
|
211
|
-
const
|
|
212
|
-
return f.role === "user" ?
|
|
212
|
+
E.current = 0, d(), m((n) => {
|
|
213
|
+
const a = [...n], f = a[a.length - 1];
|
|
214
|
+
if (!f) return a;
|
|
215
|
+
const u = ne;
|
|
216
|
+
return f.role === "user" ? a.push({
|
|
213
217
|
id: F(16),
|
|
214
218
|
role: "assistant",
|
|
215
|
-
parts: [{ type: "text", text:
|
|
216
|
-
}) : f.parts = [{ type: "text", text:
|
|
219
|
+
parts: [{ type: "text", text: u }]
|
|
220
|
+
}) : f.parts = [{ type: "text", text: u }], a;
|
|
217
221
|
});
|
|
218
222
|
});
|
|
219
223
|
return;
|
|
220
224
|
}
|
|
221
|
-
|
|
222
|
-
const t =
|
|
223
|
-
if (
|
|
225
|
+
E.current = 0, l !== null && d();
|
|
226
|
+
const t = Tt(e) || Ne(e), s = Ve(e);
|
|
227
|
+
if (g({
|
|
224
228
|
eventName: "chat_error",
|
|
225
229
|
properties: {
|
|
226
230
|
conversationId: c,
|
|
227
|
-
messageId:
|
|
231
|
+
messageId: w.at(-1)?.id,
|
|
228
232
|
error: e.message
|
|
229
233
|
}
|
|
230
234
|
}), t) {
|
|
231
|
-
|
|
232
|
-
{ title: "Request failed", message:
|
|
233
|
-
|
|
234
|
-
),
|
|
235
|
-
let
|
|
236
|
-
return
|
|
235
|
+
L(
|
|
236
|
+
{ title: s === oe ? "Message too large" : s === Be ? "Rate limit reached" : s === De || s === Le ? "Connection error" : "Request failed", message: s },
|
|
237
|
+
Pe
|
|
238
|
+
), m((p) => {
|
|
239
|
+
let n = [...p];
|
|
240
|
+
return n.at(-1)?.role === "assistant" && (n = n.slice(0, -1)), n.at(-1)?.role === "user" && (n = n.slice(0, -1)), n;
|
|
237
241
|
});
|
|
238
|
-
const r =
|
|
239
|
-
r &&
|
|
242
|
+
const r = P.current?.content;
|
|
243
|
+
r && A(r), te.current = e;
|
|
240
244
|
return;
|
|
241
245
|
}
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
return
|
|
246
|
+
m((o) => {
|
|
247
|
+
const r = [...o], p = r[r.length - 1];
|
|
248
|
+
return p && (p.role === "user" ? r.push({
|
|
245
249
|
id: F(16),
|
|
246
250
|
role: "assistant",
|
|
247
|
-
parts: [{ type: "text", text:
|
|
248
|
-
}) :
|
|
251
|
+
parts: [{ type: "text", text: s }]
|
|
252
|
+
}) : p.parts = [{ type: "text", text: s }]), r;
|
|
249
253
|
});
|
|
250
254
|
}
|
|
251
|
-
}),
|
|
252
|
-
|
|
253
|
-
const e =
|
|
254
|
-
|
|
255
|
-
}, [
|
|
256
|
-
const
|
|
257
|
-
const e = (
|
|
258
|
-
if (!
|
|
259
|
-
const
|
|
260
|
-
return typeof
|
|
261
|
-
}, t = [...
|
|
255
|
+
}), Te = i(R);
|
|
256
|
+
_(() => {
|
|
257
|
+
const e = Te.current;
|
|
258
|
+
Te.current = R, e !== R && (B(), b(null), m([]), y(""), A(""), Y([]), d());
|
|
259
|
+
}, [R, B, m, y, d]);
|
|
260
|
+
const we = Ae === "submitted", X = Ae === "streaming", Je = Oe(() => {
|
|
261
|
+
const e = (o) => {
|
|
262
|
+
if (!o || typeof o != "object") return !1;
|
|
263
|
+
const r = o;
|
|
264
|
+
return typeof r.type == "string" && r.type.startsWith("tool-");
|
|
265
|
+
}, t = [...w ?? []].reverse().find((o) => o.role === "assistant");
|
|
262
266
|
if (!t) return !1;
|
|
263
|
-
const
|
|
264
|
-
return !(!e(
|
|
265
|
-
}, [
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
if (
|
|
269
|
-
|
|
267
|
+
const s = t.parts?.at(-1);
|
|
268
|
+
return !(!e(s) || s.state !== "output-available" || !s.approval?.id || X);
|
|
269
|
+
}, [w, X]), [Qe, Z] = k(!1), be = X || Je && !Qe, Me = we || be, Xe = w.length === 0, ee = !C.trim() && I.length === 0 || Me, Ze = gt("(max-width: 768px)"), [et, b] = k(null), te = i(null);
|
|
270
|
+
_(() => {
|
|
271
|
+
if (D) {
|
|
272
|
+
if (te.current === D) {
|
|
273
|
+
te.current = null;
|
|
270
274
|
return;
|
|
271
275
|
}
|
|
272
|
-
b(
|
|
276
|
+
b(D);
|
|
273
277
|
}
|
|
274
|
-
}, [
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
}, [D]);
|
|
279
|
+
const tt = () => b(null), { inputNotification: st, showInputNotification: L, clearInputNotification: rt } = Ct(), _e = i(null);
|
|
280
|
+
_(() => {
|
|
281
|
+
He?.(C);
|
|
278
282
|
}, [C]);
|
|
279
|
-
const
|
|
280
|
-
e.key === "Enter" && !e.shiftKey && !
|
|
283
|
+
const nt = (e) => {
|
|
284
|
+
e.key === "Enter" && !e.shiftKey && !ee && !e.nativeEvent.isComposing && (e.preventDefault(), se());
|
|
281
285
|
}, se = async (e = C) => {
|
|
282
|
-
if (
|
|
286
|
+
if (ee && (!e || e.trim().length === 0) && I.length === 0 || !e.trim() && !I.length && !h.current?.length)
|
|
283
287
|
return;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
if (bt({
|
|
289
|
+
text: e,
|
|
290
|
+
rawFileBytes: I.reduce((f, u) => f + u.size, 0),
|
|
291
|
+
encodedPartsBytes: (h.current ?? []).reduce(
|
|
292
|
+
(f, u) => f + u.url.length,
|
|
293
|
+
0
|
|
294
|
+
)
|
|
295
|
+
}) > Mt) {
|
|
296
|
+
L(
|
|
297
|
+
{ title: "Message too large", message: oe },
|
|
298
|
+
Pe
|
|
299
|
+
);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const t = I;
|
|
303
|
+
Y([]), A(""), E.current = 0, j.current = null, Z(!1);
|
|
304
|
+
let s = c;
|
|
305
|
+
s || (s = `conv_${F(16)}`, y(s));
|
|
306
|
+
const o = F(21);
|
|
307
|
+
Ue(s), await g({
|
|
290
308
|
eventName: "user_message_submitted",
|
|
291
309
|
properties: {
|
|
292
|
-
conversationId:
|
|
293
|
-
messageId:
|
|
310
|
+
conversationId: s,
|
|
311
|
+
messageId: o
|
|
294
312
|
}
|
|
295
313
|
});
|
|
296
|
-
const r =
|
|
297
|
-
|
|
298
|
-
let
|
|
299
|
-
if (
|
|
314
|
+
const r = h.current;
|
|
315
|
+
h.current = void 0;
|
|
316
|
+
let p = [];
|
|
317
|
+
if (t.length > 0)
|
|
300
318
|
try {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
return new Promise((
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
if (typeof
|
|
308
|
-
xe(new Error(`Failed to read file "${
|
|
319
|
+
p = await Promise.all(
|
|
320
|
+
t.map((f) => {
|
|
321
|
+
const u = ut(f);
|
|
322
|
+
return new Promise((ct, xe) => {
|
|
323
|
+
const M = new FileReader();
|
|
324
|
+
M.onload = () => {
|
|
325
|
+
if (typeof M.result != "string") {
|
|
326
|
+
xe(new Error(`Failed to read file "${u.name}"`));
|
|
309
327
|
return;
|
|
310
328
|
}
|
|
311
|
-
|
|
329
|
+
ct({
|
|
312
330
|
type: "file",
|
|
313
|
-
url:
|
|
314
|
-
mediaType:
|
|
315
|
-
filename:
|
|
331
|
+
url: M.result,
|
|
332
|
+
mediaType: u.type,
|
|
333
|
+
filename: u.name
|
|
316
334
|
});
|
|
317
|
-
},
|
|
335
|
+
}, M.onerror = () => xe(new Error(`Failed to read file "${u.name}"`)), M.readAsDataURL(u);
|
|
318
336
|
});
|
|
319
337
|
})
|
|
320
338
|
);
|
|
321
339
|
} catch {
|
|
322
|
-
|
|
340
|
+
L({
|
|
323
341
|
title: "Failed to attach files",
|
|
324
342
|
message: "Could not read one or more files. Please try again."
|
|
325
343
|
});
|
|
326
344
|
return;
|
|
327
345
|
}
|
|
328
|
-
const
|
|
346
|
+
const n = p.length || r?.length ? [...p, ...r ?? []] : void 0, a = [
|
|
329
347
|
...e.trim() ? [{ type: "text", text: e }] : [],
|
|
330
|
-
...
|
|
348
|
+
...n ?? []
|
|
331
349
|
];
|
|
332
|
-
|
|
350
|
+
P.current = {
|
|
333
351
|
content: e,
|
|
334
|
-
messageId:
|
|
335
|
-
body: { conversationId:
|
|
336
|
-
files:
|
|
337
|
-
},
|
|
338
|
-
},
|
|
352
|
+
messageId: o,
|
|
353
|
+
body: { conversationId: s },
|
|
354
|
+
files: n
|
|
355
|
+
}, J({ id: o, parts: a }, { body: { conversationId: s } });
|
|
356
|
+
}, ot = $(
|
|
339
357
|
(e) => {
|
|
340
|
-
|
|
358
|
+
E.current = 0, j.current = e, Z(!1), Q(e);
|
|
341
359
|
},
|
|
342
360
|
[Q]
|
|
343
|
-
), re =
|
|
344
|
-
|
|
361
|
+
), re = $(() => {
|
|
362
|
+
Z(!0), B().then(() => {
|
|
345
363
|
x("aborted", { conversationId: c });
|
|
346
364
|
});
|
|
347
|
-
}, [
|
|
348
|
-
|
|
365
|
+
}, [B, c, x]), ke = () => {
|
|
366
|
+
tt(), m([]), y(""), d(), h.current = T?.length ? T : void 0, g({
|
|
349
367
|
eventName: "chat_clear_button_clicked",
|
|
350
368
|
properties: {
|
|
351
369
|
conversationId: c
|
|
352
370
|
}
|
|
353
371
|
});
|
|
354
|
-
},
|
|
355
|
-
(e,
|
|
356
|
-
b(null),
|
|
372
|
+
}, S = $(
|
|
373
|
+
(e, l) => {
|
|
374
|
+
b(null), m(l), y(e), d(), h.current = void 0;
|
|
357
375
|
},
|
|
358
|
-
[
|
|
359
|
-
), Fe =
|
|
360
|
-
async (e,
|
|
361
|
-
re(),
|
|
376
|
+
[m, y, d]
|
|
377
|
+
), Fe = $(
|
|
378
|
+
async (e, l) => {
|
|
379
|
+
re(), S(e, []), Se(!0);
|
|
362
380
|
try {
|
|
363
|
-
const t = await
|
|
381
|
+
const t = await Ie(e, l);
|
|
364
382
|
if (t === null) return !1;
|
|
365
|
-
const
|
|
383
|
+
const o = t[t.length - 1]?.role === "user" ? [
|
|
366
384
|
...t,
|
|
367
385
|
{
|
|
368
386
|
id: F(16),
|
|
@@ -375,66 +393,68 @@ const Bt = () => {
|
|
|
375
393
|
]
|
|
376
394
|
}
|
|
377
395
|
] : t;
|
|
378
|
-
return
|
|
396
|
+
return S(e, o), !0;
|
|
379
397
|
} finally {
|
|
380
|
-
|
|
398
|
+
l?.aborted || Se(!1);
|
|
381
399
|
}
|
|
382
400
|
},
|
|
383
|
-
[
|
|
401
|
+
[S, Ie, re]
|
|
384
402
|
);
|
|
385
|
-
|
|
386
|
-
conversationId:
|
|
387
|
-
|
|
403
|
+
vt({
|
|
404
|
+
conversationId: v.conversationId,
|
|
405
|
+
fetchedConversation: v.fetchedConversation,
|
|
406
|
+
effectiveAuthToken: me,
|
|
407
|
+
restoreSession: S,
|
|
388
408
|
loadAndRestoreSession: Fe,
|
|
389
|
-
onLoadFailed: () =>
|
|
409
|
+
onLoadFailed: () => S("", [])
|
|
390
410
|
});
|
|
391
|
-
const { openForm:
|
|
392
|
-
return
|
|
411
|
+
const { openForm: at } = Rt(), it = Et();
|
|
412
|
+
return pt(v.chatFunctionsRef, () => ({
|
|
393
413
|
submitMessage: se,
|
|
394
414
|
updateInputMessage(e) {
|
|
395
|
-
|
|
415
|
+
A(e);
|
|
396
416
|
},
|
|
397
417
|
clearChat: ke,
|
|
398
418
|
openForm: (e) => {
|
|
399
|
-
|
|
419
|
+
it?.setView("chat"), at(e, void 0);
|
|
400
420
|
},
|
|
401
421
|
focusInput: () => {
|
|
402
|
-
|
|
422
|
+
_e.current?.focus();
|
|
403
423
|
}
|
|
404
424
|
})), {
|
|
405
|
-
messages:
|
|
406
|
-
sendMessage:
|
|
407
|
-
addToolApprovalResponse:
|
|
408
|
-
isLoading:
|
|
409
|
-
isStreaming:
|
|
410
|
-
isBusy:
|
|
411
|
-
error:
|
|
425
|
+
messages: w,
|
|
426
|
+
sendMessage: J,
|
|
427
|
+
addToolApprovalResponse: ot,
|
|
428
|
+
isLoading: we,
|
|
429
|
+
isStreaming: be,
|
|
430
|
+
isBusy: Me,
|
|
431
|
+
error: et,
|
|
412
432
|
setError: b,
|
|
413
|
-
isSubmitDisabled:
|
|
433
|
+
isSubmitDisabled: ee,
|
|
414
434
|
input: C,
|
|
415
|
-
handleInputChange:
|
|
416
|
-
handleInputKeyDown:
|
|
435
|
+
handleInputChange: Ge,
|
|
436
|
+
handleInputKeyDown: nt,
|
|
417
437
|
handleSubmit: se,
|
|
418
438
|
stop: re,
|
|
419
439
|
clear: ke,
|
|
420
|
-
inputRef:
|
|
421
|
-
isMobile:
|
|
422
|
-
files:
|
|
423
|
-
setFiles:
|
|
424
|
-
isNewChat:
|
|
440
|
+
inputRef: _e,
|
|
441
|
+
isMobile: Ze,
|
|
442
|
+
files: I,
|
|
443
|
+
setFiles: Y,
|
|
444
|
+
isNewChat: Xe,
|
|
425
445
|
conversationId: c,
|
|
426
|
-
restoreSession:
|
|
446
|
+
restoreSession: S,
|
|
427
447
|
loadAndRestoreSession: Fe,
|
|
428
|
-
isSessionLoading:
|
|
429
|
-
authToken:
|
|
430
|
-
refreshSession:
|
|
431
|
-
getCaptchaHeader:
|
|
432
|
-
invalidateCaptcha:
|
|
433
|
-
inputNotification:
|
|
434
|
-
showInputNotification:
|
|
435
|
-
clearInputNotification:
|
|
448
|
+
isSessionLoading: We,
|
|
449
|
+
authToken: R ? le : pe,
|
|
450
|
+
refreshSession: R ? de : fe,
|
|
451
|
+
getCaptchaHeader: O,
|
|
452
|
+
invalidateCaptcha: d,
|
|
453
|
+
inputNotification: st,
|
|
454
|
+
showInputNotification: L,
|
|
455
|
+
clearInputNotification: rt
|
|
436
456
|
};
|
|
437
457
|
};
|
|
438
458
|
export {
|
|
439
|
-
|
|
459
|
+
Wt as useInkeepChat
|
|
440
460
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react"),d=({conversationId:r,fetchedConversation:n,effectiveAuthToken:s,restoreSession:o,loadAndRestoreSession:c,onLoadFailed:i})=>{const e=t.useRef(null),a=t.useRef(o);a.current=o;const f=t.useRef(c);f.current=c;const R=t.useRef(i);R.current=i,t.useEffect(()=>{if(n&&!r&&console.warn("[useInitialConversation] fetchedConversation was provided without conversationId and will be ignored"),!r||e.current===r)return;if(n){e.current=r,a.current(r,n);return}if(!s)return;e.current=r;const u=new AbortController,b=l=>{e.current=null,console.error("[useInitialConversation] failed to load conversation:",r,l),R.current?.()};return(async()=>{try{!await f.current(r,u.signal)&&!u.signal.aborted&&b()}catch(l){u.signal.aborted||b(l)}})(),()=>{e.current=null,u.abort()}},[r,n,s])};exports.useInitialConversation=d;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { Message } from '../../types/message';
|
|
1
2
|
interface UseInitialConversationOptions {
|
|
2
3
|
/** The conversationId provided via props to load and restore. */
|
|
3
4
|
conversationId: string | undefined;
|
|
5
|
+
/** Pre-fetched messages — when provided, skips the network fetch entirely. */
|
|
6
|
+
fetchedConversation: Message[] | undefined;
|
|
4
7
|
/** Resolved auth token — fetch is skipped until one is available. */
|
|
5
8
|
effectiveAuthToken: string | null;
|
|
9
|
+
/** Restores a session with the given id and messages (no fetch). */
|
|
10
|
+
restoreSession: (sessionId: string, messages: Message[]) => void;
|
|
6
11
|
/** Returns true if the conversation was loaded successfully, false otherwise. */
|
|
7
12
|
loadAndRestoreSession: (sessionId: string, signal?: AbortSignal) => Promise<boolean>;
|
|
8
13
|
/** Called when the load fails (404 or other error) — use to reset conversation state. */
|
|
@@ -10,7 +15,8 @@ interface UseInitialConversationOptions {
|
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
17
|
* Loads and restores a conversation by ID once an auth token is available.
|
|
18
|
+
* When `fetchedConversation` is provided, seeds the session directly without fetching.
|
|
13
19
|
* Calls `onLoadFailed` if the conversation is not found or the fetch fails.
|
|
14
20
|
*/
|
|
15
|
-
export declare const useInitialConversation: ({ conversationId, effectiveAuthToken, loadAndRestoreSession, onLoadFailed, }: UseInitialConversationOptions) => void;
|
|
21
|
+
export declare const useInitialConversation: ({ conversationId, fetchedConversation, effectiveAuthToken, restoreSession, loadAndRestoreSession, onLoadFailed, }: UseInitialConversationOptions) => void;
|
|
16
22
|
export {};
|
|
@@ -1,34 +1,45 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useRef as
|
|
3
|
-
const
|
|
2
|
+
import { useRef as l, useEffect as d } from "react";
|
|
3
|
+
const p = ({
|
|
4
4
|
conversationId: r,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
fetchedConversation: e,
|
|
6
|
+
effectiveAuthToken: o,
|
|
7
|
+
restoreSession: s,
|
|
8
|
+
loadAndRestoreSession: c,
|
|
9
|
+
onLoadFailed: i
|
|
8
10
|
}) => {
|
|
9
|
-
const t =
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const t = l(null), a = l(s);
|
|
12
|
+
a.current = s;
|
|
13
|
+
const f = l(c);
|
|
14
|
+
f.current = c;
|
|
15
|
+
const b = l(i);
|
|
16
|
+
b.current = i, d(() => {
|
|
17
|
+
if (e && !r && console.warn(
|
|
18
|
+
"[useInitialConversation] fetchedConversation was provided without conversationId and will be ignored"
|
|
19
|
+
), !r || t.current === r) return;
|
|
20
|
+
if (e) {
|
|
21
|
+
t.current = r, a.current(r, e);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!o) return;
|
|
14
25
|
t.current = r;
|
|
15
|
-
const
|
|
16
|
-
t.current = null, console.error("[useInitialConversation] failed to load conversation:", r,
|
|
26
|
+
const n = new AbortController(), w = (u) => {
|
|
27
|
+
t.current = null, console.error("[useInitialConversation] failed to load conversation:", r, u), b.current?.();
|
|
17
28
|
};
|
|
18
29
|
return (async () => {
|
|
19
30
|
try {
|
|
20
|
-
!await
|
|
31
|
+
!await f.current(
|
|
21
32
|
r,
|
|
22
|
-
|
|
23
|
-
) && !
|
|
24
|
-
} catch (
|
|
25
|
-
|
|
33
|
+
n.signal
|
|
34
|
+
) && !n.signal.aborted && w();
|
|
35
|
+
} catch (u) {
|
|
36
|
+
n.signal.aborted || w(u);
|
|
26
37
|
}
|
|
27
38
|
})(), () => {
|
|
28
|
-
t.current = null,
|
|
39
|
+
t.current = null, n.abort();
|
|
29
40
|
};
|
|
30
|
-
}, [r,
|
|
41
|
+
}, [r, e, o]);
|
|
31
42
|
};
|
|
32
43
|
export {
|
|
33
|
-
|
|
44
|
+
p as useInitialConversation
|
|
34
45
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("react/jsx-runtime"),t=require("react"),p=require("./config-provider.cjs"),n=t.createContext(void 0),g=({children:e})=>{const{baseSettings:s,componentType:o}=p.useInkeepConfig(),{tags:r,analyticsProperties:i}=s,c="0.17.
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("react/jsx-runtime"),t=require("react"),p=require("./config-provider.cjs"),n=t.createContext(void 0),g=({children:e})=>{const{baseSettings:s,componentType:o}=p.useInkeepConfig(),{tags:r,analyticsProperties:i}=s,c="0.17.7",a=t.useMemo(()=>({widgetLibraryVersion:c,componentType:o,tags:r}),[o,r,c]),v={logEvent:t.useCallback(async u=>{const E={...a,...u.properties,...i},d={eventName:u.eventName,properties:E};return s.onEvent?.(d)},[s,a,i])};return l.jsx(n.Provider,{value:v,children:e})},m=()=>{const e=t.useContext(n);if(!e)throw new Error("useBaseEvents must be used within a BaseEventsProvider");return e};exports.BaseEventsContext=n;exports.BaseEventsProvider=g;exports.useBaseEvents=m;
|
|
@@ -3,7 +3,7 @@ import { jsx as u } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext as E, useMemo as d, useCallback as l, useContext as g } from "react";
|
|
4
4
|
import { useInkeepConfig as x } from "./config-provider.js";
|
|
5
5
|
const c = E(void 0), b = ({ children: e }) => {
|
|
6
|
-
const { baseSettings: t, componentType: o } = x(), { tags: s, analyticsProperties: n } = t, r = "0.17.
|
|
6
|
+
const { baseSettings: t, componentType: o } = x(), { tags: s, analyticsProperties: n } = t, r = "0.17.7", i = d(
|
|
7
7
|
() => ({
|
|
8
8
|
widgetLibraryVersion: r,
|
|
9
9
|
componentType: o,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("react/jsx-runtime"),u=require("react"),B=require("./config-provider.cjs"),O=require("./chat-auth-provider.cjs"),U=require("../hooks/use-events-api.cjs"),x=require("./base-events-provider.cjs"),M=({children:g})=>{const{baseSettings:n,aiChatSettings:h,componentType:i}=B.useInkeepConfig(),{tags:t,analyticsProperties:r,privacyPreferences:y,userProperties:o}=n,c="0.17.
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("react/jsx-runtime"),u=require("react"),B=require("./config-provider.cjs"),O=require("./chat-auth-provider.cjs"),U=require("../hooks/use-events-api.cjs"),x=require("./base-events-provider.cjs"),M=({children:g})=>{const{baseSettings:n,aiChatSettings:h,componentType:i}=B.useInkeepConfig(),{tags:t,analyticsProperties:r,privacyPreferences:y,userProperties:o}=n,c="0.17.7",a=u.useMemo(()=>({widgetLibraryVersion:c,componentType:i}),[i,c]),{effectiveAuthToken:b,applicableRefreshSession:f,getCaptchaHeader:E,invalidateCaptcha:P}=O.useChatAuth(),{baseUrl:m,appId:A,analyticsApiBaseUrl:C,headers:I}=h,p=y?.optOutAllAnalytics??!1,{logEvent:l}=U.useEventsApi({baseUrl:C??m,appId:A,authToken:b,getCaptchaHeader:E,invalidateCaptcha:P,refreshSession:f,headers:I}),k={logEvent:u.useCallback(async s=>{const q={eventName:s.eventName,properties:{...a,...t?{tags:t}:{},...s.properties,...r}};if(!p){const e=s.properties??{},v=typeof e.conversationId=="string"?e.conversationId:void 0,d=typeof e.messageId=="string"?e.messageId:void 0,S=!!o&&Object.keys(o).length>0,w={...t?{tags:t}:{},...r??{}},T={...a,...s.properties};l({body:{type:s.eventName,...v?{conversationId:v}:{},...d?{messageId:d}:{},...S?{userProperties:o}:{},properties:w,metadata:T}})}try{return await n.onEvent?.(q)}catch(e){console.warn("[events] onEvent callback threw",e)}},[n,a,t,r,l,p,o])};return j.jsx(x.BaseEventsContext.Provider,{value:k,children:g})};exports.ChatBaseEventsProvider=M;
|
|
@@ -6,7 +6,7 @@ import { useChatAuth as O } from "./chat-auth-provider.js";
|
|
|
6
6
|
import { useEventsApi as j } from "../hooks/use-events-api.js";
|
|
7
7
|
import { BaseEventsContext as M } from "./base-events-provider.js";
|
|
8
8
|
const F = ({ children: m }) => {
|
|
9
|
-
const { baseSettings: r, aiChatSettings: g, componentType: i } = N(), { tags: t, analyticsProperties: n, privacyPreferences: f, userProperties: o } = r, c = "0.17.
|
|
9
|
+
const { baseSettings: r, aiChatSettings: g, componentType: i } = N(), { tags: t, analyticsProperties: n, privacyPreferences: f, userProperties: o } = r, c = "0.17.7", a = x(
|
|
10
10
|
() => ({
|
|
11
11
|
widgetLibraryVersion: c,
|
|
12
12
|
componentType: i
|
|
@@ -3,6 +3,7 @@ import { GetHelpOption, CustomMessageAction } from './settings/actions';
|
|
|
3
3
|
import { SearchAndChatFilters } from './filters';
|
|
4
4
|
import { ComponentsConfig } from './components';
|
|
5
5
|
import { FileUIPart } from 'ai';
|
|
6
|
+
import { Message } from '../message';
|
|
6
7
|
export type { FileUIPart } from 'ai';
|
|
7
8
|
export interface InkeepAIChatSettings {
|
|
8
9
|
/**
|
|
@@ -166,6 +167,13 @@ export interface InkeepAIChatSettings {
|
|
|
166
167
|
* restore a previous conversation state.
|
|
167
168
|
*/
|
|
168
169
|
conversationIdOverride?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Pre-fetched conversation messages to render without fetching.
|
|
172
|
+
* Requires `conversationId` to be set.
|
|
173
|
+
*
|
|
174
|
+
* @beta This prop is subject to change.
|
|
175
|
+
*/
|
|
176
|
+
fetchedConversation?: Message[];
|
|
169
177
|
/**
|
|
170
178
|
* When enabled, prevents users from sending new messages.
|
|
171
179
|
* Useful for displaying archived or shared chat sessions.
|