diff --git a/frontend/src/components/evaluations/EvaluationDetail.vue b/frontend/src/components/evaluations/EvaluationDetail.vue
index 4a2e3c20b..a0d18089c 100644
--- a/frontend/src/components/evaluations/EvaluationDetail.vue
+++ b/frontend/src/components/evaluations/EvaluationDetail.vue
@@ -108,96 +108,150 @@
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
- {{
- (row.dataset[datasetName] || [])
- .find(item => item.metric === metricItem.metric)?.score || 0
- }}
+
+
+
+
+
+
+ {{
+ (row.dataset[datasetName] || [])
+ .find(item => item.metric === metricItem.metric)?.score || 0
+ }}
+
+
+
+
+
+ {{ error }}
+ {{ $t('common.noData') }}
+
+
+
+
+
+
+
+
+
+
{{ $t('evaluation.detail.evaluationFailed') }}
+
-
-
-
-
- {{ error }}
- {{ $t('all.noData') }}
-
-
-
-
-
-
-
-
-
-
{{ $t('evaluation.detail.evaluationFailed') }}
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t('endpoints.analysis.noInstances') }}
+
+
+
-
+
@@ -208,7 +262,10 @@
import { ElMessage } from 'element-plus'
import useFetchApi from '@/packs/useFetchApi'
import { formatDate } from '@/packs/datetimeUtils'
- import SvgIcon from '@/components/shared/SvgIcon.vue';
+ import SvgIcon from '@/components/shared/SvgIcon.vue'
+ import * as echarts from 'echarts'
+ import { timestampToDatetimeStr } from '@/packs/datetimeUtils'
+ import useEvaluationDetailStore from '@/stores/EvaluationDetailStore'
const props = defineProps({
evaluationId: {
@@ -227,6 +284,32 @@
const error = ref('')
const categories = ref([])
const evaluationSucceed = ref(true)
+ const isCustomDataset = ref(false)
+ const evDetailStore = useEvaluationDetailStore()
+ const activeTabName = ref('evaluation-result')
+
+ const loadStore = () => {
+ if (props.evaluationId) {
+ const tabName = evDetailStore.getActiveTabName(props.evaluationId)
+ if (tabName) {
+ activeTabName.value = tabName
+ }
+ }
+ }
+
+ const cleanStore = () => {
+ evDetailStore.clearStore(props.evaluationId)
+ }
+
+ const handleTabContainerClick = (tab) => {
+ activeTabName.value = tab.props.name
+ evDetailStore.setActiveTabName(tab.props.name, props.evaluationId)
+ if(activeTabName.value === "evaluation-analysis") {
+ nextTick(() => {
+ refeshCurrentPeriod()
+ })
+ }
+ }
const groupedDatasets = computed(() => {
return categories.value.map((category) => ({
@@ -425,6 +508,14 @@
scoresKey.value = getScoresKey(evaluationResult.value)
refreshTableData(activeName.value)
}
+
+ if (evaluation.value.task_id) {
+ instances.value = [evaluation.value.task_id]
+ selectedInstance.value = evaluation.value.task_id
+ nextTick(() => {
+ refeshCurrentPeriod()
+ })
+ }
} catch (e) {
error.value = e.message
ElMessage({
@@ -436,7 +527,187 @@
}
}
+ const timePeriodOptions = [
+ {
+ label: t('endpoints.analysis.thirty_m'),
+ value: '30m'
+ },
+ {
+ label: t('endpoints.analysis.one_h'),
+ value: '1h'
+ },
+ {
+ label: t('endpoints.analysis.three_h'),
+ value: '3h'
+ },
+ {
+ label: t('endpoints.analysis.six_h'),
+ value: '6h'
+ },
+ {
+ label: t('endpoints.analysis.twelve_h'),
+ value: '12h'
+ },
+ {
+ label: t('endpoints.analysis.one_d'),
+ value: '1d'
+ },
+ {
+ label: t('endpoints.analysis.three_d'),
+ value: '3d'
+ },
+ {
+ label: t('endpoints.analysis.one_w'),
+ value: '1w'
+ }
+ ]
+ const timePeriod = ref(timePeriodOptions[timePeriodOptions.length - 1].value)
+ const instances = ref([])
+ const selectedInstance = ref()
+ const chartThreeRef = ref(null)
+ const chartFourRef = ref(null)
+
+ const hasRunningInstances = computed(() => {
+ return evaluationSucceed.value && instances.value.length > 0
+ })
+
+ const renderCpuUsage = async () => {
+ const chartDom = chartThreeRef.value
+ const existingChart = echarts.getInstanceByDom(chartDom)
+ if (existingChart) {
+ existingChart.dispose()
+ await nextTick()
+ }
+ var myChart = echarts.init(chartDom)
+ const url = `/models/evaluations/${props.evaluationId}/cpu/${evaluation.value.task_id}/usage?last_duration=${timePeriod.value}`
+ let xAxisData = []
+ let seriesData = []
+ try {
+ const { data, error } = await useFetchApi(url).json()
+ if (error.value) {
+ console.log(`Failed to fetch cpu usage: ${error.value}`)
+ } else {
+ const monitorData = data.value.data?.result[0]
+ if (monitorData) {
+ xAxisData = monitorData?.values.map(item => timestampToDatetimeStr(item.timestamp))
+ seriesData = monitorData?.values.map(item => (item.value < 0 ? 0 : item.value))
+ }
+ }
+ } catch (error) {
+ console.log(`Failed to fetch http count: ${error}`)
+ }
+ myChart.setOption({
+ title: {
+ text: t('endpoints.analysis.cpuUsage')
+ },
+ tooltip: {
+ trigger: 'axis'
+ },
+ legend: {
+ align: 'right',
+ left: 'right',
+ data: ['usage in percent']
+ },
+ color: ['#3250BD', '#5271E3', '#223B99'],
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ show: false,
+ data: xAxisData
+ },
+ yAxis: {
+ type: 'value'
+ },
+ series: [
+ {
+ name: 'usage in percent',
+ type: 'line',
+ stack: 'Total',
+ data: seriesData
+ }
+ ]
+ })
+ }
+
+ const renderMemoryUsage = async () => {
+ const chartDom = chartFourRef.value
+ const existingChart = echarts.getInstanceByDom(chartDom)
+ if (existingChart) {
+ existingChart.dispose()
+ await nextTick()
+ }
+ var myChart = echarts.init(chartDom)
+ const url = `/models/evaluations/${props.evaluationId}/memory/${evaluation.value.task_id}/usage?last_duration=${timePeriod.value}`
+ let xAxisData = []
+ let seriesData = []
+ try {
+ const { data, error } = await useFetchApi(url).json()
+ if (error.value) {
+ console.log(`Failed to fetch memory usage: ${error.value}`)
+ } else {
+ const monitorData = data.value.data?.result[0]
+ if (monitorData) {
+ xAxisData = monitorData?.values.map(item => timestampToDatetimeStr(item.timestamp))
+ seriesData = monitorData?.values.map(item => item.value)
+ }
+ }
+ } catch (error) {
+ console.log(`Failed to fetch http count: ${error}`)
+ }
+ myChart.setOption({
+ title: {
+ text: t('endpoints.analysis.memoryUsage')
+ },
+ tooltip: {
+ trigger: 'axis'
+ },
+ legend: {
+ align: 'right',
+ left: 'right',
+ data: ['usage in GB']
+ },
+ color: ['#3250BD', '#5271E3', '#223B99'],
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ show: false,
+ data: xAxisData
+ },
+ yAxis: {
+ type: 'value'
+ },
+ series: [
+ {
+ name: 'usage in GB',
+ type: 'line',
+ stack: 'Total',
+ data: seriesData
+ }
+ ]
+ })
+ }
+
+ const refeshCurrentPeriod = () => {
+ if (hasRunningInstances.value) {
+ renderCpuUsage()
+ renderMemoryUsage()
+ }
+ }
+
onMounted(() => {
+ loadStore()
fetchTags()
fetchEvaluation()
handleResize()
@@ -445,6 +716,7 @@
onBeforeUnmount(() => {
unObserveResizeEvent()
+ cleanStore()
})
diff --git a/frontend/src/locales/en_js/evaluation.js b/frontend/src/locales/en_js/evaluation.js
index 91da55a65..efda4a5bf 100644
--- a/frontend/src/locales/en_js/evaluation.js
+++ b/frontend/src/locales/en_js/evaluation.js
@@ -44,7 +44,10 @@ export const evaluation = {
download: "Download Results",
evaluationFailed: "Evaluation Failed",
return: "Return",
- recreate: "Recreate"
+ recreate: "Recreate",
+ invalidDataset: "The dataset has been deleted or is invalid",
+ evaluationResultTab: "Evaluation result",
+ evaluationAnalysisTab: "Analysis"
},
list: {
title: "Model Evaluation",
diff --git a/frontend/src/locales/zh_hant_js/evaluation.js b/frontend/src/locales/zh_hant_js/evaluation.js
index 948e334fc..369941bfb 100644
--- a/frontend/src/locales/zh_hant_js/evaluation.js
+++ b/frontend/src/locales/zh_hant_js/evaluation.js
@@ -44,7 +44,10 @@ export const evaluation = {
download: "下載評測結果",
evaluationFailed: "評測失敗",
return: "返回",
- recreate: "重新創建"
+ recreate: "重新創建",
+ invalidDataset: "數據集已刪除或失效",
+ evaluationResultTab: "評測結果",
+ evaluationAnalysisTab: "分析"
},
list: {
title: "模型評測",
diff --git a/frontend/src/locales/zh_js/evaluation.js b/frontend/src/locales/zh_js/evaluation.js
index 827c2e209..e2a7c3382 100644
--- a/frontend/src/locales/zh_js/evaluation.js
+++ b/frontend/src/locales/zh_js/evaluation.js
@@ -44,7 +44,10 @@ export const evaluation = {
download: "下载评测结果",
evaluationFailed: "评测失败",
return: "返回",
- recreate: "重新创建"
+ recreate: "重新创建",
+ invalidDataset: "数据集已删除或失效",
+ evaluationResultTab: "评测结果",
+ evaluationAnalysisTab: "分析"
},
list: {
title: "模型评测",
diff --git a/frontend/src/stores/EvaluationDetailStore.js b/frontend/src/stores/EvaluationDetailStore.js
new file mode 100644
index 000000000..249cda40e
--- /dev/null
+++ b/frontend/src/stores/EvaluationDetailStore.js
@@ -0,0 +1,42 @@
+import { defineStore } from 'pinia'
+
+const getPersistKey = (evaluationId) =>
+ evaluationId ? `evaluation-detail-store-${evaluationId}` : null
+
+const useEvaluationDetailStore = defineStore('EvaluationDetail', () => {
+ const getActiveTabName = (evaluationId) => {
+ const key = getPersistKey(evaluationId)
+ if (key) {
+ const persistedName = localStorage.getItem(key)
+ if (persistedName) {
+ return persistedName
+ }
+ }
+ return ''
+ }
+
+ const setActiveTabName = (name, evaluationId) => {
+ if (evaluationId) {
+ localStorage.setItem(getPersistKey(evaluationId), name)
+ }
+ }
+
+ const clearStore = (evaluationId) => {
+ const key = getPersistKey(evaluationId)
+ if (key) {
+ localStorage.removeItem(key)
+ }
+ }
+
+ return {
+ getActiveTabName,
+ setActiveTabName,
+ clearStore
+ }
+}, {
+ persist: {
+ key: (context) => getPersistKey(context.store.$state.evaluationId)
+ }
+})
+
+export default useEvaluationDetailStore