Prerequisites

Development environment

See Panel MiniApp > Set up environment.

Product name: Panel Agent

Requirement prototype

Create an agent

After the Smart Life OEM app and optional smart devices are ready, create an agent on the AI Agent Platform.

  1. Click Create Agent, fill in the required project information, and click OK to create an agent.

  1. In 01 Model Capability Configuration, customize the model, memory, tools, knowledge base, and workflows. Then, in 02 Prompt Development, configure the prompts and finish creating the agent in the current data center.

Debug the agent

  1. After the prompts are configured, click Retrieve QR Code.

  1. After debugging, configure the agents in each data center as needed, click Release, set the version number, and publish the agent online.

  1. After the release succeeds, click Publish to make the agent available under your platform account for subsequent product binding.

  1. Return to the agent list page to view the dedicated agent you just created.

A product defines the data points (DPs) and agents carried by the panel and the device. Before you start panel development, create an AI plush-toy product, define its DPs, associate the required agents, and then implement these DPs in the panel.

First, sign in to the Tuya Developer Platform and create a product.

  1. Under Product on the Tuya Developer Platform, click Create Product.

  1. Under Standard Category, choose Audio Wearables > AI Plush Toy.

  1. Select the smart mode and solution, complete the product information, and click Create Product.

  1. On Add Standard Function, select the appropriate DPs for the hardware solution and click OK.

  1. After that, you have created the product. Continue to 01 Function Definition > Product AI Function to add AI capabilities.

  1. Click Add Product AI Capabilities, choose the capabilities your product needs, and click OK.

  1. Configure the default agent for the product (device-side agents are used in this template). Click Select AI Agent to add an agent.

  1. Select the agent you want to bind with the device.

  1. After the binding configuration is completed, click Next Step: Device Interaction.

  1. On Device Interaction, select a panel template. For the remaining steps, refer to the Product Development Guide.

Create a panel miniapp on the developer platform

Panel miniapps are managed on the Smart MiniApp Developer Platform. Go to the developer platform, register, and sign in.

For step-by-step instructions, see Create panel miniapp.

Create a project from the template in the IDE

Open Tuya MiniApp IDE and create a project with the AI Agent MiniApp template.

For detailed steps, see Initialize project.

After completing the steps above, the panel miniapp template is initialized. The project directory is organized as follows:

├── src
│   ├── api // Aggregate file for all cloud API requests
│   │   ├── atop.ts // ATOP request wrapper
│   │   ├── getCachedLaunchOptions.ts // Cached launch options
│   │   ├── getCachedSystemInfo.ts // Cached system info
│   │   ├── index_highway.ts // Agent-related requests
│   │   ├── panelAgent // Agent API module
│   │   │   ├── apis.ts // Agent API definitions
│   │   │   ├── index.ts // Agent API exports
│   │   │   ├── types.d.ts // Agent type definitions
│   │   │   ├── universal.ts // Common API helpers
│   │   │   ├── utils.ts // Agent utilities
│   │   ├── request.ts // Request helper
│   ├── components
│   │   ├── Avatar // Avatar component
│   │   ├── AvatarBar // Avatar bar
│   │   ├── Battery // Battery component
│   │   ├── BottomButton // Bottom button
│   │   ├── DialogConfirm // Confirmation dialog
│   │   ├── DialogInput // Text input dialog
│   │   ├── DialogPicker // DP picker dialog
│   │   ├── GridBattery // Battery grid
│   │   ├── icon-font // SVG icon container
│   │   ├── Modal // Generic modal
│   │   ├── NoData // Empty state component
│   │   ├── PickerItem // Generic picker item
│   │   ├── SearchBar // Search bar
│   │   ├── SubTopBar // Sub-page top bar
│   │   ├── Tag // Tag subcomponent
│   │   ├── TagBar // Tag bar
│   │   ├── Text // Text component
│   │   ├── TopBar // Top bar
│   │   ├── TouchableOpacity // Button component
│   │   ├── WifiSignal // Wi-Fi signal
│   │   ├── index.ts // Component exports
│   ├── constant
│   │   ├── dpCodes.ts // dpCode constants
│   │   ├── index.ts // Constant definitions
│   ├── devices // Device models
│   │   ├── index.ts // Device exports
│   │   ├── protocols // Device protocols
│   │   │   ├── index.ts // Protocol exports
│   │   ├── schema.ts // Schema definitions
│   ├── hooks // Hooks
│   │   ├── useAgentLanguages.ts // Agent language hook
│   │   ├── useChatEmotion.ts // Chat emotion hook
│   │   ├── usePanelConfig.ts // Panel config hook
│   │   ├── useSelectorWithEquality.ts // Redux selector hook
│   │   ├── useSystemInfo.tsx // System info hook
│   │   ├── useWakeWord.ts // Wake-word hook
│   ├── i18n // Internationalization
│   │   ├── index.ts // i18n exports
│   │   ├── strings.ts // Localized strings
│   ├── image // Image assets
│   │   ├── bg-light@3x.png // Background
│   ├── pages
│   │   ├── AvatarSelect // Avatar selection page
│   │   ├── CustomAgentEdit // Add/Edit role page
│   │   ├── DialogHistory // Chat history page
│   │   │   ├── DialogSingleContentNew // Conversation component
│   │   ├── HomeRole // Role homepage
│   │   │   ├── BaseInfoCard // Basic info card
│   │   │   ├── HomeTopBar // Homepage top bar
│   │   │   ├── RoleMemoryEntry // Memory entry
│   │   ├── RoleChange // Role switch page
│   │   │   ├── RoleItem // Role list item
│   │   ├── RoleMemory // Role memory page
│   │   │   ├── ChatSummary // Chat summary
│   │   │   ├── MemoryFormat // Memory format
│   │   ├── VoiceSquare // Voice square page
│   │   │   ├── SliderValueItem // Slider item
│   │   │   ├── VoiceItem // Voice item
│   ├── redux // Redux
│   │   ├── index.ts // Redux exports
│   │   ├── modules // Redux modules
│   ├── res // Assets & SVGs
│   │   ├── agent // Agent images
│   │   ├── iconfont // Icon font files
│   │   ├── signal // Signal icons
│   │   ├── iconsvg.ts // SVG definitions
│   │   ├── index.ts // Asset exports
│   ├── styles // Global styles
│   │   ├── index.less // Style entry
│   ├── types // Global types
│   │   ├── index.ts // Type exports
│   ├── utils // Utility helpers
│   │   ├── index.ts // Utility exports
│   │   ├── string.js // String helpers
│   │   ├── time.ts // Time helpers
│   ├── app.config.ts // App config
│   ├── app.less // App styles
│   ├── app.tsx // App entry
│   ├── composeLayout.tsx // Handle sub-device events and DP changes
│   ├── global.config.ts // Global config
│   ├── global.less // Global styles
│   ├── mixins.less // Less mixins
│   ├── routes.config.ts // Routes
│   ├── theme.json // Theme config
│   ├── variables.less // Less variables

Get the currently bound role

Feature description

Call the getAIAgentRoles API to obtain the role currently bound to the device.

Code snippet

// Request the currently bound role
const getBindAgentRoleData = async () => {
  getBindAgentRoleLocal()
    .then((res: any) => {
      dispatch(initBindAgentRole(res));
      if (res?.bindRoleType === 1) {
        dispatch(updateRoleInfo({ roleTemplateId: res?.templateId }));
      }
      // bindRoleType indicates whether multi-role is supported:
      // 0 custom, 1 template, 2 single-role only
      dispatch(initSupportMultiRole(res?.bindRoleType !== 2));
      initLoading && setInitLoading(false);
      setIsUnconfigured(false);
    })
    .catch((err) => {
      console.log("getBindAgentRoleLocal::err::", err);
      setInitLoading(false);
      setIsUnconfigured(true);
    });
};

Get the latest emotion

Feature description

By listening for device messages, you can keep track of the emotional state of the current role.

Code snippet

useEffect(() => {
  registerMQTTDeviceListener({ deviceId: getDevInfo().devId });

  const handleCallback = async ({ messageData }) => {
    try {
      const { bizType, data } = messageData;
      const { code, custom } = data;
      if (bizType === "SKILL" && code === "emo") {
        setEmojiUrl(custom?.url);

        // Refresh the chat info at the top
        getBindAgentRoleData();
        setChatEmotion((state) => ({
          ...state,
          disableTime: true,
        }));
      }
    } catch (err) {
      console.log(err);
    }
  };

  onMqttMessageReceived(handleCallback);

  return () => {
    console.log("============================");
    unregisterMQTTDeviceListener({ deviceId: getDevInfo().devId });
    offMqttMessageReceived(handleCallback);
  };
}, []);

Get the role lists

Feature description

Retrieve both the recommended roles and the custom roles.

Code snippet

const getCustomRoleFunc = (params: GetListParams) => {
  return new Promise((resolve, reject) => {
    getCustomRoleListLocal({})
      .then((res: VoiceRes) => {
        const { totalPage, list = [] } = res;
        // Avoid flashing "no role"
        setTimeout(() => {
          setLoading(false);
        }, 200);
        resolve({
          total: totalPage,
          list,
        });
      })
      .catch((error) => {
        setLoading(false);
        reject(error);
      });
  });
};

const initRoleList = () => {
  getRecommendRoleListLocal()
    .then((res) => {
      setResList(res);
      setTimeout(() => {
        setLoading(false);
      }, 200);
    })
    .catch((error) => {
      setLoading(false);
    });
};

Switch the bound role

Feature description

Tap an item in the list to switch the current role.

Code snippet

const handleItemChecked = async (roleId: string, item: any) => {
  // bindRoleType: 0 custom, 1 template, 2 single-role
  const params = { roleId, bindRoleType: tag === "custom" ? 0 : 1 };
  postBindRoleLocal(params)
    .then(() => {
      dispatch(updateRoleInfo({ roleId }));
      // Refresh the homepage after switching
      emitter.emit("refreshDialogData", "");
      navigateBack({ delta: 1 });
    })
    .catch((error) => {
      console.log(error);
    });
};

Delete a custom role

Code snippet

const deleteAIRole = (roleId: string) => {
  showLoading({
    title: "",
  });
  deleteAIAgentRoles(roleId)
    .then(() => {
      hideLoading();
      showToast({
        title: Strings.getLang("dsc_delete_success"),
        icon: "success",
      });
      // Refresh the custom list after deletion
      initData("");
    })
    .catch((err) => {
      hideLoading();
      console.log("deleteAIAgentRoles::err::", err);
      showToast({
        title: Strings.getLang("dsc_delete_fail"),
        icon: "error",
      });
    });
  setUnbindId("-1");
  setIsShowUnbindConfirm(false);
};

Page description

Use this page to update role information (avatar, name, introduction, and memory), and select the timbre, language, and large model.

Get role templates and details

Feature description

You can pre-configure templates for B-end users, so that they can quickly pick a baseline role.

Code snippet

// Get the role template list
const getTemplateList = async () => {
  if (source === "createRole") {
    return;
  }
  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 template details
const getTemplateListDetail = async (
  roleId: string,
  onlyReduxUpdate?: boolean
) => {
  if (CustomAgentEditForbiddenGetDetail.current) return;
  getCommonRoleDetailLocal(roleId)
    .then((res: any) => {
      console.log("==getTemplateListDetail", res);
      if (onlyReduxUpdate) {
        dispatch(initRoleDetail(res));
      } else {
        initRoleData(res);
        initRoleInfoData();
      }
    })
    .catch((err) => {
      console.log("getTemplateListDetail::err::", err, roleId);
    });
};

Get role details

Feature description

When editing a role, retrieve its full details.

Code snippet

const getAgentRoleDetail = async (
  roleId: string,
  onlyReduxUpdate?: boolean
) => {
  if (CustomAgentEditForbiddenGetDetail.current) return;
  getCustomRoleDetailLocal(roleId)
    .then((res: any) => {
      console.log("==getCustomRoleDetailLocal", res);
      if (onlyReduxUpdate) {
        dispatch(initRoleDetail(res));
      } else {
        initRoleData(res);
        initRoleInfoData();
      }
    })
    .catch((err) => {
      console.log("getCustomRoleDetailLocal::err::", err);
    });
};

Create or edit roles

Feature description

Handle the logic for creating or editing roles.

Code snippet

// After creating or editing a role, refresh the custom list

/* Editing flows:
 * 1. Custom roles: updateCustomRole -> refresh list ✅
 * 2. Recommended roles: createFromTemplate -> jump to custom tab ✅
 * From homepage:
 * 1. Custom roles: update -> refresh homepage ✅
 * 2. Recommended roles: create from template -> jump to custom tab ✅
 * Creating roles:
 * 1. Default values are pre-filled ✅
 * 2. Tag determines whether to call template or custom API ✅
 */
try {
  showLoading({
    title: "",
  });
  setLoading(true);
  if (roleId) {
    params.roleId = roleId;
    params.speed = roleDetail?.speed || 33;
    params.tone = roleDetail?.tone || 33;
    console.log("roleDetail::1", roleDetail);
    await postUpdateCustomRoleLocal(params);
    setTimeout(() => {
      emitter.emit("refreshRoleList", "");
    }, 500);
  } else if (templateRoleId && source !== "createRole") {
    params.roleId = templateRoleId;
    params.speed = roleDetail?.speed || 33;
    params.tone = roleDetail?.tone || 33;
    console.log("roleDetail::2", roleDetail);
    if (needBind === "true") {
      params.needBind = true;
    }
    await postCreateRoleFromTemplateLocal(params);
    setTimeout(() => {
      emitter.emit("refreshRoleList", { toCustomTab: true });
    }, 500);
  } else if (source === "createRole") {
    params.speed = roleDetail?.speed || 33;
    params.tone = roleDetail?.tone || 33;
    console.log("roleDetail::3", roleDetail);
    await postCreateCustomRoleLocal(params);
    setTimeout(() => {
      emitter.emit("refreshRoleList", { toCustomTab: true });
    }, 500);
  }

  showToast({
    title: Strings.getLang("save_success"),
    icon: "success",
  });
  // Refresh the homepage after editing from the homepage
  emitter.emit("refreshDialogData", "");
  navigateBack({ delta: 1 });
  setTimeout(() => {
    hideLoading();
    setLoading(false);
  }, 500);
} catch (error) {
  hideLoading();
  setLoading(false);
  if (platform === "android") {
    showToast({
      title: error?.innerError?.errorMsg,
      icon: "error",
    });
  } else if (platform === "ios") {
    showToast({
      title: iOSExtractErrorMessage(error?.innerError?.errorMsg),
      icon: "error",
    });
  }
}

Get role avatars

Feature description

Provide pre-configured avatars for users to choose from.

Code snippet

// Avatars
const getBoundAgentsFunc = async () => {
  getAvatarListLocal()
    .then((res) => {
      console.log("getAIAvatars::", res);
      setAvatarList(res);
    })
    .catch((err) => {
      console.log("getAIAvatars::err::", err);
    });
};

useEffect(() => {
  getBoundAgentsFunc();
}, []);

Get supported languages

Feature description

Fetch the supported languages.

Code snippet

// Get supported languages
const useAgentLanguages = (id: number) => {
  const [langRangeList, setSupportLangs] = useState<Array<{ key: string; dataString: string }>>([]);

  useEffect(() => {
    const fetchSupportedLangs = async () => {
      try {
        const response = await getLanguageListLocal();
        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 large language models

Feature description

Retrieve the list of large language models.

Code snippet

// Display the latest model list
const response = await getModelListLocal();
let models = [];
if (response) {
  models = response?.map((model) => ({
    key: model.llmId,
    dataString: model.llmName,
  }));
}

Page description

Switch the timbre associated with the agent role and manage the system-default timbres.

Timbre square list

Feature description

Display the default timbres and allow end users to pick the best match. Filtering by category and searching are supported.

Code snippet

// Request the timbre list
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);
      });
  });
};
// Manage the pagination requests
const { pagination, data, run } = usePagination(
  ({ current, pageSize, searchText }) =>
    getVoiceListFunc({
      current,
      pageSize,
      searchText,
    }) as Promise<GetStandardVoice>,
  {
    manual: true,
  }
);
// Lazy load updates
useEffect(() => {
  if (data?.list) {
    pagination.current > 1
      ? dispatch(updateVoiceList([...data?.list]))
      : dispatch(initVoiceList([...data?.list]));
  }
}, [data]);

Search timbres

Code snippet

// Keep the same request flow; searchText determines whether it's a search
const { pagination, data, run } = usePagination(
  ({ current, pageSize, searchText }) =>
    getVoiceListFunc({ current, pageSize, searchText }) as Promise<GetStandardVoice>,
  {
    manual: true,
  }
);

Switch timbres

Code snippet

// Switch the timbre when editing role details
const handleItemChecked = async (idKey: string, item: any) => {
  try {

    playVoiceAudio(item?.demoUrl);

    dispatch(
      updateRoleInfo({
        voiceId: idKey,
        voiceName: item?.voiceName,
        supportLangs: item?.supportLangs,
      })
    );
    // When creating, block template detail updates triggered by roleDetail changes
    emitter.emit("CustomAgentEditForbiddenGetDetail", true);
    setTimeout(() => {
      dispatch(initRoleDetail({ ...roleDetail, speed: item?.speed, tone: item?.tone }));
    }, 50);

  } catch (err) {
    hideLoading();
  }
};

Edit the speaking speed

Code snippet

// Edit the speech speed in role details
const onChangeToneOrSpeed = (sv: number, tv: number) => {
  if (!isEditMode) {
    // Prevent template detail updates triggered by roleDetail changes
    emitter.emit("CustomAgentEditForbiddenGetDetail", true);
    setTimeout(() => {
      dispatch(initRoleDetail({ ...roleDetail, speed: sv }));
    }, 50);
    return;
  }
  showLoading({
    title: "",
  });
  postUpdateCustomRoleLocal({
    roleId,
    [speedKey]: `${sv}`,
    // [toneKey]: `${tv}`,
  })
    .then(async (res) => {
      sv >= 0 && dispatch(initRoleDetail({ ...roleDetail, speed: 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",
      });
    });
};

Page description

Display the conversations between the end user and the agent role. If there is no conversation yet, the template shows an empty placeholder.

Code snippet

// Fetch the chat history
const fetchHistoryData = useCallback(
  async (gmtEnd: number, isRefresh = false) => {
    if ((loadingUpper && !isRefresh) || (loadingLower && isRefresh)) return;

    try {
      if (isRefresh) {
        setLoadingLower(true);
      } else {
        setLoadingUpper(true);
      }

      const response = await fetchPanelAgentChatHistory({
        devId: getDevInfo().devId,
        bindRoleType: bindAgentRole?.bindRoleType,
        roleId: bindAgentRole?.roleId,
        gmtEnd,
        fetchSize: 10,
        timeAsc: false,
      });

      setRefresherTriggeredState(false);

      if (response && response.length > 0) {
        // Find the oldest timestamp
        const oldestTime = Math.min(
          ...response.map((item) => item.gmtCreate)
        );
        oldestGmtCreate.current = oldestTime;

        // Add parsedTime for display
        const processedList = response
          .map((item) => ({
            ...item,
            parsedTime: formatTimeByTimezone(item.gmtCreate, timezoneId),
          }))
          .filter((item) => item.parsedTime !== null);

        if (isRefresh) {
          // Append to the end (latest data) and sort chronologically
          setHistoryList((prev) => {
            const combined = [...prev, ...processedList];
            const uniqList = _.uniqBy(combined, "requestId");
            const sortedList = uniqList.sort(
              (a, b) => a.gmtCreate - b.gmtCreate
            );
            if (sortedList.length > 0) {
              oldestGmtCreate.current = sortedList[0].gmtCreate;
            }
            return sortedList;
          });
        } else {
          // Prepend older data when loading more
          setHistoryList((prev) => {
            const combined = [...processedList, ...prev];
            const uniqList = _.uniqBy(combined, "requestId");
            return uniqList.sort((a, b) => a.gmtCreate - b.gmtCreate);
          });
        }

        setTimeout(() => {
          setEmptyIdState("bottomView");
        }, 500);
      } else {
        // No more data
      }
    } catch (error: any) {
      setRefresherTriggeredState(false);
      if (platform === "android") {
        showToast({
          title: error?.innerError?.errorMsg || error?.errorMsg,
          icon: "error",
        });
      } else if (platform === "ios") {
        showToast({
          title: error?.innerError?.errorMsg
            ? iOSExtractErrorMessage(error?.innerError?.errorMsg)
            : error?.errorMsg,
          icon: "error",
        });
      }
    } finally {
      setLoadingUpper(false);
      setLoadingLower(false);
    }
  },
  [loadingUpper, loadingLower, bindAgentRole, platform, timezoneId]
);

Delete the context

Feature description

Delete one or more chat messages for the role.

Code snippet

// clearAgentHistoryMessage
const handleDeleteMessage = async (requestIds?: string) => {
  try {
    showLoading({ title: "" });
    await clearAgentHistoryMessage({
      bindRoleType: bindAgentRole?.bindRoleType,
      roleId: bindAgentRole?.roleId,
      requestIds: requestIds || selectedItems?.join(","),
      clearAllHistory: false,
    });

    const newList = historyList.filter(
      (item) =>
        !selectedItems.includes(item.requestId) &&
        item.requestId !== requestIds
    );
    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",
    });
  }
};

Page description

Manage role memories, including clearing chat history, removing temporary memories, structured memories, conversation summaries, and long-term memories.

Clear chat history

Code snippet

const handleClickDialog = (message: string, type: string) => {
  DialogInstance.confirm({
    selector: `#smart-dialog-edit`,
    message,
    cancelButtonText: Strings.getLang("cancel"),
    confirmButtonText: Strings.getLang("confirm"),
  })
    .then(() => {
      // Clear chat history
      if (type === "clear_chat_history_all") {
        clearingHistoryRecord({
          bindRoleType:
            roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
          roleId: roleDetail?.roleId || bindAgentRole?.roleId,
          clearAllHistory: true,
        })
          .then(() => {
            showToast({
              title: Strings.getLang("dsc_delete_chat_history_tip"),
              icon: "success",
            });
          })
          .catch(() => {
            showToast({
              title: Strings.getLang("dsc_delete_chat_history_failed"),
              icon: "error",
            });
          });
      }
      // Clear temporary context
      if (type === "clear_context") {
        clearingContext({
          bindRoleType:
            roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
          roleId: roleDetail?.roleId || bindAgentRole?.roleId,
        })
          .then(() => {
            showToast({
              title: Strings.getLang("clear_context_success"),
              icon: "success",
            });
          })
          .catch(() => {
            showToast({
              title: Strings.getLang("clear_context_failed"),
              icon: "error",
            });
          });
      }
      // Clear all long-term memories
      if (type === "clear_memory") {
        clearingMemory({
          bindRoleType:
            roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
          roleId: roleDetail?.roleId || bindAgentRole?.roleId,
          clearAllMemory: true,
        })
          .then(() => {
            showToast({
              title: Strings.getLang("clear_memory_success"),
              icon: "success",
            });
          })
          .catch(() => {
            showToast({
              title: Strings.getLang("clear_memory_failed"),
              icon: "error",
            });
          });
      }
    })
    .catch(() => {
      console.log("cancel");
    });
};


Structured memories

Feature description

Display role memories and shared household memories, and allow deletions.

Code snippet

// Fetch memories
const getPanelMemoryListData = async () => {
  const params = {
    bindRoleType:
      roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
    roleId: roleDetail?.roleId || bindAgentRole?.roleId,
  };
  getPanelMemoryListLocal(params)
    .then((res: any) => {
      setMemoryList(res);
    })
    .catch((err) => {
      console.log("getMemorySwitchLocal::err::", err);
    });
};
// Delete memories
const handleDeleteMemory = (memoryKey) => {
  clearingMemory({
    bindRoleType:
      roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
    roleId: roleDetail?.roleId || bindAgentRole?.roleId,
    memoryKeys: memoryKey,
    clearAllMemory: false,
  })
    .then(() => {
      // Refresh after deletion
      getPanelMemoryListData();
    })
    .catch((error) => {
      showToast({
        title: error?.innerError?.errorMsg,
        icon: "error",
      });
    });
};

Conversation summaries

Feature description

Show and delete conversation summaries.

Code snippet

// Fetch conversation summaries
const getChatMemoryListData = async () => {
  const params = {
    bindRoleType:
      roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
    roleId: roleDetail?.roleId || bindAgentRole?.roleId,
  };
  getChatMemoryListLocal(params)
    .then((res: any) => {
      setSummaryList(res);
    })
    .catch((err) => {
      console.log("getMemorySwitchLocal::err::", err);
    });
};
// Delete conversation summaries
const handleDeleteMemory = (memoryKey) => {
  const leftMemoryList = summaryList?.filter((item) => item !== memoryKey);
  const params = {
    bindRoleType:
      roleMemoryEntry === "custom" ? 0 : bindAgentRole?.bindRoleType,
    roleId: roleDetail?.roleId || bindAgentRole?.roleId,
    summaryItems: leftMemoryList,
  };

  postUpdateChatMemoryListLocal(params).then(() => {
    // Refresh after deletion
    getChatMemoryListData();
  });
};