feat: add token usage and top questions statistics retrieval

This commit is contained in:
wxg0103 2025-11-13 14:55:47 +08:00
parent 719ba08c6e
commit 74b1bce315
4 changed files with 127 additions and 63 deletions

View File

@ -192,6 +192,26 @@ const getStatistics: (
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix.value}/${application_id}/application_stats`, data, loading)
}
/**
* token消耗
*/
const getTokenUsage: (
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix.value}/${application_id}/application_token_usage`, data, loading)
}
/**
*
*/
const topQuestions: (
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix.value}/${application_id}/top_questions`, data, loading)
}
/**
* id
* @param application_id id
@ -207,11 +227,11 @@ const open: (application_id: string, loading?: Ref<boolean>) => Promise<Result<s
/**
*
* @param workspace_id
* @param model_id
* @param application_id
* @param data
* @returns
* @param workspace_id
* @param model_id
* @param application_id
* @param data
* @returns
*/
const generate_prompt: (workspace_id:string ,model_id:string, application_id:string,data: any) => Promise<any> = (
workspace_id,
@ -408,5 +428,7 @@ export default {
speechToText,
getMcpTools,
postUploadFile,
generate_prompt
generate_prompt,
getTokenUsage,
topQuestions
}

View File

@ -111,6 +111,23 @@ const getStatistics: (
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix}/${application_id}/application_stats`, data, loading)
}
/**
* token消耗
*/
const getTokenUsage: (
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix}/${application_id}/application_token_usage`, data, loading)
}
const topQuestions: (
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
return get(`${prefix}/${application_id}/top_questions`, data, loading)
}
/**
* id
* @param application_id id
@ -126,10 +143,10 @@ const open: (application_id: string, loading?: Ref<boolean>) => Promise<Result<s
/**
*
* @param application_id
* @param model_id
* @param data
* @returns
* @param application_id
* @param model_id
* @param data
* @returns
*/
const generate_prompt: (application_id:string, model_id:string, data: any) => Promise<any> = (
application_id,
@ -174,7 +191,7 @@ const playDemoText: (application_id: string, data: any, loading?: Ref<boolean>)
*
*/
const postTextToSpeech: (
application_id: String,
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
@ -184,7 +201,7 @@ const postTextToSpeech: (
*
*/
const speechToText: (
application_id: String,
application_id: string,
data: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (application_id, data, loading) => {
@ -289,7 +306,7 @@ const updatePlatformConfig: (
/**
* mcp
*/
const getMcpTools: (application_id: String, loading?: Ref<boolean>) => Promise<Result<any>> = (
const getMcpTools: (application_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
application_id,
loading,
) => {
@ -320,5 +337,7 @@ export default {
speechToText,
getMcpTools,
putXpackAccessToken,
generate_prompt
generate_prompt,
getTokenUsage,
topQuestions
}

View File

@ -13,14 +13,14 @@
<el-card shadow="never">
<div class="flex align-center ml-8 mr-8">
<el-avatar :size="40" shape="square" :style="{ background: item.background }">
<appIcon :iconName="item.icon" :style="{ fontSize: '24px', color: item.color }" />
<appIcon :iconName="item.icon" :style="{ fontSize: '24px', color: item.color }"/>
</el-avatar>
<div class="ml-12">
<p class="color-secondary lighter mb-4">{{ item.name }}</p>
<div v-if="item.id !== 'starCharts'" class="flex align-baseline">
<h2>{{ numberFormat(item.sum?.[0]) }}</h2>
<span v-if="item.sum.length > 1" class="ml-12" style="color: #f54a45"
>+{{ numberFormat(item.sum?.[1]) }}</span
>+{{ numberFormat(item.sum?.[1]) }}</span
>
</div>
<div v-else class="flex align-center mr-8">
@ -47,23 +47,32 @@
>
<el-card shadow="never">
<div class="p-8">
<AppCharts height="316px" :id="item.id" type="line" :option="item.option" />
<AppCharts height="316px" :id="item.id" type="line" :option="item.option"/>
</div>
</el-card>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import {ref, computed, onMounted} from 'vue'
import AppCharts from '@/components/app-charts/index.vue'
import { getAttrsArray, getSum } from '@/utils/array'
import { numberFormat } from '@/utils/common'
import { t } from '@/locales'
import {getAttrsArray, getSum} from '@/utils/array'
import {numberFormat} from '@/utils/common'
import {t} from '@/locales'
const props = defineProps({
data: {
type: Array,
default: () => [],
},
tokenUsage: {
type: Array,
default: () => [],
},
topQuestions: {
type: Array,
default: () => [],
}
})
const statisticsType = computed(() => [
{

View File

@ -11,7 +11,7 @@
<div class="title flex align-center">
<div class="edit-avatar mr-12">
<el-avatar shape="square" :size="32" style="background: none">
<img :src="resetUrl(detail?.icon, resetUrl('./favicon.ico'))" alt="" />
<img :src="resetUrl(detail?.icon, resetUrl('./favicon.ico'))" alt=""/>
</el-avatar>
</div>
@ -22,7 +22,7 @@
<el-col :span="12" class="mt-16">
<div class="flex">
<el-text type="info"
>{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
>{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
</el-text>
<el-switch
v-model="accessToken.is_active"
@ -52,7 +52,7 @@
style="margin-left: 1px"
>
<el-icon>
<RefreshRight />
<RefreshRight/>
</el-icon>
</el-button>
</el-tooltip>
@ -98,12 +98,12 @@
<el-col :span="12" class="mt-16">
<div class="flex">
<el-text type="info"
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
</el-text>
</div>
<div class="mt-4 mb-16 url-height">
<div>
<el-text>API {{ $t('common.fileUpload.document') }} </el-text>
<el-text>API {{ $t('common.fileUpload.document') }}</el-text>
<el-button
type="primary"
link
@ -119,8 +119,8 @@
</span>
<span class="vertical-middle lighter break-all ellipsis-1">{{
baseUrl + id
}}</span>
baseUrl + id
}}</span>
<el-tooltip effect="dark" :content="$t('common.copy')" placement="top">
<el-button type="primary" text @click="copyClick(baseUrl + id)">
<AppIcon iconName="app-copy"></AppIcon>
@ -134,7 +134,7 @@
v-if="permissionPrecise.overview_api_key(id)"
>
<el-icon class="mr-4">
<Key />
<Key/>
</el-icon>
{{ $t('views.applicationOverview.appInfo.apiKey') }}
</el-button>
@ -173,7 +173,8 @@
/>
</div>
<div v-loading="statisticsLoading">
<StatisticsCharts :data="statisticsData" />
<StatisticsCharts :data="statisticsData" :token-usage="tokenUsage"
:top-questions="topQuestions"/>
</div>
</el-card>
</div>
@ -184,17 +185,17 @@
:data="detail"
:api-input-params="mapToUrlParams(apiInputParams)"
/>
<APIKeyDialog ref="APIKeyDialogRef" />
<APIKeyDialog ref="APIKeyDialogRef"/>
<!-- 社区版访问限制 -->
<component :is="currentLimitDialog" ref="LimitDialogRef" @refresh="refresh" />
<component :is="currentLimitDialog" ref="LimitDialogRef" @refresh="refresh"/>
<!-- 显示设置 -->
<component :is="currentDisplaySettingDialog" ref="DisplaySettingDialogRef" @refresh="refresh" />
<component :is="currentDisplaySettingDialog" ref="DisplaySettingDialogRef" @refresh="refresh"/>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, shallowRef, nextTick } from 'vue'
import { useRoute } from 'vue-router'
import {ref, computed, onMounted, shallowRef, nextTick} from 'vue'
import {useRoute} from 'vue-router'
import EmbedDialog from './component/EmbedDialog.vue'
import APIKeyDialog from './component/APIKeyDialog.vue'
import LimitDialog from './component/LimitDialog.vue'
@ -202,20 +203,20 @@ import XPackLimitDrawer from './xpack-component/XPackLimitDrawer.vue'
import DisplaySettingDialog from './component/DisplaySettingDialog.vue'
import XPackDisplaySettingDialog from './xpack-component/XPackDisplaySettingDialog.vue'
import StatisticsCharts from './component/StatisticsCharts.vue'
import { nowDate, beforeDay } from '@/utils/time'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { copyClick } from '@/utils/clipboard'
import { resetUrl } from '@/utils/common'
import { mapToUrlParams } from '@/utils/application'
import { t } from '@/locales'
import { EditionConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import {nowDate, beforeDay} from '@/utils/time'
import {MsgSuccess, MsgConfirm} from '@/utils/message'
import {copyClick} from '@/utils/clipboard'
import {resetUrl} from '@/utils/common'
import {mapToUrlParams} from '@/utils/application'
import {t} from '@/locales'
import {EditionConst} from '@/utils/permission/data'
import {hasPermission} from '@/utils/permission/index'
import permissionMap from '@/permission'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import {loadSharedApi} from '@/utils/dynamics-api/shared-api'
const route = useRoute()
const {
params: { id },
params: {id},
} = route as any
const apiType = computed(() => {
@ -287,6 +288,8 @@ const daterange = ref({
const statisticsLoading = ref(false)
const statisticsData = ref([])
const tokenUsage = ref([])
const topQuestions = ref([])
const apiInputParams = ref([])
@ -308,7 +311,7 @@ function openDisplaySettingDialog() {
}
nextTick(() => {
if (currentDisplaySettingDialog.value == XPackDisplaySettingDialog) {
loadSharedApi({ type: 'application', systemType: apiType.value })
loadSharedApi({type: 'application', systemType: apiType.value})
.getApplicationSetting(id)
.then((ok: any) => {
DisplaySettingDialogRef.value?.open(ok.data, detail.value)
@ -351,11 +354,21 @@ function changeDayRangeHandle(val: string) {
}
function getAppStatistics() {
loadSharedApi({ type: 'application', systemType: apiType.value })
loadSharedApi({type: 'application', systemType: apiType.value})
.getStatistics(id, daterange.value, statisticsLoading)
.then((res: any) => {
statisticsData.value = res.data
})
loadSharedApi({type: 'application', systemType: apiType.value})
.getTokenUsage(id, daterange.value, statisticsLoading)
.then((res: any) => {
tokenUsage.value = res.data
})
loadSharedApi({type: 'application', systemType: apiType.value})
.topQuestions(id, daterange.value, statisticsLoading)
.then((res: any) => {
topQuestions.value = res.data
})
}
function refreshAccessToken() {
@ -374,7 +387,8 @@ function refreshAccessToken() {
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
updateAccessToken(obj, str)
})
.catch(() => {})
.catch(() => {
})
}
async function changeState(bool: boolean) {
@ -392,7 +406,7 @@ async function changeState(bool: boolean) {
}
async function updateAccessToken(obj: any, str: string) {
loadSharedApi({ type: 'application', systemType: apiType.value })
loadSharedApi({type: 'application', systemType: apiType.value})
.putAccessToken(id as string, obj, loading)
.then((res: any) => {
accessToken.value = res?.data
@ -409,7 +423,7 @@ function openDialog() {
}
function getAccessToken() {
loadSharedApi({ type: 'application', systemType: apiType.value })
loadSharedApi({type: 'application', systemType: apiType.value})
.getAccessToken(id, loading)
.then((res: any) => {
accessToken.value = res?.data
@ -417,7 +431,7 @@ function getAccessToken() {
}
function getDetail() {
loadSharedApi({ type: 'application', systemType: apiType.value })
loadSharedApi({type: 'application', systemType: apiType.value})
.getApplicationDetail(id, loading)
.then((res: any) => {
detail.value = res.data
@ -426,20 +440,20 @@ function getDetail() {
.map((v: any) => {
apiInputParams.value = v.properties.api_input_field_list
? v.properties.api_input_field_list.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
return {
name: v.variable,
value: v.default_value,
}
})
: v.properties.input_field_list
? v.properties.input_field_list
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
: []
})
})