From bd92679a317f64f70b5e818b6501ea751e497d37 Mon Sep 17 00:00:00 2001 From: wshyao Date: Fri, 25 Jul 2025 05:48:31 +0000 Subject: [PATCH] merge conflict --- .../evaluations/EvaluationDetail.vue | 444 ++++++++++++++---- frontend/src/locales/en_js/evaluation.js | 5 +- frontend/src/locales/zh_hant_js/evaluation.js | 5 +- frontend/src/locales/zh_js/evaluation.js | 5 +- frontend/src/stores/EvaluationDetailStore.js | 42 ++ 5 files changed, 412 insertions(+), 89 deletions(-) create mode 100644 frontend/src/stores/EvaluationDetailStore.js 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 @@ - -
- - - - - + +
+ +
+ - - - - - - - - -
-
-
- -
-
{{ $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