|
|
@@ -0,0 +1,370 @@
|
|
|
+<template>
|
|
|
+ <div class="white content">
|
|
|
+ <div class="white">
|
|
|
+ <div v-loading="loading">
|
|
|
+ <v-banner class="mb-3">
|
|
|
+ Redis
|
|
|
+ <template v-slot:actions>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @click="getConversationsStatus"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-refresh</v-icon>
|
|
|
+ 刷新
|
|
|
+ </v-btn>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="deep-purple accent-4"
|
|
|
+ @click="onClearRedisCache({ clear_all_agent_data: true })"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-trash-can-outline</v-icon>
|
|
|
+ 清空所有agent对话数据
|
|
|
+ </v-btn>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="deep-purple accent-4"
|
|
|
+ @click="onClearRedisCache({ cleanup_invalid_refs: true })"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-trash-can-outline</v-icon>
|
|
|
+ 清理无效引用
|
|
|
+ </v-btn>
|
|
|
+ <!-- <v-btn
|
|
|
+ text
|
|
|
+ color="deep-purple accent-4"
|
|
|
+ @click="onClearRedisCache"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-trash-can-outline</v-icon>
|
|
|
+ 清除所有对话缓存
|
|
|
+ </v-btn> -->
|
|
|
+ </template>
|
|
|
+ </v-banner>
|
|
|
+ <v-container>
|
|
|
+ <v-row >
|
|
|
+ <v-col
|
|
|
+ v-for="elevation in elevations"
|
|
|
+ :key="elevation.value"
|
|
|
+ cols="3"
|
|
|
+ >
|
|
|
+ <v-card
|
|
|
+ class="pa-2"
|
|
|
+ tile
|
|
|
+ height="150"
|
|
|
+ >
|
|
|
+ <div class="d-flex align-center justify-center flex-column" style="height: 100%;">
|
|
|
+ <div class="text-h3" :class="`${elevation.textColor}--text`">{{ itemData[elevation.value] }}</div>
|
|
|
+ <div>{{ elevation.text }}</div>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ </v-col>
|
|
|
+ </v-row>
|
|
|
+ </v-container>
|
|
|
+ </div>
|
|
|
+ <!-- <v-divider></v-divider> -->
|
|
|
+
|
|
|
+ <div v-loading="embLoading">
|
|
|
+ <v-banner class="mb-3">
|
|
|
+ embedding
|
|
|
+ <template v-slot:actions>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @click="getEmbeddingStats"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-refresh</v-icon>
|
|
|
+ 刷新
|
|
|
+ </v-btn>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="deep-purple accent-4"
|
|
|
+ @click="onClearEmbeddingCache"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-trash-can-outline</v-icon>
|
|
|
+ 清除对话缓存
|
|
|
+ </v-btn>
|
|
|
+ </template>
|
|
|
+ </v-banner>
|
|
|
+ <v-container>
|
|
|
+ <v-row >
|
|
|
+ <v-col
|
|
|
+ v-for="elevation in embeddingElevations"
|
|
|
+ :key="elevation.value"
|
|
|
+ cols="3"
|
|
|
+ >
|
|
|
+ <v-card
|
|
|
+ class="pa-2"
|
|
|
+ tile
|
|
|
+ height="150"
|
|
|
+ >
|
|
|
+ <div class="d-flex align-center justify-center flex-column" style="height: 100%;">
|
|
|
+ <div class="text-h3" :class="`${elevation.textColor}--text`">{{ embeddingData[elevation.value] }}</div>
|
|
|
+ <div>{{ elevation.text }}</div>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ </v-col>
|
|
|
+ </v-row>
|
|
|
+ </v-container>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-loading="checkpointLoading">
|
|
|
+ <v-banner class="mb-3">
|
|
|
+ Checkpoint
|
|
|
+ <template v-slot:actions>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @click="getCheckpoint"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-refresh</v-icon>
|
|
|
+ 刷新
|
|
|
+ </v-btn>
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="deep-purple accent-4"
|
|
|
+ @click="onClearCheckpoint"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-trash-can-outline</v-icon>
|
|
|
+ 清除对话缓存
|
|
|
+ </v-btn>
|
|
|
+ </template>
|
|
|
+ </v-banner>
|
|
|
+ <v-container>
|
|
|
+ <v-row >
|
|
|
+ <v-col
|
|
|
+ v-for="elevation in checkpointElevations"
|
|
|
+ :key="elevation.value"
|
|
|
+ cols="3"
|
|
|
+ >
|
|
|
+ <v-card
|
|
|
+ class="pa-2"
|
|
|
+ tile
|
|
|
+ height="150"
|
|
|
+ >
|
|
|
+ <div class="d-flex align-center justify-center flex-column" style="height: 100%;">
|
|
|
+ <div
|
|
|
+ class="text-h3"
|
|
|
+ :class="`${elevation.textColor}--text ${elevation.onClick ? 'cursor-pointer' : ''}`"
|
|
|
+ @click="elevation.onClick && elevation.onClick(elevation, checkpointData)"
|
|
|
+ >
|
|
|
+ {{ checkpointData[elevation.value] }}
|
|
|
+ </div>
|
|
|
+ <div>{{ elevation.text }}</div>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ </v-col>
|
|
|
+ </v-row>
|
|
|
+ </v-container>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <MDialog :visible.sync="show" title="设置保留checkpoint数量" @submit="handleSubmit" widthType="2">
|
|
|
+ <v-text-field
|
|
|
+ v-model="checkpointParam.keep_count"
|
|
|
+ dense
|
|
|
+ label="checkpoint数量"
|
|
|
+ placeholder="请输入每个线程保留的checkpoint数量"
|
|
|
+ outlined
|
|
|
+ ></v-text-field>
|
|
|
+ </MDialog>
|
|
|
+ <ModelDetails ref="userRefs" title="用户列表">
|
|
|
+ <template #actions="{ item }">
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @click="onClearCheckpoint({
|
|
|
+ keep_count: 10,
|
|
|
+ user_id: item.user_id,
|
|
|
+ thread_id: undefined
|
|
|
+ })"
|
|
|
+ >清理Checkpoint</v-btn>
|
|
|
+ </template>
|
|
|
+ </ModelDetails>
|
|
|
+ <ModelDetails ref="threadRefs" title="线程列表">
|
|
|
+ <template #actions="{ item }">
|
|
|
+ <v-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @click="onClearCheckpoint({
|
|
|
+ keep_count: 10,
|
|
|
+ user_id: undefined,
|
|
|
+ thread_id: item.thread_id
|
|
|
+ })"
|
|
|
+ >清理Checkpoint</v-btn>
|
|
|
+ </template>
|
|
|
+ </ModelDetails>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ getConversationsStatus,
|
|
|
+ clearConversation,
|
|
|
+ getEmbeddingStats,
|
|
|
+ clearEmbeddingCache,
|
|
|
+ getCheckpoint,
|
|
|
+ clearCheckpoint
|
|
|
+} from '@/api/dataChart'
|
|
|
+import MDialog from '@/components/Dialog'
|
|
|
+import ModelDetails from './modelDetails'
|
|
|
+export default {
|
|
|
+ name: 'modelHistory',
|
|
|
+ components: {
|
|
|
+ MDialog,
|
|
|
+ ModelDetails
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ showUser: false,
|
|
|
+ show: false,
|
|
|
+ loading: false,
|
|
|
+ embLoading: false,
|
|
|
+ checkpointLoading: false,
|
|
|
+ elevations: [
|
|
|
+ { value: 'connected_clients', text: '客户端连接数', textColor: 'primary' },
|
|
|
+ { value: 'memory_usage_mb', text: 'redis占用内存(M)', textColor: 'primary' },
|
|
|
+ { value: 'total_conversations', text: '会话总数', textColor: 'primary' },
|
|
|
+ { value: 'total_users', text: '用户数', textColor: 'primary' }
|
|
|
+ ],
|
|
|
+ itemData: {},
|
|
|
+ embeddingElevations: [
|
|
|
+ { value: 'memory_usage_mb', text: 'embedding占用内存(M)', textColor: 'primary' },
|
|
|
+ { value: 'total_count', text: '总数', textColor: 'primary' }
|
|
|
+ ],
|
|
|
+ checkpointElevations: [
|
|
|
+ { value: 'total_users', text: '用户数量', textColor: 'primary', onClick: this.onClickUser },
|
|
|
+ { value: 'total_threads', text: '线程总数', textColor: 'primary', onClick: this.onClickThread },
|
|
|
+ { value: 'total_checkpoints', text: 'checkpoints总数', textColor: 'primary' }
|
|
|
+ ],
|
|
|
+ embeddingData: {},
|
|
|
+ checkpointData: {},
|
|
|
+ checkpointParam: {
|
|
|
+ keep_count: 10,
|
|
|
+ user_id: undefined,
|
|
|
+ thread_id: undefined
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created () {
|
|
|
+ this.getConversationsStatus()
|
|
|
+ this.getEmbeddingStats()
|
|
|
+ this.getCheckpoint()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onClickUser (item, itemData) {
|
|
|
+ this.$refs.userRefs.open([
|
|
|
+ { text: '用户ID', value: 'user_id' },
|
|
|
+ { text: '线程总数', value: 'thread_count' },
|
|
|
+ { text: 'checkpoints数', value: 'total_checkpoints' },
|
|
|
+ { text: '操作', value: 'actions' }
|
|
|
+ ], itemData.users)
|
|
|
+ },
|
|
|
+ onClickThread (item, itemData) {
|
|
|
+ this.$refs.threadRefs.open([
|
|
|
+ { text: '线程ID', value: 'thread_id' },
|
|
|
+ { text: 'checkpoints数', value: 'checkpoint_count' },
|
|
|
+ { text: '用户ID', value: 'user_id' },
|
|
|
+ { text: '操作', value: 'actions' }
|
|
|
+ ], itemData.users.reduce((acc, cur) => {
|
|
|
+ acc.push(...cur.threads.map(e => {
|
|
|
+ return { ...e, user_id: cur.user_id }
|
|
|
+ }))
|
|
|
+ return acc
|
|
|
+ }, []))
|
|
|
+ },
|
|
|
+ async getConversationsStatus () {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const { data } = await getConversationsStatus()
|
|
|
+ this.itemData = { ...data, ...data.redis_info }
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async getEmbeddingStats () {
|
|
|
+ this.embLoading = true
|
|
|
+ try {
|
|
|
+ const { data } = await getEmbeddingStats()
|
|
|
+ this.embeddingData = data
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ this.embLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async getCheckpoint () {
|
|
|
+ this.checkpointLoading = true
|
|
|
+ try {
|
|
|
+ const { data } = await getCheckpoint()
|
|
|
+ this.checkpointData = data
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ this.checkpointLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onClearRedisCache (param) {
|
|
|
+ this.$confirm('提示', '确定执行清除吗?').then(async () => {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ await clearConversation(param)
|
|
|
+ this.$snackbar.success('清除成功')
|
|
|
+ this.getConversationsStatus()
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onClearEmbeddingCache () {
|
|
|
+ this.$confirm('提示', '确定清除embedding缓存吗?').then(async () => {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ await clearEmbeddingCache()
|
|
|
+ this.$snackbar.success('清除成功')
|
|
|
+ this.getEmbeddingStats()
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onClearCheckpoint (param = {
|
|
|
+ keep_count: 10,
|
|
|
+ user_id: undefined,
|
|
|
+ thread_id: undefined
|
|
|
+ }) {
|
|
|
+ this.show = true
|
|
|
+ this.checkpointParam = param
|
|
|
+ },
|
|
|
+ async handleSubmit () {
|
|
|
+ if (!this.checkpointParam.keep_count) {
|
|
|
+ this.$snackbar.error('请输入保留的checkpoint数量')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ this.checkpointParam.keep_count = Number(this.checkpointParam.keep_count)
|
|
|
+ const { data } = await clearCheckpoint(this.checkpointParam)
|
|
|
+ this.$snackbar.success(data.response)
|
|
|
+ this.show = false
|
|
|
+ this.getCheckpoint()
|
|
|
+ this.$refs.userRefs.close()
|
|
|
+ this.$refs.threadRefs.close()
|
|
|
+ } catch (error) {
|
|
|
+ this.$snackbar.error(error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.content {
|
|
|
+ font-size: 16px;
|
|
|
+}
|
|
|
+.cursor-pointer {
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|