@ant-design/x
Version:
Craft AI-driven interfaces effortlessly
208 lines (200 loc) • 6.49 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useXChat;
var _rcUtil = require("rc-util");
var _react = _interopRequireDefault(require("react"));
var _useSyncState = _interopRequireDefault(require("./useSyncState"));
function toArray(item) {
return Array.isArray(item) ? item : [item];
}
function useXChat(config) {
const {
defaultMessages,
agent,
requestFallback,
requestPlaceholder,
parser,
transformMessage,
transformStream,
resolveAbortController
} = config;
// ========================= Agent Messages =========================
const idRef = _react.default.useRef(0);
const [messages, setMessages, getMessages] = (0, _useSyncState.default)(() => (defaultMessages || []).map((info, index) => ({
id: `default_${index}`,
status: 'local',
...info
})));
const createMessage = (message, status) => {
const msg = {
id: `msg_${idRef.current}`,
message,
status
};
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' && info.status !== 'error').map(info => info.message);
// For agent to use. Will filter out loading and error message
const getRequestMessages = () => getFilteredMessages(getMessages());
const getTransformMessage = params => {
const {
chunk,
chunks,
originMessage
} = params;
if (typeof transformMessage === 'function') {
return transformMessage(params);
}
// Compatible bug fixes, onSuccess(output) => onSuccess(output[]),before v1.1.1
if (chunk) {
return chunk;
}
if (Array.isArray(chunks)) {
const chunk = chunks?.length > 0 ? chunks?.[chunks?.length - 1] : undefined;
return originMessage ? originMessage : chunk;
}
return chunks;
};
const onRequest = (0, _rcUtil.useEvent)(requestParams => {
if (!agent) throw new Error('The agent parameter is required when using the onRequest method in an agent generated by useXAgent.');
let loadingMsgId = null;
let message;
let otherRequestParams = {};
if (requestParams && typeof requestParams === 'object' && 'message' in requestParams) {
const {
message: requestParamsMessage,
...other
} = requestParams;
message = requestParamsMessage;
otherRequestParams = other;
} else {
message = requestParams;
}
// Add placeholder message
setMessages(ori => {
let nextMessages = [...ori, createMessage(message, 'local')];
if (requestPlaceholder) {
let placeholderMsg;
if (typeof requestPlaceholder === 'function') {
// typescript has bug that not get real return type when use `typeof function` check
placeholderMsg = requestPlaceholder(message, {
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) => {
let msg = getMessages().find(info => info.id === updatingMsgId);
if (!msg) {
// Create if not exist
const transformData = getTransformMessage({
chunk,
status,
chunks
});
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 = getTransformMessage({
originMessage: info.message,
chunk,
chunks,
status
});
return {
...info,
message: transformData,
status
};
}
return info;
});
});
}
return msg;
};
agent.request({
message,
messages: getRequestMessages(),
...otherRequestParams
}, {
onUpdate: chunk => {
updateMessage('loading', chunk, []);
},
onSuccess: chunks => {
updateMessage('success', undefined, chunks);
},
onError: async error => {
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
fallbackMsg = await requestFallback(message, {
error,
messages: getRequestMessages()
});
} else {
fallbackMsg = requestFallback;
}
setMessages(ori => [...ori.filter(info => info.id !== loadingMsgId && info.id !== updatingMsgId), createMessage(fallbackMsg, 'error')]);
} else {
// Remove directly
setMessages(ori => {
return ori.filter(info => info.id !== loadingMsgId && info.id !== updatingMsgId);
});
}
},
onStream: controller => {
resolveAbortController?.(controller);
}
}, transformStream);
});
return {
onRequest,
messages,
parsedMessages,
setMessages
};
}