@ant-design/x-sdk
Version:
placeholder for @ant-design/x-sdk
291 lines (286 loc) • 10.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useXChat;
var _rcUtil = require("rc-util");
var _react = _interopRequireWildcard(require("react"));
var _store = require("./store");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var MessageStatusEnum = /*#__PURE__*/function (MessageStatusEnum) {
MessageStatusEnum["local"] = "local";
MessageStatusEnum["loading"] = "loading";
MessageStatusEnum["updating"] = "updating";
MessageStatusEnum["success"] = "success";
MessageStatusEnum["error"] = "error";
MessageStatusEnum["abort"] = "abort";
return MessageStatusEnum;
}(MessageStatusEnum || {});
function toArray(item) {
return Array.isArray(item) ? item : [item];
}
const IsRequestingMap = new Map();
function useXChat(config) {
const {
defaultMessages,
requestFallback,
requestPlaceholder,
parser,
provider,
conversationKey
} = config;
// ========================= Agent Messages =========================
const idRef = _react.default.useRef(0);
const requestHandlerRef = _react.default.useRef(undefined);
const [isRequesting, setIsRequesting] = (0, _react.useState)(false);
const {
messages,
setMessages,
getMessages,
setMessage
} = (0, _store.useChatStore)(() => (defaultMessages || []).map((info, index) => ({
id: `default_${index}`,
status: 'local',
...info
})), conversationKey);
const createMessage = (message, status, extra) => {
const msg = {
id: `msg_${idRef.current}`,
message,
status
};
if (extra) {
msg.extra = extra;
}
idRef.current += 1;
return msg;
};
// ========================= BubbleMessages =========================
const parsedMessages = _react.default.useMemo(() => {
const list = [];
messages.forEach(agentMsg => {
const rawParsedMsg = parser ? parser(agentMsg.message) : agentMsg.message;
const bubbleMsgs = toArray(rawParsedMsg);
bubbleMsgs.forEach((bubbleMsg, bubbleMsgIndex) => {
let key = agentMsg.id;
if (bubbleMsgs.length > 1) {
key = `${key}_${bubbleMsgIndex}`;
}
list.push({
id: key,
message: bubbleMsg,
status: agentMsg.status
});
});
});
return list;
}, [messages]);
// ============================ Request =============================
const getFilteredMessages = msgs => msgs.filter(info => info.status !== 'loading').map(info => info.message);
provider?.injectGetMessages(() => {
return getFilteredMessages(getMessages());
});
requestHandlerRef.current = provider?.request;
// For agent to use. Will filter out loading and error message
const getRequestMessages = () => getFilteredMessages(getMessages());
const innerOnRequest = (requestParams, opts) => {
if (!provider) {
return;
}
const {
updatingId,
reload
} = opts || {};
let loadingMsgId = null;
const localMessage = provider.transformLocalMessage(requestParams);
const messages = (Array.isArray(localMessage) ? localMessage : [localMessage]).map(message => createMessage(message, 'local', opts?.extra));
if (reload) {
loadingMsgId = updatingId;
setMessages(ori => {
const nextMessages = [...ori];
if (requestPlaceholder) {
let placeholderMsg;
if (typeof requestPlaceholder === 'function') {
// typescript has bug that not get real return type when use `typeof function` check
placeholderMsg = requestPlaceholder(requestParams, {
messages: getFilteredMessages(nextMessages)
});
} else {
placeholderMsg = requestPlaceholder;
}
nextMessages.forEach(info => {
if (info.id === updatingId) {
info.status = 'loading';
info.message = placeholderMsg;
if (opts?.extra) {
info.extra = opts?.extra;
}
}
});
}
return nextMessages;
});
} else {
// Add placeholder message
setMessages(ori => {
let nextMessages = [...ori, ...messages];
if (requestPlaceholder) {
let placeholderMsg;
if (typeof requestPlaceholder === 'function') {
// typescript has bug that not get real return type when use `typeof function` check
placeholderMsg = requestPlaceholder(requestParams, {
messages: getFilteredMessages(nextMessages)
});
} else {
placeholderMsg = requestPlaceholder;
}
const loadingMsg = createMessage(placeholderMsg, 'loading');
loadingMsgId = loadingMsg.id;
nextMessages = [...nextMessages, loadingMsg];
}
return nextMessages;
});
}
// Request
let updatingMsgId = null;
const updateMessage = (status, chunk, chunks, responseHeaders) => {
let msg = getMessages().find(info => info.id === updatingMsgId);
if (!msg) {
if (reload && updatingId) {
msg = getMessages().find(info => info.id === updatingId);
if (msg) {
msg.status = status;
msg.message = provider.transformMessage({
chunk,
status,
chunks,
responseHeaders
});
setMessages(ori => {
return [...ori];
});
updatingMsgId = msg.id;
}
} else {
// Create if not exist
const transformData = provider.transformMessage({
chunk,
status,
chunks,
responseHeaders
});
msg = createMessage(transformData, status);
setMessages(ori => {
const oriWithoutPending = ori.filter(info => info.id !== loadingMsgId);
return [...oriWithoutPending, msg];
});
updatingMsgId = msg.id;
}
} else {
// Update directly
setMessages(ori => {
return ori.map(info => {
if (info.id === updatingMsgId) {
const transformData = provider.transformMessage({
originMessage: info.message,
chunk,
chunks,
status,
responseHeaders
});
return {
...info,
message: transformData,
status
};
}
return info;
});
});
}
return msg;
};
provider.injectRequest({
onUpdate: (chunk, headers) => {
updateMessage('updating', chunk, [], headers);
},
onSuccess: (chunks, headers) => {
setIsRequesting(false);
conversationKey && IsRequestingMap.delete(conversationKey);
updateMessage('success', undefined, chunks, headers);
},
onError: async error => {
setIsRequesting(false);
conversationKey && IsRequestingMap.delete(conversationKey);
if (requestFallback) {
let fallbackMsg;
// Update as error
if (typeof requestFallback === 'function') {
// typescript has bug that not get real return type when use `typeof function` check
const messages = getRequestMessages();
const msg = getMessages().find(info => info.id === loadingMsgId || info.id === updatingMsgId);
fallbackMsg = await requestFallback(requestParams, {
error,
messageInfo: msg,
messages
});
} else {
fallbackMsg = requestFallback;
}
setMessages(ori => [...ori.filter(info => info.id !== loadingMsgId && info.id !== updatingMsgId), createMessage(fallbackMsg, error.name === 'AbortError' ? 'abort' : 'error')]);
} else {
// Remove directly
setMessages(ori => {
return ori.map(info => {
if (info.id === loadingMsgId || info.id === updatingMsgId) {
return {
...info,
status: error.name === 'AbortError' ? 'abort' : 'error'
};
}
return info;
});
});
}
}
});
setIsRequesting(true);
conversationKey && IsRequestingMap.set(conversationKey, true);
provider.request.run(provider.transformParams(requestParams, provider.request.options));
};
const onRequest = (0, _rcUtil.useEvent)((requestParams, opts) => {
if (!provider) {
throw new Error('provider is required');
}
innerOnRequest(requestParams, opts);
});
const onReload = (id, requestParams, opts) => {
if (!provider) {
throw new Error('provider is required');
}
if (!id || !getMessages().find(info => info.id === id)) {
throw new Error(`message [${id}] is not found`);
}
innerOnRequest(requestParams, {
updatingId: id,
reload: true,
extra: opts?.extra
});
};
return {
onRequest,
messages,
parsedMessages,
setMessages,
setMessage,
abort: () => {
if (!provider) {
throw new Error('provider is required');
}
requestHandlerRef.current?.abort();
},
isRequesting: conversationKey ? IsRequestingMap?.get(conversationKey) || false : isRequesting,
onReload
};
}