Skip to content

Commit

Permalink
feat: 添加agent-ui小程序源码组件
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan committed Feb 14, 2025
1 parent 98a1525 commit 4a2409e
Show file tree
Hide file tree
Showing 110 changed files with 7,871 additions and 2 deletions.
Empty file.
19 changes: 19 additions & 0 deletions miniprogram/tcb-agent-ui/miniprogram/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// app.js
App({
onLaunch: function () {
if (!wx.cloud) {
console.error("请使用 2.2.3 或以上的基础库以使用云能力");
} else {
wx.cloud.init({
// env 参数说明:
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
// 如不填则使用默认环境(第一个创建的环境)
env: "lowcode-1gm3ep4m027742a2",
traceUser: true,
});
}

this.globalData = {};
},
});
17 changes: 17 additions & 0 deletions miniprogram/tcb-agent-ui/miniprogram/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"pages": [
"pages/index/index",
"pages/chatBot/chatBot"
],
"window": {
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "腾讯云开发Agent-UI",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light",
"enablePullDownRefresh": true
},
"sitemapLocation": "sitemap.json",
"style": "v2",
"lazyCodeLoading": "requiredComponents"
}
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
219 changes: 219 additions & 0 deletions miniprogram/tcb-agent-ui/miniprogram/components/agent-ui/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// components/agent-ui/index.js
import { guide, checkConfig } from './tools'
Component({
properties: {
agentConfig: {
type: Object,
value: {
type: "", // 值为'bot'或'model'。当type='bot'时,botId必填;当type='model'时,model必填
botId: "", // agent id
modelName: "", // 大模型服务商
model: "", // 具体的模型版本
logo: "",// 图标(只在model模式下生效)
welcomeMessage: ""// 欢迎语(只在model模式下生效)
}
}
},

data: {
isLoading: true, // 判断是否尚在加载中
article: {},
windowInfo: wx.getWindowInfo(),
bot: {},
inputValue: '',
output: "",
chatRecords: [],
scrollTop: 0,
streamStatus: false,
setPanelVisibility: false,
questions: [],
scrollTop: 0,
guide,
showGuide: false
},

attached: async function () {
const { botId, type } = this.data.agentConfig
const [check, message] = checkConfig(this.data.agentConfig)
if (!check) {
wx.showModal({
title: '提示',
content: message,
})
this.setData({ showGuide: true })
} else {
this.setData({ showGuide: false })
}
if (type === 'bot') {
const ai = wx.cloud.extend.AI
const bot = await ai.bot.get({ botId })
this.setData({ bot, questions: bot.initQuestions })
return;
}
},
methods: {
bindKeyInput: function (e) {
this.setData({
inputValue: e.detail.value
})
},
clearChatRecords: function () {
this.setData({ chatRecords: [], streamStatus: false, setPanelVisibility: !this.data.setPanelVisibility })
},
stop: function () {
const { chatRecords } = this.data
const newChatRecords = [...chatRecords]
const record = newChatRecords[newChatRecords.length - 1]
if (record.content === '...') {
record.content = '已暂停回复'
}
this.setData({ streamStatus: false, chatRecords: newChatRecords })
},
openSetPanel: function () {
this.setData({ setPanelVisibility: true })
},
closeSetPanel: function () {
this.setData({ setPanelVisibility: false })
},
sendMessage: async function (event) {
const { message } = event.currentTarget.dataset
let { inputValue, bot, agentConfig } = this.data
if (message) {
inputValue = message
}
if (!inputValue) {
return;
}
const { type, modelName, model } = agentConfig
// console.log(inputValue,bot.botId)
const userRecord = {
content: inputValue,
record_id: 'record_id' + String(+new Date() - 10),
role: "user"
}
const record = {
content: "...",
record_id: 'record_id' + String(+new Date() + 10),
role: "assistant"
}
this.setData({ inputValue: "", questions: [], chatRecords: [...this.data.chatRecords, userRecord, record], streamStatus: false })
// 先这样写,后面抽离出来
if (type === 'bot') {
const ai = wx.cloud.extend.AI
const res = await ai.bot.sendMessage({
data: {
botId: bot.botId,
history: [],
msg: inputValue,
}
})
this.setData({ streamStatus: true })
let contentText = ""
let reasoningText = ""
for await (let event of res.eventStream) {
if (!this.data.streamStatus) {
break;
}
this.toBottom()
const { data } = event
try {
const dataJson = JSON.parse(data)
// console.log(dataJson)
const { content, reasoning_content, record_id } = dataJson
contentText += content
reasoningText += reasoning_content
const newValue = [...this.data.chatRecords]
newValue[newValue.length - 1] = { record_id, role: dataJson.role || 'assistant', content: contentText, reasoning_content: reasoningText }
this.setData({ chatRecords: newValue })
} catch (e) {
// console.log('err', event, e)
break;
}
}
this.setData({ streamStatus: false })
if (bot.isNeedRecommend) {
const ai = wx.cloud.extend.AI
const recommendRes = await ai.bot.getRecommendQuestions({
data: {
botId: bot.botId,
history: [],
msg: inputValue,
agentSetting: "",
introduction: "",
name: "",
}
})
let result = ''
for await (let str of recommendRes.textStream) {
// console.log(str);
this.toBottom()
result += str
this.setData({ questions: result.split('\n').filter(item => !!item) })
}
}
}
if (type === 'model') {
const aiModel = wx.cloud.extend.AI.createModel(modelName);
const res = await aiModel.streamText({
data: {
model: model,
messages: [{
role: "user",
content: inputValue
}],
},
});
let contentText = ''
let reasoningText = ''
this.setData({ streamStatus: true })
for await (let event of res.eventStream) {
if (!this.data.streamStatus) {
break;
}
this.toBottom()

const { data } = event
try {
const dataJson = JSON.parse(data)
const { id, choices = [] } = dataJson || {}
const { delta, finish_reason } = choices[0] || {}
if (finish_reason === 'stop') {
break
}
const { content, reasoning_content, role } = delta
reasoningText += (reasoning_content || '')
contentText += (content || '')
const newValue = [...this.data.chatRecords]
newValue[newValue.length - 1] = {
content: contentText,
reasoning_content: reasoningText,
record_id: 'record_id' + String(id),
role: role
}
this.setData({ chatRecords: newValue })
} catch (e) {
// console.log(e, event)
break;
}
}
this.setData({ streamStatus: false })
}
},
toBottom: function () {
this.setData({ scrollTop: this.data.scrollTop + 4 })
},
copyChatRecord: function (e) {
// console.log(e)
const { content } = e.currentTarget.dataset
wx.setClipboardData({
data: content + '\n\n来自微信云开发AI+',
success: function (res) {
wx.showToast({
title: '复制成功',
icon: 'success'
});
}
});
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"markdownPreview":"/components/agent-ui/markdownPreview/index"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!-- agent ui 组件根容器 -->
<view class="agent-ui">
<!-- 聊天对话区 -->
<scroll-view class="main" style="height: {{windowInfo.windowHeight-80}}px;" scroll-y="{{true}}" bind:tap="toBottom" scroll-top="{{scrollTop}}">
<view class="nav">
<image src="{{bot.avatar||agentConfig.logo}}" mode="aspectFill" class="avatar" />
<view style="line-height: 47px;">{{agentConfig.type==='bot'?bot.name:agentConfig.modelName}}</view>
<view style="line-height: 26px;padding: 0px 16px;">{{agentConfig.type==='bot'?"":agentConfig.welcomeMessage}}</view>
</view>
<view class="system" wx:if="{{showGuide}}">
<markdownPreview markdown="{{guide}}"></markdownPreview>
</view>
<view class="system" wx:if="{{agentConfig.type==='bot'}}">
<markdownPreview markdown="{{bot.introduction||''}}"></markdownPreview>
</view>
<block wx:for="{{chatRecords}}" wx:key="record_id">
<view class="system" wx:if="{{item.role==='assistant'}}">
<view wx:if="{{!!item.reasoning_content}}" style="opacity: 0.7;">
<view>{{item.reasoning_content&&!item.content?"思考中...":"已深度思考"}}</view>
<view style="padding-left: 30rpx;border-left: rgb(165, 164, 164) solid 2px;">
<markdownPreview markdown="{{item.reasoning_content||''}}"></markdownPreview>
</view>
</view>
<markdownPreview markdown="{{item.content||''}}"></markdownPreview>
<view style="display: flex; gap: 10px;justify-content: flex-end;" wx:if="{{!streamStatus}}">
<image mode="widthFix" bind:tap="copyChatRecord" src='./imgs/copy.png' style="width: 36rpx; height: 36rpx;" data-content="{{item.content}}" />
<button class="share_btn" open-type="share">
<image mode="widthFix" src='./imgs/share1.png' style="width: 36rpx; height: 36rpx;vertical-align: top;" bind:tap="share" />
</button>
</view>
</view>
<view class="user" wx:if="{{item.role==='user'}}">
<view>
{{item.content}}
</view>
</view>
</block>
<!-- 推荐问题 -->
<block wx:for="{{questions}}" wx:key="item">
<view class="questions">
<view class="question_content" bind:tap="sendMessage" data-message="{{item}}">{{item}}</view>
</view>
</block>
<view style="width: 100%;height: 20px;"></view>
</scroll-view>
<!-- 底部输入区 -->
<view class="footer">
<view class="input_box">
<input class="input" value="{{inputValue}}" type="text" maxlength="1024" bindinput="bindKeyInput" placeholder="说点什么吧" bindconfirm="sendMessage" confirm-type="send" adjust-position cursor-spacing="20" />
<image src="./imgs/set.png" class="set" mode="widthFix" bind:tap="openSetPanel" />
<image src="./imgs/send.png" class="set" mode="widthFix" wx:if="{{!!inputValue&&!streamStatus}}" bind:tap="sendMessage" />
<image src="./imgs/stop.png" class="set" mode="widthFix" wx:if="{{!!streamStatus}}" bind:tap="stop" />
</view>
<!-- 设置面板 -->
<view class="set_panel_modal" wx:if="{{setPanelVisibility}}" bind:tap="closeSetPanel">
<view class="set_panel">
<view class="set_panel_funtion">
<view class="function" bind:tap="clearChatRecords">
<image src="./imgs/clear.png" alt="widthFix" class="icon" />
<text class="text_desc">清除对话</text>
</view>
</view>
<view class="set_panel_cancel" bind:tap="closeSetPanel">取消</view>
</view>
</view>
</view>
</view>
Loading

0 comments on commit 4a2409e

Please sign in to comment.