Skip to content

Commit 1466136

Browse files
zhendizhendi.wangCYan1203
authored
Model evaluation (#850)
* Model evaluation * 模型评测在框架改变后重新获取数据集选项 * fix render function * migrate evaluation table styles from SCSS to vanilla CSS syntax * update model operation buttons --------- Co-authored-by: zhendi.wang <[email protected]> Co-authored-by: niuran <[email protected]>
1 parent 9db5d9f commit 1466136

File tree

23 files changed

+1634
-90
lines changed

23 files changed

+1634
-90
lines changed
+5
Loading
Loading
Loading
Loading

frontend/src/assets/stylesheets/element-plus/input.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
:root {
22
/* 所有的input 添加 */
3-
.el-textarea__inner, .el-input__wrapper, .el-select__wrapper {
3+
.el-textarea__inner, .el-input__wrapper, .el-select__wrapper, .el-cascader {
44
border: 1px solid transparent;
55
border-radius: var(--border-radius-md);-radius: var(--border-radius-md);
66
}
77

88
/* default focus style */
9-
.el-textarea__inner:focus, .el-input__wrapper.is-focus, .el-select__wrapper.is-focused {
9+
.el-textarea__inner:focus, .el-input__wrapper.is-focus, .el-select__wrapper.is-focused, .el-cascader.is-focused {
1010
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05), 0px 0px 0px 4px rgba(77, 106, 214, 0.24);
1111
border: 1px solid var(--Brand-300);
1212
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
<template>
2+
<div class="evaluation-detail md:px-5">
3+
<div class="bg-gray-25 border-b border-gray-200">
4+
<div class="max-w-[1280px] m-auto py-8">
5+
<div class="mb-4 flex items-center justify-between">
6+
<a
7+
href="/resource-console"
8+
class="text-gray-700 text-2xl font-medium flex items-center gap-2"
9+
>
10+
<SvgIcon name="chevron_left" />
11+
{{ evaluation.task_name }}
12+
</a>
13+
<a
14+
v-if="evaluation.download_url"
15+
download
16+
:href="evaluation.download_url"
17+
class="btn btn-primary btn-md"
18+
>
19+
<SvgIcon
20+
name="download"
21+
class="mr-1"
22+
style="width: 14px; height: 14px"
23+
/>
24+
{{ $t('evaluation.detail.download') }}</a
25+
>
26+
</div>
27+
28+
<div class="flex items-center gap-5 mb-4">
29+
<div class="flex items-center">
30+
<div class="text-gray-500 text-base mr-1">
31+
{{ $t('evaluation.detail.creationTime') }}:
32+
</div>
33+
<div class="text-gray-700 text-base">
34+
{{ formatDate(evaluation.submit_time) }}
35+
</div>
36+
</div>
37+
<SvgIcon name="vertical_divider" />
38+
<div class="flex items-center">
39+
<div class="text-gray-500 text-base mr-1">
40+
{{ $t('evaluation.detail.evaluationModel') }}:
41+
</div>
42+
<div
43+
class="text-gray-700 text-base"
44+
v-if="evaluation.repo_ids"
45+
>
46+
<a
47+
:href="`/models/${evaluation.repo_ids[0]}`"
48+
class="text-brand-700 underline"
49+
>
50+
{{ evaluation.repo_ids[0].split('/')[1] }}
51+
</a>
52+
</div>
53+
</div>
54+
</div>
55+
56+
<div class="mb-4">
57+
<span class="text-gray-500 text-base">
58+
{{ $t('evaluation.detail.desc') }}:
59+
</span>
60+
<span class="text-gray-700 text-base">
61+
{{ evaluation.task_desc }}
62+
</span>
63+
</div>
64+
65+
<div
66+
class="border border-gray-200 rounded-md px-5 py-4 mb-4 flex flex-col gap-4"
67+
>
68+
<template
69+
v-for="category in groupedDatasets"
70+
:key="category.name"
71+
>
72+
<div
73+
class="flex items-center gap-4"
74+
v-if="category.datasets.length"
75+
>
76+
<div
77+
class="border border-gray-300 rounded-sm flex items-center gap-1 px-[6px] py-[2px]"
78+
>
79+
<SvgIcon name="dot_black" />
80+
<div class="text-gray-700 text-xs">{{ category.name }}</div>
81+
</div>
82+
<div class="flex items-center gap-3">
83+
<template
84+
v-for="(dataset, index) in category.datasets"
85+
:key="dataset"
86+
>
87+
<div class="text-gray-700 text-base">{{ dataset }}</div>
88+
<SvgIcon
89+
v-if="index < category.datasets.length - 1"
90+
name="vertical_divider"
91+
/>
92+
</template>
93+
</div>
94+
</div>
95+
</template>
96+
</div>
97+
</div>
98+
</div>
99+
100+
<div class="max-w-[1280px] m-auto bg-white pt-4">
101+
<el-tabs
102+
v-model="activeName"
103+
@tab-click="handleTabClick"
104+
>
105+
<el-tab-pane
106+
v-for="tab in tabsList"
107+
:key="tab.name"
108+
:label="tab.label"
109+
:name="tab.name"
110+
>
111+
<el-table
112+
v-loading="loading"
113+
:data="getTableData(tab.name)"
114+
:border="true"
115+
header-row-class-name="evaluation-table-header-row"
116+
header-cell-class-name="evaluation-table-header-cell"
117+
row-class-name="evaluation-table-row"
118+
cell-class-name="evaluation-table-row-cell"
119+
class="evaluation-table mt-6 mb-10 rounded-xl"
120+
>
121+
<el-table-column
122+
prop="dataset"
123+
:label="$t('evaluation.detail.dataset')"
124+
/>
125+
<el-table-column
126+
prop="metric"
127+
:label="$t('evaluation.detail.metrics')"
128+
/>
129+
<el-table-column
130+
prop="score"
131+
:label="$t('evaluation.detail.scores')"
132+
:sortable="true"
133+
:sort-method="
134+
(a, b) => {
135+
return parseFloat(a.score) > parseFloat(b.score) ? 1 : -1
136+
}
137+
"
138+
/>
139+
<template #empty>
140+
<span v-if="error">{{ error }}</span>
141+
<span v-else>{{ $t('common.noData') }}</span>
142+
</template>
143+
</el-table>
144+
</el-tab-pane>
145+
</el-tabs>
146+
</div>
147+
</div>
148+
</template>
149+
150+
<script setup>
151+
import { ref, onMounted, computed } from 'vue'
152+
import { useI18n } from 'vue-i18n'
153+
import { ElMessage } from 'element-plus'
154+
import useFetchApi from '@/packs/useFetchApi'
155+
import { formatDate } from '@/packs/datetimeUtils'
156+
157+
const props = defineProps({
158+
evaluationId: {
159+
type: String,
160+
required: true
161+
}
162+
})
163+
164+
const { t, locale } = useI18n()
165+
166+
const evaluation = ref({})
167+
const evaluationResult = ref({})
168+
const scoresKey = ref('')
169+
const activeName = ref('all')
170+
const loading = ref(false)
171+
const error = ref('')
172+
const categories = ref([])
173+
174+
const groupedDatasets = computed(() => {
175+
return categories.value.map((category) => ({
176+
name: locale.value === 'en' ? category.name : category.show_name,
177+
datasets:
178+
evaluation.value.datasets
179+
?.filter((d) => d.tags.some((t) => t.name === category.name))
180+
.map((d) => d.repo_id.split('/')[1]) || []
181+
}))
182+
})
183+
184+
const datasets = computed(() => {
185+
if (!evaluationResult.value) return []
186+
return Object.keys(evaluationResult.value).filter(
187+
(key) => key !== 'summary'
188+
)
189+
})
190+
191+
const tabsList = computed(() => {
192+
return [
193+
{ name: 'all', label: t('evaluation.detail.all') },
194+
...datasets.value.map((dataset) => ({
195+
name: dataset,
196+
label: dataset
197+
}))
198+
]
199+
})
200+
201+
const getScoresKey = (result) => {
202+
if (!result?.summary?.column) return ''
203+
const keyColumn = result.summary.column.find((col) => !!col.customizeRender)
204+
return keyColumn?.key || ''
205+
}
206+
207+
const getTableData = (tab) => {
208+
if (!evaluationResult.value?.summary) return []
209+
210+
const source =
211+
tab === 'all'
212+
? evaluationResult.value.summary.data
213+
: evaluationResult.value[tab]?.data
214+
215+
return (
216+
source?.map((item) => ({
217+
dataset: item.dataset,
218+
metric: item.metric,
219+
score: item[scoresKey.value]
220+
})) || []
221+
)
222+
}
223+
224+
const handleTabClick = (tab) => {
225+
activeName.value = tab.props.name
226+
}
227+
228+
const fetchTags = async () => {
229+
const { data } = await useFetchApi(
230+
'/tags?scope=dataset&category=evaluation'
231+
).json()
232+
233+
if (data.value?.data) {
234+
categories.value = data.value.data
235+
} else {
236+
throw new Error('Failed to fetch tags')
237+
}
238+
}
239+
240+
const fetchEvaluation = async () => {
241+
try {
242+
loading.value = true
243+
const { data } = await useFetchApi(
244+
`/evaluations/${props.evaluationId}`
245+
).json()
246+
247+
if (!data.value) {
248+
throw new Error(data.value?.msg || 'Failed to fetch evaluation')
249+
}
250+
251+
evaluation.value = data.value.data
252+
253+
if (evaluation.value.result_url) {
254+
const result = await fetch(evaluation.value.result_url)
255+
evaluationResult.value = await result.json()
256+
scoresKey.value = getScoresKey(evaluationResult.value)
257+
}
258+
} catch (e) {
259+
error.value = e.message
260+
ElMessage({
261+
message: t('all.fetchError'),
262+
type: 'error'
263+
})
264+
} finally {
265+
loading.value = false
266+
}
267+
}
268+
269+
onMounted(() => {
270+
fetchTags()
271+
fetchEvaluation()
272+
})
273+
</script>
274+
275+
<style scoped>
276+
.evaluation-table :deep(.el-table__empty-block) {
277+
border-left: 1px solid #eaecf0;
278+
border-right: 1px solid #eaecf0;
279+
}
280+
281+
.evaluation-table .evaluation-table-header-row .evaluation-table-header-cell {
282+
padding: 12px 24px;
283+
font-size: 12px;
284+
font-weight: 400;
285+
color: var(--gray-600);
286+
border-top: 1px solid #eaecf0;
287+
background-color: #f9fafb;
288+
}
289+
290+
.evaluation-table
291+
.evaluation-table-header-row
292+
.evaluation-table-header-cell:nth-child(1) {
293+
border-left: 1px solid #eaecf0;
294+
border-top-left-radius: 12px;
295+
}
296+
297+
.evaluation-table
298+
.evaluation-table-header-row
299+
.evaluation-table-header-cell:nth-last-child(1) {
300+
border-right: 1px solid #eaecf0;
301+
border-top-right-radius: 12px;
302+
}
303+
304+
.evaluation-table
305+
.evaluation-table-header-row
306+
.evaluation-table-header-cell
307+
.cell {
308+
line-height: 18px;
309+
}
310+
311+
.evaluation-table .evaluation-table-row .evaluation-table-row-cell {
312+
padding: 16px 24px;
313+
font-size: 14px;
314+
color: var(--gray-900);
315+
}
316+
317+
.evaluation-table
318+
.evaluation-table-row
319+
.evaluation-table-row-cell:nth-child(1) {
320+
border-left: 1px solid #eaecf0;
321+
}
322+
323+
.evaluation-table
324+
.evaluation-table-row
325+
.evaluation-table-row-cell:nth-last-child(1) {
326+
border-right: 1px solid #eaecf0;
327+
}
328+
329+
.evaluation-table .evaluation-table-row .evaluation-table-row-cell .cell {
330+
line-height: 22px;
331+
}
332+
333+
.evaluation-table
334+
.evaluation-table-row:nth-last-child(1)
335+
.evaluation-table-row-cell:nth-child(1) {
336+
border-bottom-left-radius: 12px;
337+
}
338+
339+
.evaluation-table
340+
.evaluation-table-row:nth-last-child(1)
341+
.evaluation-table-row-cell:nth-last-child(1) {
342+
border-bottom-right-radius: 12px;
343+
}
344+
</style>

0 commit comments

Comments
 (0)