Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,6 @@ module.exports = {
defineProps: true,
defineEmits: true,
defineExpose: true,
RemoteCompoentRef: true,
RemoteComponentRef: true,
},
};
2 changes: 0 additions & 2 deletions .lintstagedrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ const removeEslintIgnored = async (stagedFilenames) => {
const isIgnored = await Promise.all(stagedFilenames.map((file) => eslint.isPathIgnored(file)));
const filteredFiles = stagedFilenames.filter((_, i) => !isIgnored[i]);

console.log('filteredFiles:', filteredFiles);

return `eslint --max-warnings=0 ${filteredFiles.join(' ')}`;
};

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ RUN apk -U upgrade

COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
COPY startup.sh /usr/share
COPY ./scripts/startup.sh /usr/share

RUN chown -R nginx:nginx /var/cache/nginx && \
chown -R nginx:nginx /var/log/nginx && \
Expand Down
2 changes: 1 addition & 1 deletion charts/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ spec:
spec:
containers:
- name: datatunerx-ui
image: release.daocloud.io/datatunerx/datatunerx-ui:v0.0.1-dev-91deb6a
image: release.daocloud.io/datatunerx/datatunerx-ui:v0.0.1-dev-954f706
env:
- name: 'API_URL'
value: "https://10.33.1.10:6443"
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@vue/eslint-config-typescript": "^9.1.0",
"@vue/test-utils": "^2.3.1",
"@vue/vue3-jest": "^27.0.0-alpha.1",
"autoprefixer": "^10.4.16",
"babel-jest": "^27.0.6",
"commitizen": "^4.2.4",
"eslint": "^8.7.0",
Expand All @@ -76,6 +77,7 @@
"jest-junit": "^13.0.0",
"kubernetes-types": "^1.26.0",
"lint-staged": "^12.3.2",
"postcss": "^8.4.32",
"postcss-html": "^1.5.0",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
Expand All @@ -84,7 +86,7 @@
"stylelint-config-recess-order": "^4.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-sass-guidelines": "^10.0.0",
"tailwindcss": "^3.3.5",
"tailwindcss": "^3.3.6",
"ts-jest": "^27.0.4",
"typescript": "^4.9.5",
"unplugin-auto-import": "^0.17.2",
Expand Down
File renamed without changes.
2 changes: 0 additions & 2 deletions src/hooks/useWatchResource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//

// const lastResourceVersion = ref();

// function streamUpdates() {
Expand Down
2 changes: 1 addition & 1 deletion src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { App } from 'vue';
import {
createRouter, type RouteRecordRaw, type RouterHistory, type Router, createWebHistory,
} from 'vue-router';
Expand All @@ -18,7 +19,6 @@ import FinetuneExperimentCreate from '@/views/finetune-experiment/FinetuneExperi
import FinetuneExperimentJobDetail from '@/views/finetune-experiment-job/FinetuneExperimentJobDetail.vue';

import FinetuneRegistryList from '@/views/finetune-registry/FinetuneRegistryList.vue';
import { App } from 'vue';

const routes: Array<RouteRecordRaw> = [
{
Expand Down
136 changes: 118 additions & 18 deletions src/stores/experiment.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,137 @@
import { i18n } from '@/plugins/vue-i18n';
import { FinetuneExperiment, finetuneExperimentClient } from '@/api/finetune-experiment';
import { nError } from '@/utils/useNoty';
import { useNamespaceStore } from './namespace';

function onNewLine(buffer: string, fn: (chunk: string) => void): string {
const newLineIndex = buffer.indexOf('\n');

if (newLineIndex === -1) {
return buffer;
}
const chunk = buffer.slice(0, newLineIndex);
const newBuffer = buffer.slice(newLineIndex + 1);

fn(chunk);

return onNewLine(newBuffer, fn);
}

export const useExperimentStore = defineStore('experiment', {
state: () => ({
experiment: [] as FinetuneExperiment[],
experimentMap: {} as { [key: string]: FinetuneExperiment },
lastResourceVersion: '' as string,
experiments: [] as FinetuneExperiment[],
datasetWithExperiment: {} as { [key: string]: string[] },
hyperparameterWithExperiment: {} as { [key: string]: string[] },
}),

actions: {
async fetchExperiment() {
const { namespace } = storeToRefs(useNamespaceStore());
const { t } = useI18n();

async fetchExperiment(namespace: string) {
try {
const { data } = await finetuneExperimentClient.list(namespace.value);
const { data } = await finetuneExperimentClient.list(namespace);

this.experiment = data.items;
this.lastResourceVersion = data.metadata.resourceVersion as string;
this.experiments = data.items;

this.experimentMap = this.experiment.reduce((map, experiment) => {
if (experiment.metadata?.name) {
const newMap = { ...map };
// this.streamUpdates(namespace, this.lastResourceVersion);

newMap[experiment.metadata.name] = experiment;
const { datasetWithExperiment, hyperparameterWithExperiment } = this.experiments.reduce(
(acc, experiment) => {
experiment.spec?.finetuneJobs.forEach(({ spec }) => {
if (spec?.finetune.finetuneSpec.dataset) {
const dataset = spec?.finetune.finetuneSpec.dataset;

return newMap;
}
if (!acc.datasetWithExperiment[dataset]) {
acc.datasetWithExperiment[dataset] = [];
}

acc.datasetWithExperiment[dataset].push(experiment.metadata?.name as string);
}
if (spec?.finetune.finetuneSpec.hyperparameter?.hyperparameterRef) {
const hyperparameterRef = spec?.finetune.finetuneSpec.hyperparameter?.hyperparameterRef;

if (!acc.hyperparameterWithExperiment[hyperparameterRef]) {
acc.hyperparameterWithExperiment[hyperparameterRef] = [];
}
acc.hyperparameterWithExperiment[hyperparameterRef].push(
experiment.metadata?.name as string,
);
}
});

return map;
}, {} as { [key: string]: FinetuneExperiment });
return acc;
},
{
datasetWithExperiment: {} as { [key: string]: string[] },
hyperparameterWithExperiment: {} as { [key: string]: string[] },
},
);

this.datasetWithExperiment = datasetWithExperiment;
this.hyperparameterWithExperiment = hyperparameterWithExperiment;
} catch (error) {
nError(t('common.fetchFailed'), error);
nError(i18n.t('common.fetchFailed'), error);
}
},

streamUpdates(namespace: string, lastResourceVersion: string) {
fetch(
`/apis/finetune.datatunerx.io/v1beta1/namespaces/${namespace}/finetuneexperiments?watch=1&resourceVersion=${lastResourceVersion}`,
{
headers: {
Authorization: `Bearer ${process.env.VUE_APP_AUTH}`,
},
},
)
.then((response) => {
const stream = response.body?.getReader();
const utf8Decoder = new TextDecoder('utf-8');
const buffer = '';

return stream?.read().then((result) => this.processText(result, buffer, utf8Decoder, stream));
})
.catch(() => {
console.log('Error! Retrying in 5 seconds...');
setTimeout(() => this.streamUpdates(namespace, lastResourceVersion), 5000);
});
},

processText(
{ done, value }: ReadableStreamReadResult<Uint8Array>,
buffer: string,
utf8Decoder: TextDecoder,
stream: ReadableStreamDefaultReader<Uint8Array>,
): Promise<void> {
if (done) {
// eslint-disable-next-line no-console
console.log('Request terminated');

return Promise.resolve();
}
// eslint-disable-next-line no-param-reassign
buffer += utf8Decoder.decode(value);
// eslint-disable-next-line no-param-reassign
buffer = onNewLine(buffer, (chunk) => {
if (chunk.trim().length === 0) {
return;
}
try {
const event = JSON.parse(chunk);

if (event.type === 'ADDED') {
this.experiments.push(event.object);
} else if (event.type === 'DELETED') {
this.experiments = this.experiments.filter(
(experiment) => experiment.metadata?.name !== event.object.metadata.name,
);
}

console.log(event.type, event.object.metadata.name);
} catch (error) {
console.log('Error while parsing', chunk, '\n', error);
}
});

return stream.read().then((result) => this.processText(result, buffer, utf8Decoder, stream));
},
},
});
5 changes: 2 additions & 3 deletions src/stores/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NAMESPACE } from '@/utils/constant';
import { Namespace } from 'kubernetes-types/core/v1';
import { defineStore } from 'pinia';
import { listNamespaces } from '@/api/namespace';
import { i18n } from '@/plugins/vue-i18n';

export const useNamespaceStore = defineStore('namespace', {
state: () => ({
Expand All @@ -11,8 +12,6 @@ export const useNamespaceStore = defineStore('namespace', {
}),
actions: {
async fetchNamespace() {
const { t } = useI18n();

try {
const { data } = await listNamespaces(); // 请求数据

Expand All @@ -25,7 +24,7 @@ export const useNamespaceStore = defineStore('namespace', {
this.setNamespace(firstNamespace.metadata?.name || '');
}
} catch (error) {
nError(t('common.fetchFailed'), error);
nError(i18n.t('common.fetchFailed'), error);
}
},
setNamespace(val: string) {
Expand Down
42 changes: 35 additions & 7 deletions src/views/dataset/DatasetDetail.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts" setup>
import { useNamespaceStore } from '@/stores/namespace';

import { useExperimentStore } from '@/stores/experiment';
import { useDataset, useDeleteDataset } from './composition/dataset';

const { t } = useI18n();
Expand All @@ -13,10 +14,22 @@ const name = route.params.name as string;

const { dataset, fetchDataset } = useDataset();

onBeforeMount(() => {
fetchDataset(namespace.value, name);
fetchDataset(namespace.value, name);

const { fetchExperiment } = useExperimentStore();
const { datasetWithExperiment } = storeToRefs(useExperimentStore());

const referencedByExperiments = computed(() => datasetWithExperiment.value[name]);
const canDelete = computed(() => {
if (!referencedByExperiments.value) {
return true;
}

return referencedByExperiments.value?.length === 0;
});

fetchExperiment(namespace.value);

const infos = computed(() => {
const languages = dataset.value?.spec?.datasetMetadata.languages?.map((lang) => t(`views.Dataset.${lang}`)).join(', ') ?? '-';

Expand Down Expand Up @@ -131,12 +144,27 @@ const { onConfirmDelete } = useDeleteDataset(namespace.value, toList);
{{ t('common.edit') }}
</dao-dropdown-item>
<dao-dropdown-item type="divider" />
<dao-dropdown-item
color="red"
@click="onConfirmDelete(dataset?.metadata?.name)"

<dao-popover
placement="left"
:disabled="canDelete"
>
{{ t('common.delete') }}
</dao-dropdown-item>
<dao-dropdown-item
:disabled="!canDelete"
color="red"
@click="onConfirmDelete(dataset?.metadata?.name)"
>
{{ t('common.delete') }}
</dao-dropdown-item>

<template #content>
<dao-message
simple
type="error"
:content="`该数据集被实验 ${ referencedByExperiments } 使用,无法删除`"
/>
</template>
</dao-popover>
</dao-dropdown-menu>
</template>
</dao-dropdown>
Expand Down
8 changes: 7 additions & 1 deletion src/views/dataset/DatasetList.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts" setup>

import { useNamespaceStore } from '@/stores/namespace';
import { Dataset, datasetClient } from '@/api/dataset';
import { useQueryTable } from '@/hooks/useQueryTable';
import { useExperimentStore } from '@/stores/experiment';
import DatasetItem from './components/DatasetItem.vue';
import { useDeleteDataset } from './composition/dataset';

Expand All @@ -13,6 +13,11 @@ const {
isLoading, pagedData, page, pageSize, total, handleRefresh, search,
} = useQueryTable<Dataset>(async () => datasetClient.list(namespace.value));

const { fetchExperiment } = useExperimentStore();
const { datasetWithExperiment } = storeToRefs(useExperimentStore());

fetchExperiment(namespace.value);

const { onConfirmDelete } = useDeleteDataset(namespace.value, handleRefresh);

watch(namespace, handleRefresh);
Expand Down Expand Up @@ -47,6 +52,7 @@ const onCreate = () => router.push({ name: 'DatasetCreate' });
:key="dataset.metadata?.name"
class="mt-[20px]"
:data="dataset"
:referenced-by-experiments="datasetWithExperiment[dataset.metadata?.name as string]"
@on-delete="onConfirmDelete(dataset.metadata?.name)"
/>
<dao-pagination
Expand Down
Loading