For more information, see Panel MiniApp > Set up environment.
Product name: AI Agent
After the Smart Life OEM app and (optional) smart products are ready, you need to create an agent on the AI Agent Platform.
A product defines the data points (DPs) of the associated panel and device. Before you develop a panel, you must create an AI toy product, define and enable the required DPs, and then implement these DPs on the panel.
Register and log in to the Tuya Developer Platform and create a product.
Register and log in to the Smart MiniApp Developer Platform. For more information, see Create panel miniapp.
Open Tuya MiniApp IDE and create a panel miniapp project based on the AI agent template.
For more information, see Initialize project.
By now, you have completed the initialization of the development template of a panel miniapp. The following section shows the project directories.
├── src
│ ├── api // Aggregate file of all cloud API requests of the panel
│ ├── components
│ │ ├── AICard // Agent card component
│ │ ├── DialogConfirm // Confirmation dialog component
│ │ ├── DialogInput // Text input dialog component
│ │ ├── DialogPicker // DP selection dialog component
│ │ ├── GridBattery // Battery level component
│ │ ├── icon-font // SVG icon container component
│ │ ├── Modal // General dialog component
│ │ ├── NoData // Fallback component when there is no data
│ │ ├── PickerItem // General selection button component
│ │ ├── SearchBar // Search bar component
│ │ ├── Tag // Category tag subcomponent
│ │ ├── TagBar // Category tag bar component
│ │ ├── Text // General text component
│ │ ├── TopBar // General top bar component
│ │ ├── TouchableOpacity // General button component
│ ├── constant
│ │ ├── dpCodes.ts // dpCode constant
│ │ ├── index.ts // Stores all constant configurations
│ ├── devices // Device model
│ ├── hooks // Hooks
│ ├── i18n // Multilingual settings
│ ├── pages
│ │ ├── AIDialogue // Agent list page
│ │ ├── AvatarSelect // Select an avatar
│ │ ├── CloneSetting // Select the timbre cloning method
│ │ ├── CloneVoice // Clone the timbres
│ │ ├── CustomAgentEdit // Add and edit the agent roles
│ │ ├── DialogHistory // Show chat history of one agent
│ │ ├── home // Homepage
│ │ ├── VoiceSetting // Edit a single timbre
│ │ ├── VoiceSquare // Voice square
│ ├── redux // Redux
│ ├── res // Resources, such as pictures and SVG
│ ├── styles // Global style
│ ├── types // Define global types
│ ├── utils // Common utility methods
│ ├── app.config.ts
│ ├── app.less
│ ├── app.tsx
│ ├── composeLayout.tsx // Handle and listen for the adding, unbinding, and DP changes of sub-devices
│ ├── global.config.ts
│ ├── mixins.less // Less mixins
│ ├── routes.config.ts // Configure routing
│ ├── variables.less // Less variables
Call the getAIAgentRoles
API method to get the list of agent roles. Up to 10 roles can be obtained.
// Request the role list
export const getAIAgentRoles = async () => {
try {
// getAgentRoles
const response = await getAI2AgentRoles({
devId: getDevInfo().devId,
pageNo: 1,
pageSize: 10,
panelCode: 'ai_platform',
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const getBoundAgentsFunc = async () => {
getAIAgentRoles()
.then((res: any) => {
const { list: boundAgentList } = res;
console.log('==55boundAgentList', boundAgentList);
dispatch(updateRoleList([...boundAgentList]));
setRefresherTriggeredState(false);
})
.catch(err => {
setInitLoading(false);
console.log('getBoundAgentsFunc::err::', err);
setRefresherTriggeredState(false);
});
};
Tap Bind to device in the top-right corner of the agent chat card, and the agent role will be bound with the device. The default agent information display area at the top of the panel will automatically switch to the corresponding content of this agent role. The agent chat card will be automatically sorted to the first position. After binding, users will perceive the latest content about the agent's role when interacting with the device.
export const bindAIAgentRoles = async (roleId: string) => {
try {
// bindAgentRoles
const response = await bindAI2AgentRoles({
devId: getDevInfo().devId,
roleId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const bindAIRole = (roleId: string, inBind: boolean) => {
if (inBind) return;
showLoading({
title: '',
});
bindAIAgentRoles(roleId)
.then(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_binding_success'),
icon: 'success',
});
emitter.emit('refreshDialogData', '');
})
.catch(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_binding_fail'),
icon: 'error',
});
});
};
export const deleteAIAgentRoles = async (roleId: string) => {
try {
// deleteAgentRoles
const response = await deleteAI2AgentRoles({
devId: getDevInfo().devId,
roleId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const deleteAIRole = (roleId: string) => {
showLoading({
title: '',
});
deleteAIAgentRoles(roleId)
.then(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_success'),
icon: 'success',
});
emitter.emit('refreshDialogData', '');
})
.catch((err) => {
hideLoading();
console.log('deleteAIAgentRoles::err::', err);
showToast({
title: Strings.getLang('dsc_delete_fail'),
icon: 'error',
});
});
setUnbindId('-1');
setIsShowUnbindConfirm(false);
};
This chat history page shows the chat history between users and agent roles. When no chat content has been generated between the users and the agent, the template will show a placeholder graphic with a prompt. Users can tap the card at the top of the agent chat history to navigate to the agent editing page.
// Get the role's session history
export const getDialogHistoryList = async (params: GetHistoryParams) => {
try {
// getAIAgentHistory
const response = await getAI2AgentEndpointHistory({
devId: getDevInfo().devId,
roleId: params.roleId,
pageNo: params.pageNo,
pageSize: params.pageSize,
startTime: dayjs().subtract(3, 'months').format('YYYY-MM-DD HH:mm:ss'),
endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const [historyList, setHistoryList] = useState([]);
const getDialogHistoryListFunc = useCallback((params: GetListParams) => {
return new Promise((resolve, reject) => {
getDialogHistoryList({
pageNo: params.current,
pageSize: params.pageSize,
roleId,
startTime: params.startTime,
endTime: params.endTime,
})
.then((res: DialogHistoryRes) => {
const { totalPage, list } = res;
resolve({
total: totalPage,
list,
});
})
.catch(error => {
reject(error);
});
});
}, []);
const { pagination, data, run } = usePagination(
({ current, pageSize, startTime, endTime }) =>
getDialogHistoryListFunc({
current,
pageSize,
startTime,
endTime,
}) as Promise<GetDialogHistory>,
{
manual: true,
}
);
Delete chat information for one or more groups of roles.
// Clear the context clearAgentHistoryMessage
export const clearAgentHistoryMessage = async (params: any) => {
try {
// deleteAIAgentHistoryMessage
const response = await deleteAI2AgentEndpointHistory({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const handleDeleteMessage = async () => {
try {
showLoading({ title: '' });
await clearAgentHistoryMessage({
roleId,
requestIds: selectedItems?.join(','), // requestId is docId
});
const newList = historyList.filter(item => !selectedItems.includes(item.requestId));
setHistoryList(newList);
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_chat_history_tip_new'),
icon: 'success',
});
} catch (err) {
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_chat_history_failed'),
icon: 'error',
});
}
};
This feature is used to clear the chat history between the user and the agent role and initialize the chat content history of this agent role.
// Clear chat history
export const clearingHistoryRecord = async (roleId: string) => {
try {
// deleteAIAgentHistory
const response = await clearAI2AgentEndpointHistory({
devId: getDevInfo().devId,
roleId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const handleClearingHistoryRecord = () => {
showModal({
title: '',
content: Strings.getLang('dsc_delete_chat_history'),
confirmText: Strings.getLang('delete'),
cancelText: Strings.getLang('dsc_cancel'),
success: ({ confirm }) => {
if (confirm) {
showLoading({
title: '',
});
clearingHistoryRecord(roleId)
.then(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_chat_history_tip'),
icon: 'success',
});
navigateBack({});
setTimeout(() => {
emitter.emit('refreshHistoryData', '');
}, 2000);
})
.catch(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_chat_history_failed'),
icon: 'error',
});
});
}
},
});
};
Modify the role information, including avatar, name, role introduction, and max context messages. Also, select the timbre, language, and large language model.
You can provide pre-configured templates to users to streamline the operation process.
// Get a list of role templates
export const getAIAgentRolesTemplateList = async () => {
try {
// getAgentRolesTemplates
const response = await getAI2AgentRolesTemplates({ devId: getDevInfo().devId, panelCode: 'ai_platform' });
return response;
} catch (err) {
return Promise.reject(err);
}
};
const getTemplateList = async () => {
getAIAgentRolesTemplateList()
.then((res: any) => {
console.log('==getTemplateList', res);
setTemplateList(res);
if (res?.length > 0) {
setTemplateRoleId(res[0].roleId);
}
})
.catch(err => {
console.log('getTemplateList::err::', err);
});
};
// Get role template details
export const getAIAgentRolesTemplatesDetail = async (roleId: string) => {
try {
// getAgentRolesTemplatesDetail
const response = await getAI2AgentRolesTemplatesDetail({ devId: getDevInfo().devId, roleId });
return response;
} catch (err) {
return Promise.reject(err);
}
};
const getTemplateListDetail = async (roleId: string) => {
getAIAgentRolesTemplatesDetail(roleId)
.then((res: any) => {
console.log('==getTemplateListDetail', res);
initRoleData(res);
initRoleInfoData();
})
.catch(err => {
console.log('getTemplateListDetail::err::', err, roleId);
});
};
When editing a role, get the details of the specified role.
export const getAIAgentRoleDetail = async (roleId: string) => {
try {
// getAgentRolesDetail
const response = await getAI2AgentRolesDetail({
devId: getDevInfo().devId,
roleId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const getAgentRoleDetail = async (roleId: string) => {
getAIAgentRoleDetail(roleId)
.then((res: any) => {
console.log('==getAIAgentRoleDetail', res);
initRoleData(res);
initRoleInfoData();
})
.catch(err => {
console.log('getAIAgentRoleDetail::err::', err);
});
};
Create or edit the specified role.
// Create a role
export const createRole = async (params: any) => {
try {
// postCreateAgentRole
const response = await addAI2AgentRoles({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
// Edit the role
export const updateRole = async (params: any) => {
try {
// postUpdateAgentRole
const response = await updateAI2AgentRoles({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const handleSave = async () => {
if (!name || nameErrorMessage) {
showToast({
title: `${Strings.getLang('please_complete')}${Strings.getLang('role_name')}`,
icon: 'error',
});
return;
}
if (!introduction) {
showToast({
title: `${Strings.getLang('please_complete')}${Strings.getLang('introduction')}`,
icon: 'error',
});
return;
}
if (!roleInfo?.voiceId && !selectedVoiceId) {
showToast({
title: `${Strings.getLang('please_complete')}${Strings.getLang('role_voice')}`,
icon: 'error',
});
return;
}
if (!selectedModel) {
showToast({
title: `${Strings.getLang('please_complete')}${Strings.getLang('model')}`,
icon: 'error',
});
return;
}
const params = {
roleName: name,
roleIntroduce: introduction,
roleImgUrl: roleInfo?.roleImgUrl || roleImgUrl,
useLangCode: roleInfo?.useLangId || selectedLanguageCode,
useTimbreId: roleInfo?.voiceId || selectedVoiceId,
useLlmId: selectedModel,
memoryInfo: memoryInfo,
}
try {
if (roleId) {
params.roleId = roleId
await updateRole(params);
} else {
await createRole(params);
}
hideLoading();
showToast({
title: Strings.getLang('save_success'),
icon: 'success',
});
emitter.emit('refreshDialogData', '');
navigateBack({ delta: 9999 });
} catch (error) {
console.log('createRole/updateRole:::', error);
hideLoading();
showToast({
title: Strings.getLang('save_failed'),
icon: 'error',
});
}
};
You can provide pre-configured avatars for users to choose from.
// Get an avatar
export const getAIAvatars = async () => {
try {
// getAgentAvatars
const response = await getAI2AgentAvatars({
devId: getDevInfo().devId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const getBoundAgentsFunc = async () => {
getAIAvatars()
.then(res => {
console.log('getAIAvatars::', res);
setAvatarList(res);
})
.catch(err => {
console.log('getAIAvatars::err::', err);
});
};
Get the supported languages.
// Get supported languages
export const getAgentLanguages = async () => {
try {
// getAgentLanguageConfig
const response = await getAI2AgentConfigLangList({
devId: getDevInfo().devId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const useAgentLanguages = (id: number) => {
const [langRangeList, setSupportLangs] = useState<Array<{ key: string; dataString: string }>>([]);
useEffect(() => {
const fetchSupportedLangs = async () => {
try {
const response = await getAgentLanguages();
if (response) {
setSupportLangs(
response?.map(item => ({
dataString: item?.langName,
key: item?.langCode,
}))
);
}
} catch (error) {
console.error('Failed to fetch supported languages:', error);
}
};
fetchSupportedLangs();
}, []);
return { langRangeList };
};
import useAgentLanguages from '@/hooks/useAgentLanguages';
const { langRangeList } = useAgentLanguages(id);
Get the list of large language models.
// Get a list of large language models
export const getAgentModels = async () => {
try {
// getAgentModelConfig
const response = await getAI2AgentConfigLlmList({
devId: getDevInfo().devId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const fetchSupportedModels = async () => {
try {
const response = await getAgentModels();
console.log('getAgentModels:::', response);
if (response) {
const models = response?.map(model => ({
key: model.llmId,
dataString: model.llmName,
}));
setModelList(models);
}
} catch (error) {
console.error('Failed to fetch supported models:', error);
}
};
Switch the timbre information carried by the specified agent role. Manage user-cloned timbres and system default timbres.
Users can view their cloned timbres in the Mine category. If no cloned timbre is currently available, click Clone voice to navigate to the cloning page and begin creating your personalized timbre.
// Get the recording capability set
const RecorderManager = getRecorderManager({
complete: () => {
console.log('==complete');
},
success: (params: null) => {
console.log('===success==getRecorderManager', params);
},
fail: (params: null) => {
console.log('===fail==getRecorderManager', params);
},
});
// Start recording
const startRecording = async () => {
if (!RecorderManagerRef.current) {
RecorderManager?.start?.({
sampleRate: 16000,
encodeBitRate: 96000,
frameSize: 10,
format: 'wav' as any,
complete: () => {
console.log('===startRecording==complete');
},
success: params => {
console.log('===startRecording==success', params);
RecorderManagerRef.current = true;
},
fail: params => {
console.log('===startRecording==fail', params);
resetAll();
},
});
}
try {
ty.createAIAssistant({
complete: () => {
console.log('createAIAssistant==complete');
},
success: (params: null) => {
console.log('createAIAssistant===success==getRecorderManager', params);
},
fail: (params: null) => {
console.log('createAIAssistant===fail==getRecorderManager', params);
},
});
const authorize = await Asr.authorize();
if (!authorize) {
resetAll();
showToast({
title: Strings.getLang('no_asr_permission'),
icon: 'error',
});
return;
}
// if (!asrRef.current) {
// Create ASR detector
const asr = createAsrDetector();
// Start ASR
asrRef.current = asr;
// }
await asrRef.current?.start();
// Initialization is successful, you can start recording
changeAsrInted(true);
timer.current = setInterval(() => {
timerCheck();
}, 1000);
} catch (error) {
console.error('Error starting ASR:', error);
stopTouch();
showToast({
title: Strings.getLang('dsc_clone_fail'),
icon: 'error',
});
clearAsr();
}
};
// Stop recording
const stopRecording = async () => {
RecorderManager?.stop?.({
complete: () => {
console.log('===stopRecording==complete');
// setCloneState('start');
RecorderManagerRef.current = false;
clearAsr();
},
success: params => {
console.log('params?.tempFilePath', params?.tempFilePath);
try {
uploadImage({
filePath: params?.tempFilePath,
bizType: 'voice_clone',
contentType: 'audio/mpeg',
success: params => {
const { publicUrl } = JSON.parse(params?.result);
console.log('publicUrl', publicUrl);
submitVoice(publicUrl);
},
fail: params => {
console.log('===uploadImage==fail', params);
clearAsr();
resetAll();
},
});
console.log('uploadImage:::success:::params', params);
} catch (err) {
console.log('catch uploadImage err:', err);
}
},
fail: params => {
console.log('===stopRecording==fail', params);
clearAsr();
resetAll();
},
});
};
How it works:
The list on the square shows the system's default timbres. Users can select suitable timbres from the square to empower the agent role. Users can filter the timbres by category or search for desired ones.
// Get a list of cloned timbres
export const getCloneVoiceList = async (params: any) => {
try {
// getCloneList
const response = await getAI2TimbreCloneList({ devId: getDevInfo().devId, ...params });
return response;
} catch (err) {
return Promise.reject(err);
}
};
// Get a list of standard timbres
export const getStandardVoiceList = async (params: any) => {
try {
// getMarketList
const response = await getAI2TimbreMarketList({
devId: getDevInfo().devId,
pageNo: params.pageNo,
pageSize: params.pageSize,
tag: '',
keyWord: params.keyWord,
lang: params.lang,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
// Request a list of timbres (including cloned timbres)
const getVoiceListFunc = (params: GetListParams) => {
return new Promise((resolve, reject) => {
getStandardVoiceList({
pageNo: params.current,
pageSize: params.pageSize,
tag,
agentId,
keyWord: params.searchText,
lang: selectedLang,
})
.then((res: VoiceRes) => {
const { totalPage, list = [] } = res;
hideLoading();
setLoading(false);
resolve({
total: totalPage,
list,
});
})
.catch(error => {
hideLoading();
setLoading(false);
reject(error);
});
});
};
const initClone = () => {
getCloneVoiceList({ lang: selectedLang })
.then(res => {
hideLoading();
setResList(res);
setLoading(false);
})
.catch(error => {
hideLoading();
setLoading(false);
});
};
// Manage timbre list requests
const { pagination, data, run } = usePagination(
({ current, pageSize, searchText }) =>
getVoiceListFunc({ current, pageSize, searchText }) as Promise<GetStandardVoice>,
{
manual: true,
}
);
// Response lazy loading
useEffect(() => {
if (data?.list) {
pagination.current > 1
? dispatch(updateVoiceList([...data?.list]))
: dispatch(initVoiceList([...data?.list]));
}
}, [data]);
// The request process for searching for timbres is the same as that for querying the agent list. The difference lies in the [searchText] field. In order to pull the entire list, make the [searchText] field an empty string.
const { pagination, data, run } = usePagination(
({ current, pageSize, searchText }) =>
getVoiceListFunc({ current, pageSize, searchText }) as Promise<GetStandardVoice>,
{
manual: true,
}
);
// Switch to another timbre (Edit agent details)
export const updateRole = async (params: any) => {
try {
// postUpdateAgentRole
const response = await updateAI2AgentRoles({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const handleItemChecked = async (idKey: string, item: any) => {
try {
if (tag === 'mine') {
showLoading({ title: Strings.getLang('clone_voice_generating') });
const cloneMp3 = await localGetCloneRadioSource({
objectKey: item?.demoUrl || undefined,
voiceId: idKey,
lang: selectedLang,
// lang: finalLanguage,
}).catch(err => {
hideLoading();
});
setTimeout(() => {
hideLoading();
}, 500);
if (cloneMp3) {
playVoiceAudio(cloneMp3);
}
} else {
playVoiceAudio(item?.demoUrl);
}
if (squareEntry === 'role') {
dispatch(
updateRoleInfo({
voiceId: idKey,
voiceName: item?.voiceName,
supportLangs: item?.supportLangs,
})
);
return;
}
if (tag !== 'mine') {
showLoading({
title: '',
});
}
const params = { ...roleInfo, useTimbreId: idKey, roleId };
if (squareEntry === 'role') {
delete params.roleId;
}
updateRole(params)
.then(async res => {
if (res) {
dispatch(updateRoleInfo({ voiceId: idKey, voiceName: item?.voiceName }));
setTimeout(async () => {
const agentCloudInfo = (await getAgentInfo(roleId)) as AgentInfo;
dispatch(updateAgentInfo({ ...agentCloudInfo, roleId }));
hideLoading();
}, 500);
}
hideLoading();
})
.catch(error => {
if (platform === 'android' && error?.innerError?.errorCode === '13890100') {
showToast({
title: error?.innerError?.errorMsg,
icon: 'error',
});
} else if (platform === 'ios') {
showToast({
title: iOSExtractErrorMessage(error?.innerError?.errorMsg),
icon: 'error',
});
} else {
showToast({
title: Strings.getLang('dsc_choose_fail'),
icon: 'error',
});
}
hideLoading();
});
} catch (err) {
console.log('handleItemChecked:::err', err);
hideLoading();
}
};
On this page, you can edit specific details of a timbre. Regarding timbres on the square, their speed can be modified on this page. Regarding cloned ones, additional functionalities are supported, such as modifying speed, re-cloning, and deleting them.
// Edit agent details (including intonation and speaking speed)
export const updateRole = async (params: any) => {
try {
// postUpdateAgentRole
const response = await updateAI2AgentRoles({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const onChangeToneOrSpeed = (sv: number, tv: number) => {
showLoading({
title: '',
});
updateRole({ roleId, [speedKey]: `${sv}`, [toneKey]: `${tv}` })
.then(async res => {
sv >= 0 && setSpeedValue(sv);
tv >= 0 && setToneValue(tv);
hideLoading();
showToast({
title: Strings.getLang('dsc_edit_success'),
icon: 'success',
});
})
.catch(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_edit_fail'),
icon: 'error',
});
});
};
// Restore default settings (including intonation and speaking speed)
export const localResetToDefaultVoice = async (params: any) => {
try {
// resetToDefaultVoice
const response = await restoreAI2AgentRolesSpeed({
devId: getDevInfo().devId,
...params,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const onChangeToneOrSpeedReset = () => {
showLoading({
title: '',
});
localResetToDefaultVoice({ roleId })
.then(async res => {
const sv = res?.speed;
const tv = res?.tone;
sv >= 0 && setSpeedValue(sv);
tv >= 0 && setToneValue(tv);
hideLoading();
showToast({
title: Strings.getLang('dsc_edit_success'),
icon: 'success',
});
})
.catch(() => {
hideLoading();
showToast({
title: Strings.getLang('dsc_edit_fail'),
icon: 'error',
});
});
};
// Edit the name of a cloned timbre
export const renameCloneVoice = async (voiceId: string, nameText: string) => {
try {
// renameAITimbreClone
const response = await renameAI2TimbreClone({
voiceId,
name: nameText,
devId: getDevInfo().devId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const editCloneName = (text: string) => {
showLoading({
title: '',
});
renameCloneVoice(voiceId, text)
.then(async res => {
setTimeout(async () => {
const agentCloudInfo = (await getAgentInfo(Number(roleId))) as AgentInfo;
updateAgentInfo({ ...agentCloudInfo, roleId });
}, 300);
hideLoading();
setNameText(text);
showToast({
title: Strings.getLang('dsc_edit_success'),
icon: 'success',
});
emitter.emit('refreshVoiceData', '');
})
.catch(err => {
hideLoading();
showToast({
title: Strings.getLang('dsc_edit_fail'),
icon: 'error',
});
});
};
// Delete a cloned timbre
export const deleteCloneVoice = async (voiceId: string) => {
try {
// deleteAITimbreClone
const response = await delAI2TimbreClone({
voiceId,
devId: getDevInfo().devId,
});
return response;
} catch (err) {
return Promise.reject(err);
}
};
const handleDeleteClone = () => {
showLoading({
title: '',
});
deleteCloneVoice(voiceId)
.then(async () => {
hideLoading();
showToast({
title: Strings.getLang('dsc_delete_success'),
icon: 'success',
});
const agentCloudInfo = (await getAgentInfo(Number(roleId))) as AgentInfo;
updateAgentInfo({ ...agentCloudInfo, roleId });
emitter.emit('refreshVoiceData', '');
setTimeout(() => {
router.back();
}, 300);
})
.catch(error => {
hideLoading();
if (platform === 'android' && error?.innerError?.errorCode === '138903140') {
showToast({
title: error?.innerError?.errorMsg,
icon: 'error',
});
} else if (platform === 'ios') {
showToast({
title: iOSExtractErrorMessage(error?.innerError?.errorMsg),
icon: 'error',
});
} else {
showToast({
title: Strings.getLang('dsc_delete_fail'),
icon: 'error',
});
}
});
};