diff --git a/src/hooks/use-download-logs.tsx b/src/hooks/use-download-logs.tsx
new file mode 100644
index 00000000..7b5cb90a
--- /dev/null
+++ b/src/hooks/use-download-logs.tsx
@@ -0,0 +1,71 @@
+import { HandlerOptions } from '@/hooks/use-chunk-fetch';
+import useDownloadStream from '@/hooks/use-download-stream';
+import { useIntl } from '@umijs/max';
+import { Progress, notification } from 'antd';
+import dayjs from 'dayjs';
+
+const renderMessage = (title: string) => {
+ return (
+
+ {title}
+
+ );
+};
+
+const createFileName = (name: string) => {
+ const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss');
+ const fileName = `${name}_${timestamp}.txt`;
+ return fileName;
+};
+
+const useDownloadLogs = () => {
+ const { downloadStream } = useDownloadStream();
+ const intl = useIntl();
+ const [api, contextHolder] = notification.useNotification({
+ stack: { threshold: 1 }
+ });
+
+ const downloadNotification = (
+ data: HandlerOptions & {
+ filename: string;
+ duration?: number;
+ chunkRequestRef: any;
+ }
+ ) => {
+ api.open({
+ duration: data.duration,
+ message: renderMessage(data.filename),
+ key: data.filename,
+ closeIcon: (
+ {intl.formatMessage({ id: 'common.button.cancel' })}
+ ),
+ description: ,
+ onClose() {
+ data.chunkRequestRef?.current?.abort();
+ notification.destroy?.(data.filename);
+ }
+ });
+ };
+
+ const handleDownloadLog = async (params: { url: string; name: string }) => {
+ downloadStream({
+ url: params.url,
+ filename: createFileName(params.name),
+ downloadNotification
+ });
+ };
+
+ return {
+ onDownloadLog: handleDownloadLog,
+ contextHolder
+ };
+};
+
+export default useDownloadLogs;
diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts
index a600da2f..d1bd399f 100644
--- a/src/locales/en-US/common.ts
+++ b/src/locales/en-US/common.ts
@@ -229,7 +229,7 @@ export default {
'common.options.none': 'None',
'common.options.auto': 'Auto',
'common.search.empty': 'No matching results found.',
- 'common.button.downloadLog': 'Download Log',
+ 'common.button.downloadLog': 'Download Logs',
'common.button.faq': 'FAQ',
'common.button.moreInfo': 'More Info',
'common.text.warning': 'Warning',
diff --git a/src/pages/benchmark/components/compare-conditions.tsx b/src/pages/benchmark/components/compare-conditions.tsx
deleted file mode 100644
index cf7e98c0..00000000
--- a/src/pages/benchmark/components/compare-conditions.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import Wrapper from '@/components/label-selector/wrapper';
-import { DeleteOutlined } from '@ant-design/icons';
-import { useHover } from 'ahooks';
-import { Button, Divider, Flex, Input, Popover } from 'antd';
-import React, { useRef, useState } from 'react';
-import styled from 'styled-components';
-
-const ItemContainer = styled.div`
- display: flex;
- margin-bottom: 12px;
- align-items: center;
- justify-content: flex-start;
-
- .seprator {
- display: flex;
- flex: none;
- width: 12px;
- align-items: center;
- justify-content: center;
- color: var(--ant-color-text-tertiary);
- }
-
- .btn {
- width: 24px;
- margin-left: 10px;
- flex: none;
- }
-`;
-
-interface MetadataListProps {
- dataList: any[];
- label: React.ReactNode;
- description?: React.ReactNode;
- disabled?: boolean;
- btnText?: string;
- onAdd?: () => void;
- onDelete?: (index: number, item: any) => void;
- children?: (item: any, index: number) => React.ReactNode;
-}
-
-const MetadataList: React.FC = ({
- dataList,
- label,
- description,
- disabled,
- btnText,
- children,
- onDelete,
- onAdd
-}) => {
- return (
-
- {dataList.map((item, index) => (
- {children?.(item, index)}
- ))}
-
- );
-};
-
-const DeleteWrapper = styled.span`
- cursor: pointer;
- color: var(--ant-color-text-tertiary);
- &:hover {
- color: var(--ant-color-error-text-hover);
- }
-`;
-
-const Box = styled.div`
- width: 100%;
- .ant-input-suffix {
- opacity: 0;
- transition: opacity 0.2s ease-in-out;
- }
- &:hover {
- .ant-input-suffix {
- opacity: 1;
- transition: opacity 0.2s ease-in-out;
- }
- }
-`;
-
-const Container = styled.div`
- display: flex;
- align-items: center;
- border: 1px solid var(--ant-color-border);
- border-radius: 4px;
- padding: 8.5px 11px;
- height: 40px;
- width: 240px;
- cursor: pointer;
- &:hover {
- border-color: var(--ant-color-primary-hover);
- }
- &:active {
- box-shadow: var(--ant-input-active-shadow);
- }
- .holder {
- color: var(--ant-color-text-placeholder);
- }
-`;
-const CompareConditions: React.FC = () => {
- const [open, setOpen] = useState(false);
- const [filters, setFilters] = useState([]);
- const containerRef = useRef(null);
- const isHovering = useHover(containerRef);
-
- const onAdd = () => {
- setFilters((prev) => [...prev, '']);
- };
-
- const onDelete = (index: number, item: any) => {
- setFilters((prev) => prev.filter((_, i) => i !== index));
- };
-
- const handleOnChange = (value: string, index: number) => {
- const newFilters = [...filters];
- newFilters[index] = value;
- setFilters(newFilters);
- };
-
- const FiltersDrop = () => {
- return (
- <>
-
- {(item, index) => (
-
- 1 ? (
-
- onDelete?.(index, item)} />
-
- ) : null
- }
- onChange={(e) => handleOnChange(e.target.value, index)}
- />
-
- )}
-
-
-
-
-
-
- >
- );
- };
-
- return (
-
-
- Export filter
-
-
- );
-};
-
-export default CompareConditions;
diff --git a/src/pages/benchmark/components/detail-content.tsx b/src/pages/benchmark/components/detail-content.tsx
index 4c050980..160be161 100644
--- a/src/pages/benchmark/components/detail-content.tsx
+++ b/src/pages/benchmark/components/detail-content.tsx
@@ -8,7 +8,6 @@ import { BenchmarkListItem } from '../config/types';
import useQueryDetail from '../services/use-query-detail';
import Configure from './configure';
import Environment from './environment';
-import Logs from './logs';
import Summary from './summary';
const Details: React.FC<{ currentData?: BenchmarkListItem }> = ({
@@ -43,13 +42,13 @@ const Details: React.FC<{ currentData?: BenchmarkListItem }> = ({
label: intl.formatMessage({ id: 'benchmark.detail.environment.title' }),
children: ,
icon:
- },
- {
- key: 'logs',
- label: intl.formatMessage({ id: 'benchmark.detail.logs.title' }),
- children: ,
- icon:
}
+ // {
+ // key: 'logs',
+ // label: intl.formatMessage({ id: 'benchmark.detail.logs.title' }),
+ // children: ,
+ // icon:
+ // }
];
const handleChangeTab = (key: string) => {
diff --git a/src/pages/benchmark/components/row-actions.tsx b/src/pages/benchmark/components/row-actions.tsx
new file mode 100644
index 00000000..d0846160
--- /dev/null
+++ b/src/pages/benchmark/components/row-actions.tsx
@@ -0,0 +1,88 @@
+import DropdownButtons from '@/components/drop-down-buttons';
+import IconFont from '@/components/icon-font';
+import icons from '@/components/icon-font/icons';
+import useDownloadLogs from '@/hooks/use-download-logs';
+import { DownloadOutlined } from '@ant-design/icons';
+import { BENCHMARKS_API } from '../apis';
+import { BenchmarkStatusValueMap } from '../config';
+import { BenchmarkListItem as ListItem } from '../config/types';
+
+const actionList = [
+ {
+ key: 'edit',
+ label: 'common.button.edit',
+ icon: icons.EditOutlined
+ },
+ {
+ label: 'common.button.viewlog',
+ key: 'viewlog',
+ status: [
+ BenchmarkStatusValueMap.Claimed,
+ BenchmarkStatusValueMap.Running,
+ BenchmarkStatusValueMap.Error,
+ BenchmarkStatusValueMap.Completed
+ ],
+ icon:
+ },
+ {
+ label: 'common.button.downloadLog',
+ key: 'download',
+ status: [
+ BenchmarkStatusValueMap.Claimed,
+ BenchmarkStatusValueMap.Running,
+ BenchmarkStatusValueMap.Error,
+ BenchmarkStatusValueMap.Completed
+ ],
+ icon:
+ },
+ {
+ key: 'delete',
+ label: 'common.button.delete',
+ icon: icons.DeleteOutlined,
+ props: {
+ danger: true
+ }
+ }
+];
+
+interface RowActionsProps {
+ record: ListItem;
+ handleSelect: (key: string, record: ListItem) => void;
+}
+
+const RowActions: React.FC = (props) => {
+ const { record, handleSelect } = props;
+ const { onDownloadLog, contextHolder } = useDownloadLogs();
+
+ const actions = actionList.filter((action) => {
+ if (action.key === 'viewlog' || action.key === 'download') {
+ return action.status?.includes(record.state);
+ }
+
+ return true;
+ });
+
+ const handleDownloadLog = async () => {
+ onDownloadLog({
+ url: `${BENCHMARKS_API}/${record.id}/logs`,
+ name: record.name
+ });
+ };
+
+ const onSelect = (val: string) => {
+ if (val === 'download') {
+ handleDownloadLog();
+ return;
+ }
+ handleSelect(val, record);
+ };
+
+ return (
+ <>
+ {contextHolder}
+
+ >
+ );
+};
+
+export default RowActions;
diff --git a/src/pages/benchmark/components/view-logs-modal.tsx b/src/pages/benchmark/components/view-logs-modal.tsx
new file mode 100644
index 00000000..90b960d4
--- /dev/null
+++ b/src/pages/benchmark/components/view-logs-modal.tsx
@@ -0,0 +1,104 @@
+import LogsViewer from '@/components/logs-viewer/virtual-log-list';
+import { useIntl } from '@umijs/max';
+import { Modal } from 'antd';
+import React, { useCallback, useEffect } from 'react';
+
+type ViewModalProps = {
+ open: boolean;
+ url: string;
+ tail?: number;
+ status?: string;
+ onCancel: () => void;
+};
+
+const ViewLogsModal: React.FC = (props) => {
+ const intl = useIntl();
+ const { open, onCancel, tail, url, status } = props || {};
+ const logsViewerRef = React.useRef(null);
+ const requestRef = React.useRef(null);
+ const contentRef = React.useRef(null);
+
+ const handleCancel = useCallback(() => {
+ logsViewerRef.current?.abort();
+ onCancel();
+ }, [onCancel]);
+
+ useEffect(() => {
+ const handleKeyDown = (e: any) => {
+ if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
+ e.preventDefault();
+ if (contentRef.current) {
+ const range = document.createRange();
+ range.selectNodeContents(contentRef.current);
+ const selection = window.getSelection();
+ selection?.removeAllRanges();
+ selection?.addRange(range);
+ }
+ }
+ };
+
+ if (open) {
+ document.addEventListener('keydown', handleKeyDown);
+ }
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [open]);
+
+ useEffect(() => {
+ if (!url) return;
+ if (!open) {
+ logsViewerRef.current?.abort();
+ requestRef.current?.current?.cancel?.();
+ }
+
+ return () => {
+ logsViewerRef.current?.abort();
+ requestRef.current?.current?.cancel?.();
+ };
+ }, [url, open]);
+
+ return (
+
+
+ {intl.formatMessage({ id: 'common.button.viewlog' })}
+
+
+ }
+ zIndex={3000}
+ open={open}
+ centered={true}
+ onCancel={handleCancel}
+ destroyOnHidden={true}
+ closeIcon={true}
+ maskClosable={false}
+ keyboard={true}
+ styles={{
+ wrapper: {
+ borderRadius: 0
+ }
+ }}
+ width="100%"
+ footer={null}
+ >
+
+
+
+
+ );
+};
+
+export default ViewLogsModal;
diff --git a/src/pages/benchmark/hooks/use-benchmark-columns.tsx b/src/pages/benchmark/hooks/use-benchmark-columns.tsx
index de3b34b7..64fb5eb4 100644
--- a/src/pages/benchmark/hooks/use-benchmark-columns.tsx
+++ b/src/pages/benchmark/hooks/use-benchmark-columns.tsx
@@ -1,7 +1,5 @@
// columns.ts
import AutoTooltip from '@/components/auto-tooltip';
-import DropdownButtons from '@/components/drop-down-buttons';
-import icons from '@/components/icon-font/icons';
import StatusTag from '@/components/status-tag';
import { tableSorter } from '@/config/settings';
import { useIntl } from '@umijs/max';
@@ -10,25 +8,10 @@ import { ColumnsType } from 'antd/es/table';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useMemo } from 'react';
+import RowActions from '../components/row-actions';
import { BenchmarkStatus, BenchmarkStatusLabelMap } from '../config';
import { BenchmarkListItem as ListItem } from '../config/types';
-const actionList = [
- {
- key: 'edit',
- label: 'common.button.edit',
- icon: icons.EditOutlined
- },
- {
- key: 'delete',
- label: 'common.button.delete',
- icon: icons.DeleteOutlined,
- props: {
- danger: true
- }
- }
-];
-
const useBenchmarkColumns = (
sortOrder: string[],
handleSelect: (val: string, record: ListItem) => void,
@@ -174,10 +157,7 @@ const useBenchmarkColumns = (
showTitle: false
},
render: (value: string, record: ListItem) => (
- handleSelect(val, record)}
- >
+
)
}
];
diff --git a/src/pages/benchmark/hooks/use-view-logs.ts b/src/pages/benchmark/hooks/use-view-logs.ts
new file mode 100644
index 00000000..91e02ae4
--- /dev/null
+++ b/src/pages/benchmark/hooks/use-view-logs.ts
@@ -0,0 +1,44 @@
+import { useState } from 'react';
+import { BENCHMARKS_API } from '../apis';
+import { BenchmarkListItem as ListItem } from '../config/types';
+
+const useViewLogs = () => {
+ const [openModalStatus, setOpenModalStatus] = useState<{
+ open: boolean;
+ url: string;
+ tail?: number;
+ status?: string;
+ }>({
+ open: false,
+ url: '',
+ tail: 1000,
+ status: undefined
+ });
+
+ const openModal = (row?: ListItem) => {
+ setOpenModalStatus({
+ open: true,
+ url: `${BENCHMARKS_API}/${row?.id}/logs`,
+ tail: 1000,
+ status: row?.state || undefined
+ });
+ };
+
+ const closeModal = () => {
+ setOpenModalStatus({
+ open: false,
+ url: '',
+ tail: 1000,
+ status: undefined
+ });
+ };
+
+ return {
+ openViewLogsModalStatus: openModalStatus,
+ setOpenViewLogsModalStatus: setOpenModalStatus,
+ openViewLogsModal: openModal,
+ closeViewLogsModal: closeModal
+ };
+};
+
+export default useViewLogs;
diff --git a/src/pages/benchmark/index.tsx b/src/pages/benchmark/index.tsx
index e3316e84..715187ad 100644
--- a/src/pages/benchmark/index.tsx
+++ b/src/pages/benchmark/index.tsx
@@ -21,11 +21,13 @@ import {
import AddBenchmarkModal from './components/add-benchmark-modal';
import LeftActions from './components/left-actions';
import RightActions from './components/right-actions';
+import ViewLogsModal from './components/view-logs-modal';
import { FormData, BenchmarkListItem as ListItem } from './config/types';
import useBenchmarkColumns from './hooks/use-benchmark-columns';
import useColumnSettings from './hooks/use-column-settings';
import useCreateBenchmark from './hooks/use-create-benchmark';
import useExportData from './hooks/use-export-data';
+import useViewLogs from './hooks/use-view-logs';
import useQueryDataset from './services/use-query-dataset';
const Benchmark: React.FC = () => {
@@ -55,6 +57,8 @@ const Benchmark: React.FC = () => {
const { dataList: modelList, fetchData: fetchModelList } = useQueryModelList({
getValue: (item: any) => item.name
});
+ const { openViewLogsModal, closeViewLogsModal, openViewLogsModalStatus } =
+ useViewLogs();
const { SettingsButton, selectedColumns } = useColumnSettings();
const { datasetList, fetchDatasetData } = useQueryDataset();
@@ -104,6 +108,8 @@ const Benchmark: React.FC = () => {
handleEditUser(row);
} else if (val === 'delete') {
handleDelete({ ...row, name: row.name });
+ } else if (val === 'viewlog') {
+ openViewLogsModal(row);
}
});
@@ -218,6 +224,12 @@ const Benchmark: React.FC = () => {
onCancel={handleModalCancel}
onOk={handleModalOk}
>
+
>
);