diff --git a/CHANGELOG.md b/CHANGELOG.md index ef45b60..0d1d688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,18 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [1.8.2](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.8.1...v1.8.2) (2025-04-09) +## 1.9.0 (2025-04-18) + +### Features + +* 支持多会话 + +### Bug Fixes + +* 修复系统提示语在切会话时插入位置bug ([7c3e77a](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/7c3e77a2b13b5a33c18e0821fc4eb23937ebdfd5)) +* 支持request 域名校验 ([5c21561](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/5c21561ffc06523bbcb7fcb3b91b17f492ab4b8f)) +### [1.8.2](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.8.1...v1.8.2) (2025-04-09) ### Bug Fixes @@ -13,7 +23,6 @@ All notable changes to this project will be documented in this file. See [standa ### [1.8.1](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.8.0...v1.8.1) (2025-04-08) - ### Bug Fixes * 3.8.1版本切换兼容 ([190a59f](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/190a59f87d3323cb971b7627eb1806e6493651d6)) @@ -25,7 +34,6 @@ All notable changes to this project will be documented in this file. See [standa ## [1.8.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.7.0...v1.8.0) (2025-03-31) - ### Features * 环境共享迁移完成 ([16f48cb](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/16f48cbbf96b6d8ea9ae0eee4c7b6b374af330df)) @@ -33,7 +41,6 @@ All notable changes to this project will be documented in this file. See [standa * 添加环境共享参数 ([0946290](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/0946290c6e3c90acdd93776556f1544f3a52fde6)) * support mcp ([f6b6663](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/f6b66636135d84a77dfa48ddf2c03ec7f8388491)) - ### Bug Fixes * 继续优化scroll-view 高度设置 ([b520964](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/b520964192f15c70715bc07850f54fb59453857c)) @@ -47,7 +54,6 @@ All notable changes to this project will be documented in this file. See [standa ## [1.7.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.6.0...v1.7.0) (2025-03-21) - ### Features * 添加复制 ([69c4aad](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/69c4aad44de2994aadeaff20cb3c5bd86eeff4bb)) @@ -56,7 +62,6 @@ All notable changes to this project will be documented in this file. See [standa * 修改文档 ([0e642e5](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/0e642e59139043a592f7b4dd1d0ea406fbb36737)) * support upload img ([f2fcc00](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/f2fcc00cc60c7717dc9035ea1e3a426c050262e0)) - ### Bug Fixes * fix readme ([8e959db](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/8e959db57f890765437e6ae6ba48105bbd872964)) @@ -65,7 +70,6 @@ All notable changes to this project will be documented in this file. See [standa ## [1.6.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.8...v1.6.0) (2025-03-18) - ### Features * 修改参数 ([9e2a868](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/9e2a8683716958ba3c0f10d06d0482f854c514b1)) @@ -73,7 +77,6 @@ All notable changes to this project will be documented in this file. See [standa * support agent uploadfile config ([d7006a2](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/d7006a2827489938411ca6e5b5244c8019e85ae7)) * support file in history ([bbe9810](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/bbe9810b6b3c8d68a6a7a7d6549bc7730530cd16)) - ### Bug Fixes * 针对企业微信环境判断处理 ([cffabc4](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/cffabc4fccbcbf038eed8659d53920c9c04891a5)) @@ -81,21 +84,18 @@ All notable changes to this project will be documented in this file. See [standa ## [1.5.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.8...v1.5.0) (2025-03-14) - ### Features * 支持数据模型检索 ([be5ed84](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/be5ed8419b9be0ba2284ae5017f2ad35995e8b4e)) * support agent uploadfile config ([a249866](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/a249866aaafe1e744d43c2ccdbd83856c72d1eb2)) * support file in history ([51d3ef0](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/51d3ef0f4cf45bc12a7b62cb1a887e834a948be5)) - ### Bug Fixes * fix readme ([42b5830](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/42b5830de3e885a3adf6ab658f785c496158cd5c)) ### [1.4.8](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.7...v1.4.8) (2025-03-10) - ### Bug Fixes * change cos upload ([d5709df](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/d5709df55a37b203d82fca95a9388d2d896cd500)) @@ -105,35 +105,30 @@ All notable changes to this project will be documented in this file. See [standa ### [1.4.7](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.6...v1.4.7) (2025-03-10) - ### Bug Fixes * fix workflow ([eaed4f2](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/eaed4f28a10d9eecd4d83c8f25d731442851ab52)) ### [1.4.6](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.5...v1.4.6) (2025-03-10) - ### Bug Fixes * fix workflow ([a526943](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/a5269433be9a3106255bb19b5a6d2ea329ec33f5)) ### [1.4.5](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.4...v1.4.5) (2025-03-10) - ### Bug Fixes * fix workflow ([38969a5](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/38969a598b35ca4408e79ce98d53ae5780efe32b)) ### [1.4.4](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.3...v1.4.4) (2025-03-10) - ### Bug Fixes * fix workflow ([2013490](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/201349079931e3c4fd4d102c189e3d63d41ebf66)) ### [1.4.3](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.2...v1.4.3) (2025-03-10) - ### Bug Fixes * fix workflow ([7697bc4](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/7697bc40b4f00c825bdae0f421381fcfd49923c3)) @@ -142,19 +137,16 @@ All notable changes to this project will be documented in this file. See [standa ### [1.4.1](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.4.0...v1.4.1) (2025-03-10) - ### Bug Fixes * fix scripts ([271354d](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/271354d5ffafbfd8451ed763a4a64d851a0d9f7d)) ## 1.4.0 (2025-03-10) - ### ✨ Features | 新功能 * 优化工作流 ([ee33c15](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/ee33c155fe013b7e8342fd42785aa1920da58dd5)) - ### 📦 Chores | 其他更新 * **release:** 1.0.1 ([5216dc2](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/5216dc2997e7d7edfbe18b6b51df8fdd68c71637)) @@ -169,7 +161,6 @@ All notable changes to this project will be documented in this file. See [standa * sync agent-ui component before release ([0d24ce6](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/0d24ce6ec687f5dc5941ec18de6109c2abda9273)) * sync agent-ui component before release ([43b22b6](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/43b22b677860115841400cba0c9fd0e4db2afdb0)) - ### 🐛 Bug Fixes | Bug 修复 * 优化发布流程 ([e57ba51](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/e57ba51c751959976ea88a185bec8ac1ce2ae12b)) diff --git a/README.md b/README.md index 3bc1786..081d6f1 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Page({ allowUploadFile: true, // 允许上传文件 allowPullRefresh: true, // 允许下拉刷新 allowUploadImage: true, // 允许上传图片 + allowMultiConversation: true, // 允许客户端界面展示会话列表及新建会话按钮 + showToolCallDetail: true, // 允许展示 mcp server toolcall 细节 }, modelConfig: { modelProvider: "hunyuan-open", // 大模型服务厂商 @@ -147,13 +149,15 @@ Page({ #### AgentConfig -| 参数 | 类型 | 必填 | 说明 | -| -------------------- | ----------- | ---- | --------------------------------------------- | -| `botId` | `String` | 否 | Agent的唯一标识ID,当 chatMode = 'bot' 时必填 | -| `allowWebSearch` | `Boolean` | 否 | 是否允许客户端界面展示联网搜索 | -| `allowUploadFile` | `Boolean` | 否 | 是否允许客户端界面展示文件上传 | -| `allowPullRefresh` | `Boolean` | 否 | 是否允许客户端界面展示下拉获取历史记录 | -| `allowUploadImage` | `Boolean` | 否 | 是否允许客户端界面展示图片上传及拍照上传 | +| 参数 | 类型 | 必填 | 说明 | +| -------------------------- | ----------- | ---- | --------------------------------------------- | +| `botId` | `String` | 否 | Agent的唯一标识ID,当 chatMode = 'bot' 时必填 | +| `allowWebSearch` | `Boolean` | 否 | 是否允许客户端界面展示联网搜索 | +| `allowUploadFile` | `Boolean` | 否 | 是否允许客户端界面展示文件上传 | +| `allowPullRefresh` | `Boolean` | 否 | 是否允许客户端界面展示下拉获取历史记录 | +| `allowUploadImage` | `Boolean` | 否 | 是否允许客户端界面展示图片上传及拍照上传 | +| `allowMultiConversation` | `Boolean` | 否 | 是否允许客户端界面展示会话列表及新建会话按钮 | +| `showToolCallDetail` | `Boolean` | 否 | 是否允许展示 mcp server toolcall 细节 | #### ModelConfig @@ -164,11 +168,16 @@ Page({ | `logo` | `String` | 否 | 页面 logo,当 chatMode = 'model' 时生效,选填 | | `welcomeMsg` | `String` | 否 | 欢迎语,当 chatMode = 'model' 时生效,选填 | -> [!IMPORTANT] +> **上传文件限制** > 大小限制:单文件不超过10M > 数量限制:单次最多支持 5 个文件 > 文件类型:pdf、txt、doc、docx、ppt、pptx、xls、xlsx、csv -> **request合法域名配置**:微信小程序上传文件需要添加“文件上传接口”到request合法域名列表,文件上传域名为:https://{your-envid}.api.tcloudbasegateway.com, 可前往[微信公众平台](https://mp.weixin.qq.com)配置request合法域名 + +> **上传图片限制** +> 大小限制:单文件不超过30M +> 数量限制:单次最多支持 1 个文件 + +> **request合法域名配置**:微信小程序 Agent-UI 组件支持 上传文件&多会话 需要添加云开发域名到request合法域名列表,云开发域名为:https://{your-envid}.api.tcloudbasegateway.com, 可前往[微信公众平台](https://mp.weixin.qq.com)配置request合法域名 配置示例 @@ -255,6 +264,8 @@ Page({ allowUploadFile: true, // 允许上传文件 allowPullRefresh: true, // 允许下拉刷新 allowUploadImage: true, // 允许上传图片及拍照上传 + allowMultiConversation: true, // 允许客户端界面展示会话列表及新建会话按钮 + showToolCallDetail: true, // 允许展示 mcp server toolcall 细节 } } //... @@ -270,16 +281,17 @@ Page({ - ✅ 流式输出 - ✅ 联网搜索 (Agent模式) - ✅ 文档解析 (Agent模式) -- ✅ 图片上传解析(拍照/图片) +- ✅ 图片上传解析(拍照/图片)(Agent模式) +- ✅ 支持环境共享下使用 +- ✅ 历史会话管理,多轮对话上下文记忆(Agent模式) +- ✅ 支持 MCP 调用(Agent模式) ### 🚧 进行中开发 - 多模型(快速响应/深度推理)切换调用配置化 -- 历史会话管理,多轮对话上下文记忆 - 支持文字转语音播放 - 支持用户语音输入转文字 - 支持语音音色配置 -- agent支持环境共享下使用 ### 📅 未来计划 diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/chatFile/index.js b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/chatFile/index.js index 456d4d8..3d3d7ab 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/chatFile/index.js +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/chatFile/index.js @@ -35,64 +35,31 @@ Component({ const appBaseInfo = wx.getAppBaseInfo(); const fileId = res.fileID; this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, status: "parsing" }); - console.log("当前版本", appBaseInfo.SDKVersion); - // 3.8.1 及以上版本走sdk 内置方法 - if (compareVersions(appBaseInfo.SDKVersion, "3.8.1") < 0) { - const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); - commonRequest({ - url: `https://${ - cloudInstance.env || cloudInstance.extend.AI.bot.context.env - }.api.tcloudbasegateway.com/v1/aibot/bots/${botId}/files`, - data: { - fileList: [ - { - fileName: rawFileName || tempFileName, - fileId, - type: rawType, - }, - ], - }, - header: { - Authorization: `Bearer ${token}`, - }, - method: "POST", - success: (res) => { - console.log("old resolve agent file res", res); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); - }, - fail: (e) => { - console.log("resolve agent file e", e); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); - }, - }); - } else { - const ai = cloudInstance.extend.AI; - ai.request({ - path: `bots/${botId}/files`, // 填写 "v1/aibot/" 后面的内容 - data: { - fileList: [ - { - fileName: rawFileName || tempFileName, - fileId, - type: rawType, - }, - ], - }, // any - method: "POST", - timeout: 30000, - success: (res) => { - console.log("resolve agent file res", res); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); - }, - fail: (e) => { - console.log("e", e); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); - }, - complete: () => {}, - header: {}, - }); - } + commonRequest({ + path: `bots/${botId}/files`, + data: { + fileList: [ + { + fileName: rawFileName || tempFileName, + fileId, + type: rawType, + }, + ], + }, // any + method: "POST", + timeout: 60000, + success: (res) => { + console.log("resolve agent file res", res); + this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); + }, + fail: (e) => { + console.log("e", e); + this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); + }, + complete: () => {}, + header: {}, + }) }, fail: (err) => { console.error("上传失败:", err); diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/indent-left.svg b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/indent-left.svg index db9c0c2..c83d7a3 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/indent-left.svg +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/indent-left.svg @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js index ba7c215..a89f554 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js @@ -1,5 +1,5 @@ // components/agent-ui/index.js -import { checkConfig, randomSelectInitquestion, getCloudInstance } from "./tools"; +import { checkConfig, randomSelectInitquestion, getCloudInstance, commonRequest } from "./tools"; import md5 from "./md5.js"; Component({ properties: { @@ -30,6 +30,7 @@ Component({ allowWebSearch: Boolean, allowPullRefresh: Boolean, allowUploadImage: Boolean, + allowMultiConversation: Boolean, showToolCallDetail: Boolean, }, }, @@ -80,6 +81,7 @@ Component({ showWebSearchSwitch: false, showPullRefresh: true, showToolCallDetail: true, + showMultiConversation: true, useWebSearch: false, showFeatureList: false, chatStatus: 0, // 页面状态: 0-正常状态,可输入,可发送, 1-发送中 2-思考中 3-输出content中 @@ -96,6 +98,17 @@ Component({ textareaHeight: 50, defaultErrorMsg: "网络繁忙,请稍后重试!", curScrollHeight: 0, + isDrawerShow: false, + conversations: [], + transformConversations: {}, + conversationPageOptions: { + page: 1, + size: 15, + total: 0, + }, + conversation: null, + defaultConversation: null, // 旧结构默认会话 + fetchConversationLoading: false, }, attached: async function () { const chatMode = this.data.chatMode; @@ -133,24 +146,39 @@ Component({ const { chatRecords } = this.data; // 随机选取三个初始化问题 const questions = randomSelectInitquestion(bot.initQuestions, 3); - let { allowWebSearch, allowUploadFile, allowPullRefresh, allowUploadImage, showToolCallDetail } = - this.data.agentConfig; - // console.log("allowWebSearch", allowWebSearch); + let { + allowWebSearch, + allowUploadFile, + allowPullRefresh, + allowUploadImage, + showToolCallDetail, + allowMultiConversation, + } = this.data.agentConfig; + console.log("allowWebSearch", allowWebSearch); allowWebSearch = allowWebSearch === undefined ? true : allowWebSearch; allowUploadFile = allowUploadFile === undefined ? true : allowUploadFile; allowPullRefresh = allowPullRefresh === undefined ? true : allowPullRefresh; allowUploadImage = allowUploadImage === undefined ? true : allowUploadImage; showToolCallDetail = showToolCallDetail === undefined ? true : showToolCallDetail; + allowMultiConversation = allowMultiConversation === undefined ? true : allowMultiConversation; this.setData({ bot, questions, - chatRecords: [...chatRecords, record], + chatRecords: chatRecords.length > 0 ? chatRecords : [record], showWebSearchSwitch: allowWebSearch, showUploadFile: allowUploadFile, showUploadImg: allowUploadImage, showPullRefresh: allowPullRefresh, showToolCallDetail: showToolCallDetail, + showMultiConversation: allowMultiConversation, }); + console.log("bot", this.data.bot); + if (chatMode === "bot" && this.data.bot.multiConversationEnable) { + // 拉一次默认旧会话 + await this.fetchDefaultConversationList(); + // 拉一遍新会话列表 + await this.resetFetchConversationList(); + } } const topHeight = await this.calculateContentInTop(); // console.log('topHeight', topHeight) @@ -209,6 +237,263 @@ Component({ } return ""; }, + handleClickConversation: async function (e) { + // 清除旧的会话聊天记录 + this.clearChatRecords(); + const { conversation } = e.currentTarget.dataset; + this.setData({ + isDrawerShow: false, + conversation: { + conversationId: conversation.conversationId, + title: conversation.title, + }, + page: 1, // 重置历史记录分页参数 + size: 10, + }); + this.handleRefresh(); + // // 拉取当前会话聊天记录 + // const res = await wx.cloud.extend.AI.bot.getChatRecords({ + // botId: this.data.agentConfig.botId, + // pageNumber: this.data.page, + // pageSize: this.data.size, + // sort: "desc", + // conversationId: this.data.conversation?.conversationId || undefined, + // }); + // if (res.recordList) { + // } + }, + fetchDefaultConversationList: async function () { + try { + if (this.data.bot.botId) { + const res = await this.fetchConversationList(true, this.data.bot.botId); + if (res) { + const { data } = res; + console.log("data default", data.code); + if (data && !data.code) { + console.log("data", data); + this.setData({ + defaultConversation: data, + conversations: [data], + transformConversations: this.transformConversationList([data]), + // conversationPageOptions: { + // ...this.data.conversationPageOptions, + // total: data.total, + // }, + }); + } + } + } + } catch (e) { + console.log("fetchDefaultConversationList e", e); + } + }, + fetchConversationList: async function (isDefault, botId) { + // const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + if (this.data.fetchConversationLoading) { + return; + } + + return new Promise((resolve, reject) => { + const { page, size } = this.data.conversationPageOptions; + const limit = size; + const offset = (page - 1) * size; + this.setData({ + fetchConversationLoading: true, + }); + + commonRequest({ + path: `conversation/?botId=${botId}&limit=${limit}&offset=${offset}&isDefault=${isDefault}`, + method: "GET", + header: {}, + success: (res) => { + console.log("conversation list res", res); + resolve(res); + }, + fail(e) { + console.log("conversation list e", e); + reject(e); + }, + complete: () => { + this.setData({ + fetchConversationLoading: false, + }); + // wx.hideLoading(); + }, + }); + }); + }, + createConversation: async function () { + // const cloudInstance = await getCloudInstance(); + // const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + return new Promise((resolve, reject) => { + commonRequest({ + path: `conversation`, + header: { + // Authorization: `Bearer ${token}`, + }, + data: { + botId: this.data.agentConfig.botId, + }, + method: "POST", + success: (res) => { + console.log("create conversation res", res); + resolve(res); + }, + fail(e) { + console.log("create conversation e", e); + reject(e); + }, + }); + }); + }, + clickCreateInDrawer: function () { + this.setData({ + isDrawerShow: false, + }); + this.createNewConversation(); + }, + createNewConversation: async function () { + if (!this.data.bot.multiConversationEnable) { + wx.showModal({ + title: "提示", + content: "请前往腾讯云开发平台启用 Agent 多会话模式", + }); + return; + } + // // TODO: 创建新对话 + // const { data } = await this.createConversation(); + // console.log("createRes", data); + this.clearChatRecords(); + // this.setData({ + // conversation: { + // conversationId: data.conversationId, + // title: data.title, + // }, + // }); + this.setData({ + refreshText: "下拉加载历史记录", + }); + }, + scrollConToBottom: async function (e) { + console.log("scrollConToBottom", e); + const { page, size } = this.data.conversationPageOptions; + if (page * size >= this.data.conversationPageOptions.total) { + return; + } + this.setData({ + conversationPageOptions: { + ...this.data.conversationPageOptions, + page: this.data.conversationPageOptions.page + 1, + }, + }); + console.log("conversationPageOptions", this.data.conversationPageOptions); + // 调用分页接口查询更多 + if (this.data.bot.botId) { + const res = await this.fetchConversationList(false, this.data.bot.botId); + if (res) { + const { data } = res; + if (data && !data.code) { + const addConversations = [...this.data.conversations, ...data.data]; + // TODO: 临时倒序处理 + const sortConData = addConversations.sort( + (a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime() + ); + this.setData({ + conversations: sortConData, + transformConversations: this.transformConversationList(sortConData), + }); + } + } + } + }, + resetFetchConversationList: async function () { + this.setData({ + conversationPageOptions: { + page: 1, + size: 15, + total: 0, + }, + }); + try { + if (this.data.bot.botId) { + const res = await this.fetchConversationList(false, this.data.bot.botId); + console.log("res", res); + if (res) { + const { data } = res; + if (data && !data.code) { + // TODO: 临时倒序处理 + const sortData = data.data.sort( + (a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime() + ); + console.log("sortData", sortData); + const finalConData = this.data.defaultConversation + ? sortData.concat(this.data.defaultConversation) + : sortData; + console.log("finalConData", finalConData); + this.setData({ + conversations: finalConData, + transformConversations: this.transformConversationList(finalConData), + conversationPageOptions: { + ...this.data.conversationPageOptions, + total: data.total, + }, + }); + } + } + } + } catch (e) { + console.log("fetchConversationList e", e); + } + }, + transformConversationList: function (conversations) { + // 区分今天,本月,更早 + const todayCon = []; + const curMonthCon = []; + const earlyCon = []; + const now = new Date(); + const todayDate = now.setHours(0, 0, 0, 0); + const monthFirstDate = new Date(now.getFullYear(), now.getMonth(), 1).getTime(); + for (let item of conversations) { + const itemDate = new Date(item.createTime).getTime(); + if (itemDate >= todayDate) { + todayCon.push(item); + } else if (itemDate >= monthFirstDate) { + curMonthCon.push(item); + } else { + earlyCon.push(item); + } + } + console.log("todayCon curMonthCon earlyCon", todayCon, curMonthCon, earlyCon); + return { + todayCon, + curMonthCon, + earlyCon, + }; + }, + openDrawer: async function () { + if (!this.data.bot.multiConversationEnable) { + wx.showModal({ + title: "提示", + content: "请前往腾讯云开发平台启用 Agent 多会话模式", + }); + return; + } + this.setData({ + isDrawerShow: true, + // conversationPageOptions: { + // ...this.data.conversationPageOptions, + // page: 1, + // size: 15, + // }, + }); + + // await this.fetchHistoryConversationData(); + }, + closeDrawer() { + this.setData({ + isDrawerShow: false, + }); + }, showErrorMsg: function (e) { const { content, reqid } = e.currentTarget.dataset; // console.log("content", content); @@ -442,12 +727,23 @@ Component({ pageSize: this.data.size, sort: "desc", }; + if (this.data.conversation?.conversationId) { + getRecordsReq.conversationId = this.data.conversation?.conversationId; + } const res = await ai.bot.getChatRecords(getRecordsReq); if (res.recordList) { this.setData({ total: res.total, }); + if (this.data.total === this.data.chatRecords.length - 1) { + this.setData({ + triggered: false, + refreshText: "到底啦", + }); + return; + } + // 找出新获取的一页中,不在内存中的数据 const freshNum = this.data.size - ((this.data.chatRecords.length - 1) % this.data.size); const freshChatRecords = res.recordList @@ -495,8 +791,12 @@ Component({ return transformItem; }) .filter((item) => item); + // 只有一条则一定是系统开头语,需要置前,否则则为真实对话,靠后 this.setData({ - chatRecords: [...freshChatRecords, ...this.data.chatRecords], + chatRecords: + this.data.chatRecords.length === 1 + ? [...this.data.chatRecords, ...freshChatRecords] + : [...freshChatRecords, ...this.data.chatRecords], }); // console.log("totalChatRecords", this.data.chatRecords); } @@ -508,7 +808,11 @@ Component({ } ); }, + handleTapClear: function (e) { + this.clearChatRecords(); + }, clearChatRecords: function () { + console.log("执行清理"); const chatMode = this.data.chatMode; const { bot } = this.data; this.setData({ showTools: false }); @@ -520,9 +824,9 @@ Component({ return; } // 只有一条不需要清 - if (this.data.chatRecords.length === 1) { - return; - } + // if (this.data.chatRecords.length === 1) { + // return; + // } const record = { content: bot.welcomeMessage || "你好,有什么我可以帮到你?", record_id: "record_id" + String(+new Date() + 10), @@ -535,6 +839,7 @@ Component({ chatStatus: 0, questions, page: 1, // 重置分页页码 + conversation: null, }); }, chooseMedia: function (sourceType) { @@ -846,14 +1151,53 @@ Component({ if (chatMode === "bot") { const cloudInstance = await getCloudInstance(this.data.envShareConfig); const ai = cloudInstance.extend.AI; - const res = await ai.bot.sendMessage({ - data: { + // const ai = wx.cloud.extend.AI; + // 区分当前是旧的单会话模式 or 新的多会话模式 + let res; + if (!this.data.bot.multiConversationEnable) { + // 单会话 + res = await ai.bot.sendMessage({ + data: { + botId: bot.botId, + msg: inputValue, + files: this.data.showUploadFile ? userRecord.fileList.map((item) => item.fileId) : undefined, + searchEnable: this.data.useWebSearch, + }, + }); + } else { + // 多会话 + if (!this.data.conversation && this.data.bot.multiConversationEnable) { + // 发消息前构造新会话 + try { + const { data } = await this.createConversation(); + this.setData({ + conversation: { + conversationId: data.conversationId, + title: data.title, + }, + }); + } catch (e) { + console.log("createConversation e", e); + } + } + + const sendReq = { botId: bot.botId, msg: inputValue, files: this.data.showUploadFile ? userRecord.fileList.map((item) => item.fileId) : undefined, searchEnable: this.data.useWebSearch, - }, - }); + }; + + if (this.data.conversation?.conversationId) { + sendReq.conversationId = this.data.conversation.conversationId; + } + + res = await ai.bot.sendMessage({ + data: sendReq, + }); + // 当前已产生新会话,重刷一遍 + await this.resetFetchConversationList(); + } let contentText = ""; let reasoningContentText = ""; let isManuallyPaused = false; //这个标记是为了处理手动暂停时,不要请求推荐问题,不显示下面的按钮 @@ -1260,16 +1604,16 @@ Component({ // curFile.fileId = fileId const newSendFileList = this.data.sendFileList.map((item) => { if (item.tempId === tempId) { - const obj = {} - if(fileId) { - obj.fileId = fileId + const obj = {}; + if (fileId) { + obj.fileId = fileId; } - if(status) { - obj.status = status + if (status) { + obj.status = status; } return { ...item, - ...obj + ...obj, }; } return item; diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml index c05a5af..69490df 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml @@ -1,16 +1,65 @@ + + + + + + + 开启新对话 + + + + + + + + + + + + 今天 + + {{item.title}} + + + + 本月 + + {{item.title}} + + + + 更早 + + {{item.title}} + + + + + + + 暂无历史记录 + + + + + + + - + + {{bot.name}} + - + - + {{refreshText}} @@ -177,7 +226,7 @@ - + 清除 diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss index ab414d1..7cf75e0 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss @@ -535,4 +535,119 @@ /* transform: translateY(calc(-100% - 20rpx)); */ bottom: calc(100%); /* width: auto; */ +} + + +/* 抽屉遮罩层 */ +.drawer-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 998; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.drawer-mask.show { + opacity: 1; + visibility: visible; +} + +/* 抽屉主体 */ +.drawer { + position: fixed; + top: 0; + left: -80%; + width: 80%; + height: 100vh; + background: #f9fbff; + z-index: 999; + transition: all 0.3s ease; + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); + display: flex; + flex-direction: column; +} + +.drawer.show { + left: 0; +} + +.drawer-header { + padding: 16rpx 32rpx 16rpx; + /* border-bottom: 1px solid #f0f0f0; */ + display: flex; + justify-content: space-between; + align-items: center; +} + +.close-icon { + width: 24px; + height: 24px; +} + +.drawer-content { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; /* 防止内容溢出 */ + height: 0; + margin-bottom: 20px; + margin-left:32rpx; + margin-right:32rpx; +} + +.con-icon { + width: 24px; + height: 24px; + margin-left: 16rpx; + margin-right: 16rpx; +} + +.create-new-chat { + border-radius: 8px; + background: #dee9fc; + color: #4d6bfe; + cursor: pointer; + padding: 16rpx 24rpx; + display: flex; + justify-content: center; + align-items: center; +} + +.date-title { + font-size: 16px; + color: rgb(128, 128, 128); + font-weight: 400; +} + +.con-container { + margin-top: 12px; +} + +.con-block { + margin-top: 16px; +} + +.con-item { + padding: 12px 8px; + margin-bottom: 2px; + border-radius: 8px; + +} + +.con-item:active { + transition: filter 0.4s; + cursor: pointer; + background-color: rgb(249, 251, 255); + filter: brightness(0.95); +} + +.selected-con { + background-color: rgb(249, 251, 255); + filter: brightness(95%); + transition: filter 0.4s; } \ No newline at end of file diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/tools.js b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/tools.js index 3632dd0..dbf2a58 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/tools.js +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/tools.js @@ -90,38 +90,62 @@ export const compareVersions = (version1, version2) => { return 0; }; -let isDomainWarn = false +let isDomainWarn = false; -export const commonRequest = (options) => { - const self = this - return wx.request({ - ...options, - fail: (e) => { - if(options.fail) { - options.fail.bind(self)(e) - if(e.errno === 600002 || e.errMsg.includes("url not in domain list")) { - const { url } = options - let msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 https://{envid}.api.tcloudbasegateway.com` - if(url) { - const regex = /^(https?:\/\/[^/?#]+)/i; - const matches = url.match(regex); - console.log('matches', matches) - if(matches[1]) { - msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 ${matches[1]}` +export const commonRequest = async (options) => { + const cloudInstance = await getCloudInstance(); + const self = this; + // 判断 当前sdk 版本是否 小于 3.8.1 + const appBaseInfo = wx.getAppBaseInfo(); + console.log("当前版本", appBaseInfo.SDKVersion); + const { path } = options; + if (compareVersions(appBaseInfo.SDKVersion, "3.8.1") < 0) { + console.log('走wx request') + const cloudInstance = await getCloudInstance(); + const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + const envId = cloudInstance.env || cloudInstance.extend.AI.bot.context.env + console.log('envId', envId) + return wx.request({ + ...options, + path: undefined, + url: `https://${ + envId + }.api.tcloudbasegateway.com/v1/aibot/${path}`, + header: { + ...options.header, + Authorization: `Bearer ${token}`, + }, + fail: (e) => { + if (options.fail) { + options.fail.bind(self)(e); + if (e.errno === 600002 || e.errMsg.includes("url not in domain list")) { + // const { url } = options; + let msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 https://${envId}.api.tcloudbasegateway.com`; + // if (url) { + // const regex = /^(https?:\/\/[^/?#]+)/i; + // const matches = url.match(regex); + // console.log("matches", matches); + // if (matches[1]) { + // msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 ${matches[1]}`; + // } + // } + if (!isDomainWarn) { + isDomainWarn = true; + wx.showModal({ + title: "提示", + content: msg, + complete: () => { + isDomainWarn = false; + }, + }); } } - if(!isDomainWarn) { - isDomainWarn = true - wx.showModal({ - title: "提示", - content: msg, - complete: () => { - isDomainWarn = false - } - }) - } } - } - } - }) -} \ No newline at end of file + }, + }); + } else { + console.log('走内部request') + const ai = cloudInstance.extend.AI; + return ai.request(options); + } +}; diff --git a/apps/miniprogram-agent-ui/miniprogram/pages/chatBot/chatBot.js b/apps/miniprogram-agent-ui/miniprogram/pages/chatBot/chatBot.js index 52fd8c4..bcd16e2 100644 --- a/apps/miniprogram-agent-ui/miniprogram/pages/chatBot/chatBot.js +++ b/apps/miniprogram-agent-ui/miniprogram/pages/chatBot/chatBot.js @@ -20,6 +20,7 @@ Page({ allowPullRefresh: true, // 允许下拉刷新 allowUploadImage: true, // 允许上传图片 showToolCallDetail: true, // 展示 toolCall 细节 + allowMultiConversation: true, }, modelConfig: { modelProvider: "deepseek", // 大模型服务厂商 diff --git a/apps/miniprogram-agent-ui/miniprogram/pages/index/index.js b/apps/miniprogram-agent-ui/miniprogram/pages/index/index.js index dae155e..b73b516 100644 --- a/apps/miniprogram-agent-ui/miniprogram/pages/index/index.js +++ b/apps/miniprogram-agent-ui/miniprogram/pages/index/index.js @@ -30,6 +30,8 @@ Page({ allowUploadFile: true, // 允许上传文件 allowPullRefresh: true, // 允许下拉刷新 allowUploadImage: true, // 允许上传图片 + allowMultiConversation: true, // 允许客户端展示查看会话列表/新建会话按钮 + showToolCallDetail: true, // 是否展示 mcp server toolCall 细节 } }`, modelConfig1: `data: { @@ -100,10 +102,20 @@ Page({ type: "boolean", desc: "允许下拉刷新", }, + { + name: "agentConfig.allowMultiConversation", + type: "boolean", + desc: "允许展示会话列表&创建会话按钮", + }, + { + name: "agentConfig.showToolCallDetail", + type: "boolean", + desc: "允许展示mcp server toolCall 细节", + }, { name: "agentConfig.allowUploadImage", type: "boolean", - desc: "允许上传图片", + desc: "允许展示上传图片按钮", }, { name: "envShareConfig.resourceAppid", @@ -128,10 +140,12 @@ Page({ showBotAvatar: true, // 是否在对话框左侧显示头像 agentConfig: { botId: "bot-e7d1e736", // agent id, - allowWebSearch: true, // 允许客户端选择启用联网搜索 - allowUploadFile: true, // 允许上传文件 - allowPullRefresh: true, // 允许下拉刷新 - allowUploadImage: true // 允许上传图片 + allowWebSearch: true, // 允许客户端选择展示联网搜索按钮 + allowUploadFile: true, // 允许客户端展示上传文件按钮 + allowPullRefresh: true, // 允许客户端展示下拉刷新 + allowUploadImage: true, // 允许客户端展示上传图片按钮 + allowMultiConversation: true, // 允许客户端展示查看会话列表/新建会话按钮 + showToolCallDetail: true, // 是否展示 mcp server toolCall 细节 }, modelConfig: { modelProvider: "hunyuan-open", // 大模型服务厂商 diff --git a/apps/miniprogram-agent-ui/project.config.json b/apps/miniprogram-agent-ui/project.config.json index e4059f5..d7d2503 100644 --- a/apps/miniprogram-agent-ui/project.config.json +++ b/apps/miniprogram-agent-ui/project.config.json @@ -72,5 +72,6 @@ "include": [] }, "appid": "wx5ceb4e4809aa1d28", - "libVersion": "3.7.8" + "libVersion": "3.7.10", + "simulatorPluginLibVersion": {} } \ No newline at end of file diff --git a/apps/miniprogram-agent-ui/project.private.config.json b/apps/miniprogram-agent-ui/project.private.config.json index 9f71912..b5ce4ef 100644 --- a/apps/miniprogram-agent-ui/project.private.config.json +++ b/apps/miniprogram-agent-ui/project.private.config.json @@ -21,4 +21,4 @@ "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", "projectname": "cloudbase-agent-ui", "libVersion": "3.7.10" -} +} \ No newline at end of file diff --git a/components/agent-ui/chatFile/index.js b/components/agent-ui/chatFile/index.js index 456d4d8..3d3d7ab 100644 --- a/components/agent-ui/chatFile/index.js +++ b/components/agent-ui/chatFile/index.js @@ -35,64 +35,31 @@ Component({ const appBaseInfo = wx.getAppBaseInfo(); const fileId = res.fileID; this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, status: "parsing" }); - console.log("当前版本", appBaseInfo.SDKVersion); - // 3.8.1 及以上版本走sdk 内置方法 - if (compareVersions(appBaseInfo.SDKVersion, "3.8.1") < 0) { - const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); - commonRequest({ - url: `https://${ - cloudInstance.env || cloudInstance.extend.AI.bot.context.env - }.api.tcloudbasegateway.com/v1/aibot/bots/${botId}/files`, - data: { - fileList: [ - { - fileName: rawFileName || tempFileName, - fileId, - type: rawType, - }, - ], - }, - header: { - Authorization: `Bearer ${token}`, - }, - method: "POST", - success: (res) => { - console.log("old resolve agent file res", res); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); - }, - fail: (e) => { - console.log("resolve agent file e", e); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); - }, - }); - } else { - const ai = cloudInstance.extend.AI; - ai.request({ - path: `bots/${botId}/files`, // 填写 "v1/aibot/" 后面的内容 - data: { - fileList: [ - { - fileName: rawFileName || tempFileName, - fileId, - type: rawType, - }, - ], - }, // any - method: "POST", - timeout: 30000, - success: (res) => { - console.log("resolve agent file res", res); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); - }, - fail: (e) => { - console.log("e", e); - this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); - }, - complete: () => {}, - header: {}, - }); - } + commonRequest({ + path: `bots/${botId}/files`, + data: { + fileList: [ + { + fileName: rawFileName || tempFileName, + fileId, + type: rawType, + }, + ], + }, // any + method: "POST", + timeout: 60000, + success: (res) => { + console.log("resolve agent file res", res); + this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" }); + }, + fail: (e) => { + console.log("e", e); + this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" }); + }, + complete: () => {}, + header: {}, + }) }, fail: (err) => { console.error("上传失败:", err); diff --git a/components/agent-ui/imgs/indent-left.svg b/components/agent-ui/imgs/indent-left.svg index db9c0c2..c83d7a3 100644 --- a/components/agent-ui/imgs/indent-left.svg +++ b/components/agent-ui/imgs/indent-left.svg @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/components/agent-ui/index.js b/components/agent-ui/index.js index ba7c215..a89f554 100644 --- a/components/agent-ui/index.js +++ b/components/agent-ui/index.js @@ -1,5 +1,5 @@ // components/agent-ui/index.js -import { checkConfig, randomSelectInitquestion, getCloudInstance } from "./tools"; +import { checkConfig, randomSelectInitquestion, getCloudInstance, commonRequest } from "./tools"; import md5 from "./md5.js"; Component({ properties: { @@ -30,6 +30,7 @@ Component({ allowWebSearch: Boolean, allowPullRefresh: Boolean, allowUploadImage: Boolean, + allowMultiConversation: Boolean, showToolCallDetail: Boolean, }, }, @@ -80,6 +81,7 @@ Component({ showWebSearchSwitch: false, showPullRefresh: true, showToolCallDetail: true, + showMultiConversation: true, useWebSearch: false, showFeatureList: false, chatStatus: 0, // 页面状态: 0-正常状态,可输入,可发送, 1-发送中 2-思考中 3-输出content中 @@ -96,6 +98,17 @@ Component({ textareaHeight: 50, defaultErrorMsg: "网络繁忙,请稍后重试!", curScrollHeight: 0, + isDrawerShow: false, + conversations: [], + transformConversations: {}, + conversationPageOptions: { + page: 1, + size: 15, + total: 0, + }, + conversation: null, + defaultConversation: null, // 旧结构默认会话 + fetchConversationLoading: false, }, attached: async function () { const chatMode = this.data.chatMode; @@ -133,24 +146,39 @@ Component({ const { chatRecords } = this.data; // 随机选取三个初始化问题 const questions = randomSelectInitquestion(bot.initQuestions, 3); - let { allowWebSearch, allowUploadFile, allowPullRefresh, allowUploadImage, showToolCallDetail } = - this.data.agentConfig; - // console.log("allowWebSearch", allowWebSearch); + let { + allowWebSearch, + allowUploadFile, + allowPullRefresh, + allowUploadImage, + showToolCallDetail, + allowMultiConversation, + } = this.data.agentConfig; + console.log("allowWebSearch", allowWebSearch); allowWebSearch = allowWebSearch === undefined ? true : allowWebSearch; allowUploadFile = allowUploadFile === undefined ? true : allowUploadFile; allowPullRefresh = allowPullRefresh === undefined ? true : allowPullRefresh; allowUploadImage = allowUploadImage === undefined ? true : allowUploadImage; showToolCallDetail = showToolCallDetail === undefined ? true : showToolCallDetail; + allowMultiConversation = allowMultiConversation === undefined ? true : allowMultiConversation; this.setData({ bot, questions, - chatRecords: [...chatRecords, record], + chatRecords: chatRecords.length > 0 ? chatRecords : [record], showWebSearchSwitch: allowWebSearch, showUploadFile: allowUploadFile, showUploadImg: allowUploadImage, showPullRefresh: allowPullRefresh, showToolCallDetail: showToolCallDetail, + showMultiConversation: allowMultiConversation, }); + console.log("bot", this.data.bot); + if (chatMode === "bot" && this.data.bot.multiConversationEnable) { + // 拉一次默认旧会话 + await this.fetchDefaultConversationList(); + // 拉一遍新会话列表 + await this.resetFetchConversationList(); + } } const topHeight = await this.calculateContentInTop(); // console.log('topHeight', topHeight) @@ -209,6 +237,263 @@ Component({ } return ""; }, + handleClickConversation: async function (e) { + // 清除旧的会话聊天记录 + this.clearChatRecords(); + const { conversation } = e.currentTarget.dataset; + this.setData({ + isDrawerShow: false, + conversation: { + conversationId: conversation.conversationId, + title: conversation.title, + }, + page: 1, // 重置历史记录分页参数 + size: 10, + }); + this.handleRefresh(); + // // 拉取当前会话聊天记录 + // const res = await wx.cloud.extend.AI.bot.getChatRecords({ + // botId: this.data.agentConfig.botId, + // pageNumber: this.data.page, + // pageSize: this.data.size, + // sort: "desc", + // conversationId: this.data.conversation?.conversationId || undefined, + // }); + // if (res.recordList) { + // } + }, + fetchDefaultConversationList: async function () { + try { + if (this.data.bot.botId) { + const res = await this.fetchConversationList(true, this.data.bot.botId); + if (res) { + const { data } = res; + console.log("data default", data.code); + if (data && !data.code) { + console.log("data", data); + this.setData({ + defaultConversation: data, + conversations: [data], + transformConversations: this.transformConversationList([data]), + // conversationPageOptions: { + // ...this.data.conversationPageOptions, + // total: data.total, + // }, + }); + } + } + } + } catch (e) { + console.log("fetchDefaultConversationList e", e); + } + }, + fetchConversationList: async function (isDefault, botId) { + // const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + if (this.data.fetchConversationLoading) { + return; + } + + return new Promise((resolve, reject) => { + const { page, size } = this.data.conversationPageOptions; + const limit = size; + const offset = (page - 1) * size; + this.setData({ + fetchConversationLoading: true, + }); + + commonRequest({ + path: `conversation/?botId=${botId}&limit=${limit}&offset=${offset}&isDefault=${isDefault}`, + method: "GET", + header: {}, + success: (res) => { + console.log("conversation list res", res); + resolve(res); + }, + fail(e) { + console.log("conversation list e", e); + reject(e); + }, + complete: () => { + this.setData({ + fetchConversationLoading: false, + }); + // wx.hideLoading(); + }, + }); + }); + }, + createConversation: async function () { + // const cloudInstance = await getCloudInstance(); + // const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + return new Promise((resolve, reject) => { + commonRequest({ + path: `conversation`, + header: { + // Authorization: `Bearer ${token}`, + }, + data: { + botId: this.data.agentConfig.botId, + }, + method: "POST", + success: (res) => { + console.log("create conversation res", res); + resolve(res); + }, + fail(e) { + console.log("create conversation e", e); + reject(e); + }, + }); + }); + }, + clickCreateInDrawer: function () { + this.setData({ + isDrawerShow: false, + }); + this.createNewConversation(); + }, + createNewConversation: async function () { + if (!this.data.bot.multiConversationEnable) { + wx.showModal({ + title: "提示", + content: "请前往腾讯云开发平台启用 Agent 多会话模式", + }); + return; + } + // // TODO: 创建新对话 + // const { data } = await this.createConversation(); + // console.log("createRes", data); + this.clearChatRecords(); + // this.setData({ + // conversation: { + // conversationId: data.conversationId, + // title: data.title, + // }, + // }); + this.setData({ + refreshText: "下拉加载历史记录", + }); + }, + scrollConToBottom: async function (e) { + console.log("scrollConToBottom", e); + const { page, size } = this.data.conversationPageOptions; + if (page * size >= this.data.conversationPageOptions.total) { + return; + } + this.setData({ + conversationPageOptions: { + ...this.data.conversationPageOptions, + page: this.data.conversationPageOptions.page + 1, + }, + }); + console.log("conversationPageOptions", this.data.conversationPageOptions); + // 调用分页接口查询更多 + if (this.data.bot.botId) { + const res = await this.fetchConversationList(false, this.data.bot.botId); + if (res) { + const { data } = res; + if (data && !data.code) { + const addConversations = [...this.data.conversations, ...data.data]; + // TODO: 临时倒序处理 + const sortConData = addConversations.sort( + (a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime() + ); + this.setData({ + conversations: sortConData, + transformConversations: this.transformConversationList(sortConData), + }); + } + } + } + }, + resetFetchConversationList: async function () { + this.setData({ + conversationPageOptions: { + page: 1, + size: 15, + total: 0, + }, + }); + try { + if (this.data.bot.botId) { + const res = await this.fetchConversationList(false, this.data.bot.botId); + console.log("res", res); + if (res) { + const { data } = res; + if (data && !data.code) { + // TODO: 临时倒序处理 + const sortData = data.data.sort( + (a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime() + ); + console.log("sortData", sortData); + const finalConData = this.data.defaultConversation + ? sortData.concat(this.data.defaultConversation) + : sortData; + console.log("finalConData", finalConData); + this.setData({ + conversations: finalConData, + transformConversations: this.transformConversationList(finalConData), + conversationPageOptions: { + ...this.data.conversationPageOptions, + total: data.total, + }, + }); + } + } + } + } catch (e) { + console.log("fetchConversationList e", e); + } + }, + transformConversationList: function (conversations) { + // 区分今天,本月,更早 + const todayCon = []; + const curMonthCon = []; + const earlyCon = []; + const now = new Date(); + const todayDate = now.setHours(0, 0, 0, 0); + const monthFirstDate = new Date(now.getFullYear(), now.getMonth(), 1).getTime(); + for (let item of conversations) { + const itemDate = new Date(item.createTime).getTime(); + if (itemDate >= todayDate) { + todayCon.push(item); + } else if (itemDate >= monthFirstDate) { + curMonthCon.push(item); + } else { + earlyCon.push(item); + } + } + console.log("todayCon curMonthCon earlyCon", todayCon, curMonthCon, earlyCon); + return { + todayCon, + curMonthCon, + earlyCon, + }; + }, + openDrawer: async function () { + if (!this.data.bot.multiConversationEnable) { + wx.showModal({ + title: "提示", + content: "请前往腾讯云开发平台启用 Agent 多会话模式", + }); + return; + } + this.setData({ + isDrawerShow: true, + // conversationPageOptions: { + // ...this.data.conversationPageOptions, + // page: 1, + // size: 15, + // }, + }); + + // await this.fetchHistoryConversationData(); + }, + closeDrawer() { + this.setData({ + isDrawerShow: false, + }); + }, showErrorMsg: function (e) { const { content, reqid } = e.currentTarget.dataset; // console.log("content", content); @@ -442,12 +727,23 @@ Component({ pageSize: this.data.size, sort: "desc", }; + if (this.data.conversation?.conversationId) { + getRecordsReq.conversationId = this.data.conversation?.conversationId; + } const res = await ai.bot.getChatRecords(getRecordsReq); if (res.recordList) { this.setData({ total: res.total, }); + if (this.data.total === this.data.chatRecords.length - 1) { + this.setData({ + triggered: false, + refreshText: "到底啦", + }); + return; + } + // 找出新获取的一页中,不在内存中的数据 const freshNum = this.data.size - ((this.data.chatRecords.length - 1) % this.data.size); const freshChatRecords = res.recordList @@ -495,8 +791,12 @@ Component({ return transformItem; }) .filter((item) => item); + // 只有一条则一定是系统开头语,需要置前,否则则为真实对话,靠后 this.setData({ - chatRecords: [...freshChatRecords, ...this.data.chatRecords], + chatRecords: + this.data.chatRecords.length === 1 + ? [...this.data.chatRecords, ...freshChatRecords] + : [...freshChatRecords, ...this.data.chatRecords], }); // console.log("totalChatRecords", this.data.chatRecords); } @@ -508,7 +808,11 @@ Component({ } ); }, + handleTapClear: function (e) { + this.clearChatRecords(); + }, clearChatRecords: function () { + console.log("执行清理"); const chatMode = this.data.chatMode; const { bot } = this.data; this.setData({ showTools: false }); @@ -520,9 +824,9 @@ Component({ return; } // 只有一条不需要清 - if (this.data.chatRecords.length === 1) { - return; - } + // if (this.data.chatRecords.length === 1) { + // return; + // } const record = { content: bot.welcomeMessage || "你好,有什么我可以帮到你?", record_id: "record_id" + String(+new Date() + 10), @@ -535,6 +839,7 @@ Component({ chatStatus: 0, questions, page: 1, // 重置分页页码 + conversation: null, }); }, chooseMedia: function (sourceType) { @@ -846,14 +1151,53 @@ Component({ if (chatMode === "bot") { const cloudInstance = await getCloudInstance(this.data.envShareConfig); const ai = cloudInstance.extend.AI; - const res = await ai.bot.sendMessage({ - data: { + // const ai = wx.cloud.extend.AI; + // 区分当前是旧的单会话模式 or 新的多会话模式 + let res; + if (!this.data.bot.multiConversationEnable) { + // 单会话 + res = await ai.bot.sendMessage({ + data: { + botId: bot.botId, + msg: inputValue, + files: this.data.showUploadFile ? userRecord.fileList.map((item) => item.fileId) : undefined, + searchEnable: this.data.useWebSearch, + }, + }); + } else { + // 多会话 + if (!this.data.conversation && this.data.bot.multiConversationEnable) { + // 发消息前构造新会话 + try { + const { data } = await this.createConversation(); + this.setData({ + conversation: { + conversationId: data.conversationId, + title: data.title, + }, + }); + } catch (e) { + console.log("createConversation e", e); + } + } + + const sendReq = { botId: bot.botId, msg: inputValue, files: this.data.showUploadFile ? userRecord.fileList.map((item) => item.fileId) : undefined, searchEnable: this.data.useWebSearch, - }, - }); + }; + + if (this.data.conversation?.conversationId) { + sendReq.conversationId = this.data.conversation.conversationId; + } + + res = await ai.bot.sendMessage({ + data: sendReq, + }); + // 当前已产生新会话,重刷一遍 + await this.resetFetchConversationList(); + } let contentText = ""; let reasoningContentText = ""; let isManuallyPaused = false; //这个标记是为了处理手动暂停时,不要请求推荐问题,不显示下面的按钮 @@ -1260,16 +1604,16 @@ Component({ // curFile.fileId = fileId const newSendFileList = this.data.sendFileList.map((item) => { if (item.tempId === tempId) { - const obj = {} - if(fileId) { - obj.fileId = fileId + const obj = {}; + if (fileId) { + obj.fileId = fileId; } - if(status) { - obj.status = status + if (status) { + obj.status = status; } return { ...item, - ...obj + ...obj, }; } return item; diff --git a/components/agent-ui/index.wxml b/components/agent-ui/index.wxml index c05a5af..69490df 100644 --- a/components/agent-ui/index.wxml +++ b/components/agent-ui/index.wxml @@ -1,16 +1,65 @@ + + + + + + + 开启新对话 + + + + + + + + + + + + 今天 + + {{item.title}} + + + + 本月 + + {{item.title}} + + + + 更早 + + {{item.title}} + + + + + + + 暂无历史记录 + + + + + + + - + + {{bot.name}} + - + - + {{refreshText}} @@ -177,7 +226,7 @@ - + 清除 diff --git a/components/agent-ui/index.wxss b/components/agent-ui/index.wxss index ab414d1..7cf75e0 100644 --- a/components/agent-ui/index.wxss +++ b/components/agent-ui/index.wxss @@ -535,4 +535,119 @@ /* transform: translateY(calc(-100% - 20rpx)); */ bottom: calc(100%); /* width: auto; */ +} + + +/* 抽屉遮罩层 */ +.drawer-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 998; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.drawer-mask.show { + opacity: 1; + visibility: visible; +} + +/* 抽屉主体 */ +.drawer { + position: fixed; + top: 0; + left: -80%; + width: 80%; + height: 100vh; + background: #f9fbff; + z-index: 999; + transition: all 0.3s ease; + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); + display: flex; + flex-direction: column; +} + +.drawer.show { + left: 0; +} + +.drawer-header { + padding: 16rpx 32rpx 16rpx; + /* border-bottom: 1px solid #f0f0f0; */ + display: flex; + justify-content: space-between; + align-items: center; +} + +.close-icon { + width: 24px; + height: 24px; +} + +.drawer-content { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; /* 防止内容溢出 */ + height: 0; + margin-bottom: 20px; + margin-left:32rpx; + margin-right:32rpx; +} + +.con-icon { + width: 24px; + height: 24px; + margin-left: 16rpx; + margin-right: 16rpx; +} + +.create-new-chat { + border-radius: 8px; + background: #dee9fc; + color: #4d6bfe; + cursor: pointer; + padding: 16rpx 24rpx; + display: flex; + justify-content: center; + align-items: center; +} + +.date-title { + font-size: 16px; + color: rgb(128, 128, 128); + font-weight: 400; +} + +.con-container { + margin-top: 12px; +} + +.con-block { + margin-top: 16px; +} + +.con-item { + padding: 12px 8px; + margin-bottom: 2px; + border-radius: 8px; + +} + +.con-item:active { + transition: filter 0.4s; + cursor: pointer; + background-color: rgb(249, 251, 255); + filter: brightness(0.95); +} + +.selected-con { + background-color: rgb(249, 251, 255); + filter: brightness(95%); + transition: filter 0.4s; } \ No newline at end of file diff --git a/components/agent-ui/tools.js b/components/agent-ui/tools.js index 3632dd0..dbf2a58 100644 --- a/components/agent-ui/tools.js +++ b/components/agent-ui/tools.js @@ -90,38 +90,62 @@ export const compareVersions = (version1, version2) => { return 0; }; -let isDomainWarn = false +let isDomainWarn = false; -export const commonRequest = (options) => { - const self = this - return wx.request({ - ...options, - fail: (e) => { - if(options.fail) { - options.fail.bind(self)(e) - if(e.errno === 600002 || e.errMsg.includes("url not in domain list")) { - const { url } = options - let msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 https://{envid}.api.tcloudbasegateway.com` - if(url) { - const regex = /^(https?:\/\/[^/?#]+)/i; - const matches = url.match(regex); - console.log('matches', matches) - if(matches[1]) { - msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 ${matches[1]}` +export const commonRequest = async (options) => { + const cloudInstance = await getCloudInstance(); + const self = this; + // 判断 当前sdk 版本是否 小于 3.8.1 + const appBaseInfo = wx.getAppBaseInfo(); + console.log("当前版本", appBaseInfo.SDKVersion); + const { path } = options; + if (compareVersions(appBaseInfo.SDKVersion, "3.8.1") < 0) { + console.log('走wx request') + const cloudInstance = await getCloudInstance(); + const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken(); + const envId = cloudInstance.env || cloudInstance.extend.AI.bot.context.env + console.log('envId', envId) + return wx.request({ + ...options, + path: undefined, + url: `https://${ + envId + }.api.tcloudbasegateway.com/v1/aibot/${path}`, + header: { + ...options.header, + Authorization: `Bearer ${token}`, + }, + fail: (e) => { + if (options.fail) { + options.fail.bind(self)(e); + if (e.errno === 600002 || e.errMsg.includes("url not in domain list")) { + // const { url } = options; + let msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 https://${envId}.api.tcloudbasegateway.com`; + // if (url) { + // const regex = /^(https?:\/\/[^/?#]+)/i; + // const matches = url.match(regex); + // console.log("matches", matches); + // if (matches[1]) { + // msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 ${matches[1]}`; + // } + // } + if (!isDomainWarn) { + isDomainWarn = true; + wx.showModal({ + title: "提示", + content: msg, + complete: () => { + isDomainWarn = false; + }, + }); } } - if(!isDomainWarn) { - isDomainWarn = true - wx.showModal({ - title: "提示", - content: msg, - complete: () => { - isDomainWarn = false - } - }) - } } - } - } - }) -} \ No newline at end of file + }, + }); + } else { + console.log('走内部request') + const ai = cloudInstance.extend.AI; + return ai.request(options); + } +}; diff --git a/package-lock.json b/package-lock.json index 17742d9..974f4d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cloudbase-agent-ui", - "version": "1.8.3", + "version": "1.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cloudbase-agent-ui", - "version": "1.8.3", + "version": "1.9.0", "license": "MIT", "dependencies": { "standard-version": "^9.5.0" diff --git a/package.json b/package.json index ee6b687..45c0b48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudbase-agent-ui", - "version": "1.8.3", + "version": "1.9.0", "description": "微信小程序 Agent UI组件", "main": "index.js", "directories": {