chore: playground hotkey

This commit is contained in:
jialin
2024-06-25 17:09:47 +08:00
parent a7ea167298
commit ad9775db69
13 changed files with 122 additions and 37 deletions
+1
View File
@@ -27,6 +27,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"query-string": "^9.0.0", "query-string": "^9.0.0",
"react-hotkeys-hook": "^4.5.0",
"umi-presets-pro": "^2.0.3" "umi-presets-pro": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
+13
View File
@@ -53,6 +53,9 @@ dependencies:
query-string: query-string:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0 version: 9.0.0
react-hotkeys-hook:
specifier: ^4.5.0
version: 4.5.0(react-dom@18.3.1)(react@18.3.1)
umi-presets-pro: umi-presets-pro:
specifier: ^2.0.3 specifier: ^2.0.3
version: 2.0.3(@babel/core@7.24.5)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.17.0)(dva@2.5.0-beta.2)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)(umi@4.2.1) version: 2.0.3(@babel/core@7.24.5)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.17.0)(dva@2.5.0-beta.2)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)(umi@4.2.1)
@@ -14203,6 +14206,16 @@ packages:
shallowequal: 1.1.0 shallowequal: 1.1.0
dev: false dev: false
/react-hotkeys-hook@4.5.0(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==, tarball: https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.0.tgz}
peerDependencies:
react: '>=16.8.1'
react-dom: '>=16.8.1'
dependencies:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
dev: false
/react-intl@3.12.1(react@18.3.1): /react-intl@3.12.1(react@18.3.1):
resolution: {integrity: sha512-cgumW29mwROIqyp8NXStYsoIm27+8FqnxykiLSawWjOxGIBeLuN/+p2srei5SRIumcJefOkOIHP+NDck05RgHg==} resolution: {integrity: sha512-cgumW29mwROIqyp8NXStYsoIm27+8FqnxykiLSawWjOxGIBeLuN/+p2srei5SRIumcJefOkOIHP+NDck05RgHg==}
peerDependencies: peerDependencies:
+8
View File
@@ -27,3 +27,11 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
.opct-7 {
opacity: 0.7;
}
.opct-8 {
opacity: 0.8;
}
+12 -11
View File
@@ -1,13 +1,14 @@
export default { export default {
Create: ['ctrl+n', 'cmd+n'], CREATE: ['ctrl+n', 'meta+n'],
Save: ['ctrl+s', 'cmd+s'], SAVE: ['ctrl+s', 'meta+s'],
SaveAs: ['ctrl+shift+s', 'cmd+shift+s'], SUBMIT: ['ctrl+enter', 'meta+enter'],
Open: ['ctrl+o', 'cmd+o'], SAVEAS: ['ctrl+shift+s', 'meta+shift+s'],
Cancel: ['ctrl+w', 'cmd+w'], OPEN: ['ctrl+o', 'meta+o'],
Delete: ['delete'], CANCEL: ['ctrl+w', 'meta+w'],
Copy: ['ctrl+c', 'cmd+c'], DELETE: ['delete'],
Refresh: ['ctrl+r', 'cmd+r'], COPY: ['ctrl+c', 'meta+c'],
Edit: ['ctrl+e', 'cmd+e'], REFRESH: ['ctrl+r', 'meta+r'],
Search: ['ctrl+f', 'cmd+f'], EDIT: ['ctrl+e', 'meta+e'],
Reset: ['ctrl+shift+r', 'cmd+shift+r'] SEARCH: ['ctrl+f', 'meta+f'],
RESET: ['ctrl+shift+r', 'meta+shift+r']
}; };
+2 -2
View File
@@ -26,8 +26,8 @@ export interface ModelInstanceListItem {
huggingface_repo_id: string; huggingface_repo_id: string;
huggingface_filename: string; huggingface_filename: string;
s3_address: string; s3_address: string;
node_id: number; worker_id: number;
node_ip: string; worker_ip: string;
pid: number; pid: number;
port: number; port: number;
state: string; state: string;
+1 -1
View File
@@ -338,7 +338,7 @@ const Models: React.FC = () => {
<RowChildren> <RowChildren>
<Row style={{ width: '100%' }} align="middle"> <Row style={{ width: '100%' }} align="middle">
<Col span={4}> <Col span={4}>
{item.node_ip}:{item.port} {item.worker_ip}:{item.port}
</Col> </Col>
<Col span={5}> <Col span={5}>
<span>{item.huggingface_filename}</span> <span>{item.huggingface_filename}</span>
@@ -1,11 +1,14 @@
import HotKeys from '@/config/hotkeys';
import { import {
CodeOutlined, CodeOutlined,
DeleteOutlined, DeleteOutlined,
PlusOutlined, EnterOutlined,
SaveOutlined MacCommandOutlined,
PlusOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Col, Row, Space } from 'antd'; import { Button, Col, Row, Space } from 'antd';
import { useHotkeys } from 'react-hotkeys-hook';
import '../style/chat-footer.less'; import '../style/chat-footer.less';
interface ChatFooterProps { interface ChatFooterProps {
@@ -20,6 +23,14 @@ interface ChatFooterProps {
const ChatFooter: React.FC<ChatFooterProps> = (props) => { const ChatFooter: React.FC<ChatFooterProps> = (props) => {
const intl = useIntl(); const intl = useIntl();
const { onSubmit, onClear, onNewMessage, onView, feedback, disabled } = props; const { onSubmit, onClear, onNewMessage, onView, feedback, disabled } = props;
useHotkeys(
HotKeys.SUBMIT.join(','),
() => {
onSubmit();
},
{ enabled: !disabled }
);
return ( return (
<div className="chat-footer"> <div className="chat-footer">
<Row style={{ width: '100%' }}> <Row style={{ width: '100%' }}>
@@ -52,13 +63,11 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
> >
{intl.formatMessage({ id: 'playground.viewcode' })} {intl.formatMessage({ id: 'playground.viewcode' })}
</Button> </Button>
<Button <Button disabled={disabled} type="primary" onClick={onSubmit}>
disabled={disabled}
type="primary"
icon={<SaveOutlined></SaveOutlined>}
onClick={onSubmit}
>
{intl.formatMessage({ id: 'common.button.submit' })} {intl.formatMessage({ id: 'common.button.submit' })}
<span className="m-l-5 opct-7">
<MacCommandOutlined /> + <EnterOutlined />
</span>
</Button> </Button>
</Space> </Space>
</Col> </Col>
@@ -1,10 +1,12 @@
import TransitionWrapper from '@/components/transition'; import TransitionWrapper from '@/components/transition';
import HotKeys from '@/config/hotkeys';
import { EyeInvisibleOutlined } from '@ant-design/icons'; import { EyeInvisibleOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Input, Spin } from 'antd'; import { Button, Input, Spin } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { fetchChatStream, receiveChatStream } from '../apis'; import { fetchChatStream, receiveChatStream } from '../apis';
import { Roles } from '../config'; import { Roles } from '../config';
import '../style/ground-left.less'; import '../style/ground-left.less';
@@ -13,6 +15,7 @@ import ChatFooter from './chat-footer';
import MessageItem from './message-item'; import MessageItem from './message-item';
import ReferenceParams from './reference-params'; import ReferenceParams from './reference-params';
import ViewCodeModal from './view-code-modal'; import ViewCodeModal from './view-code-modal';
interface MessageProps { interface MessageProps {
parameters: any; parameters: any;
} }
@@ -40,6 +43,7 @@ const MessageList: React.FC<MessageProps> = (props) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1); const [activeIndex, setActiveIndex] = useState(-1);
const [tokenResult, setTokenResult] = useState<any>(null); const [tokenResult, setTokenResult] = useState<any>(null);
const [currentIsFocus, setCurrentIsFocus] = useState(false);
const systemRef = useRef<any>(null); const systemRef = useRef<any>(null);
const contentRef = useRef<any>(''); const contentRef = useRef<any>('');
@@ -153,6 +157,27 @@ const MessageList: React.FC<MessageProps> = (props) => {
</div> </div>
); );
}; };
const handleFocus = () => {
setCurrentIsFocus(true);
};
const handleBlur = () => {
setCurrentIsFocus(false);
};
useHotkeys(
HotKeys.SUBMIT,
() => {
handleSubmit();
},
{
enabled: currentIsFocus && !loading,
enableOnFormTags: currentIsFocus && !loading,
preventDefault: true
}
);
return ( return (
<div className="ground-left"> <div className="ground-left">
<PageContainer title={false} className="message-list-wrap"> <PageContainer title={false} className="message-list-wrap">
@@ -166,6 +191,8 @@ const MessageList: React.FC<MessageProps> = (props) => {
value={systemMessage} value={systemMessage}
variant="filled" variant="filled"
autoSize={true} autoSize={true}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={intl.formatMessage({ id: 'playground.system.tips' })} placeholder={intl.formatMessage({ id: 'playground.system.tips' })}
onChange={handleSystemMessageChange} onChange={handleSystemMessageChange}
></Input.TextArea> ></Input.TextArea>
@@ -184,6 +211,7 @@ const MessageList: React.FC<MessageProps> = (props) => {
updateMessage={(message: MessageItemProps) => updateMessage={(message: MessageItemProps) =>
handleUpdateMessage(index, message) handleUpdateMessage(index, message)
} }
onSubmit={handleSubmit}
message={item} message={item}
/> />
); );
@@ -1,7 +1,9 @@
import HotKeys from '@/config/hotkeys';
import { MinusCircleOutlined } from '@ant-design/icons'; import { MinusCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Input } from 'antd'; import { Button, Input } from 'antd';
import { memo, useEffect, useRef, useState } from 'react'; import { memo, useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { Roles } from '../config'; import { Roles } from '../config';
import '../style/message-item.less'; import '../style/message-item.less';
interface MessageItemProps { interface MessageItemProps {
@@ -14,18 +16,32 @@ const MessageItem: React.FC<{
message: MessageItemProps; message: MessageItemProps;
loading?: boolean; loading?: boolean;
islast?: boolean; islast?: boolean;
onSubmit: () => void;
updateMessage: (message: MessageItemProps) => void; updateMessage: (message: MessageItemProps) => void;
isFocus: boolean; isFocus: boolean;
onDelete: () => void; onDelete: () => void;
}> = ({ message, isFocus, onDelete, updateMessage }) => { }> = ({ message, isFocus, onDelete, updateMessage, onSubmit, loading }) => {
const intl = useIntl(); const intl = useIntl();
const [roleType, setRoleType] = useState(message.role); const [roleType, setRoleType] = useState(message.role);
const [isTyping, setIsTyping] = useState(false); const [isTyping, setIsTyping] = useState(false);
const [messageContent, setMessageContent] = useState(message.content); const [messageContent, setMessageContent] = useState(message.content);
const isInitialRender = useRef(true); const isInitialRender = useRef(true);
const [isAnimating, setIsAnimating] = useState(false); const [isAnimating, setIsAnimating] = useState(false);
const [currentIsFocus, setCurrentIsFocus] = useState(isFocus);
const inputRef = useRef<any>(null); const inputRef = useRef<any>(null);
useHotkeys(
HotKeys.SUBMIT,
() => {
onSubmit();
},
{
enabled: currentIsFocus && !loading,
enableOnFormTags: currentIsFocus && !loading,
preventDefault: true
}
);
useEffect(() => { useEffect(() => {
if (inputRef.current && isFocus) { if (inputRef.current && isFocus) {
inputRef.current.focus(); inputRef.current.focus();
@@ -75,6 +91,11 @@ const MessageItem: React.FC<{
const handleBlur = () => { const handleBlur = () => {
setIsTyping(true); setIsTyping(true);
setCurrentIsFocus(false);
};
const handleFocus = () => {
setCurrentIsFocus(true);
}; };
const handleRoleChange = () => { const handleRoleChange = () => {
@@ -103,6 +124,7 @@ const MessageItem: React.FC<{
autoSize={true} autoSize={true}
variant="filled" variant="filled"
onChange={handleMessageChange} onChange={handleMessageChange}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
></Input.TextArea> ></Input.TextArea>
</div> </div>
@@ -110,6 +132,7 @@ const MessageItem: React.FC<{
<Button <Button
type="text" type="text"
shape="circle" shape="circle"
size="small"
style={{ color: 'var(--ant-color-primary)' }} style={{ color: 'var(--ant-color-primary)' }}
onClick={handleDelete} onClick={handleDelete}
icon={<MinusCircleOutlined />} icon={<MinusCircleOutlined />}
+3 -3
View File
@@ -1,12 +1,12 @@
import { request } from '@umijs/max'; import { request } from '@umijs/max';
import { ListItem } from '../config/types'; import { ListItem } from '../config/types';
export const NODES_API = '/nodes'; export const WORKERS_API = '/workers';
export async function queryNodesList( export async function queryWorkersList(
params: Global.Pagination & { query?: string } params: Global.Pagination & { query?: string }
) { ) {
return request<Global.PageResponse<ListItem>>(`${NODES_API}`, { return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
methos: 'GET', methos: 'GET',
params params
}); });
+8 -7
View File
@@ -8,7 +8,7 @@ import { useIntl } from '@umijs/max';
import { Button, Input, Space, Table } from 'antd'; import { Button, Input, Space, Table } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { queryNodesList } from '../apis'; import { queryWorkersList } from '../apis';
import { ListItem } from '../config/types'; import { ListItem } from '../config/types';
const { Column } = Table; const { Column } = Table;
@@ -33,8 +33,8 @@ const Models: React.FC = () => {
const params = { const params = {
..._.pickBy(queryParams, (val: any) => !!val) ..._.pickBy(queryParams, (val: any) => !!val)
}; };
const res = await queryNodesList(params); const res = await queryWorkersList(params);
console.log('res=======', res);
setDataSource(res.items); setDataSource(res.items);
setTotal(res.pagination.total); setTotal(res.pagination.total);
} catch (error) { } catch (error) {
@@ -131,15 +131,16 @@ const Models: React.FC = () => {
return ( return (
<StatusTag <StatusTag
statusValue={{ statusValue={{
status: status: ['Inactive', 'unknown'].includes(record.state)
record.status?.state === 'active' ? 'success' : 'error', ? 'inactive'
text: record.status?.state : 'success',
text: record.state
}} }}
></StatusTag> ></StatusTag>
); );
}} }}
/> />
<Column title="IP" dataIndex="address" key="address" /> <Column title="IP" dataIndex="ip" key="address" />
<Column <Column
title="CPU" title="CPU"
dataIndex="CPU" dataIndex="CPU"
+2 -1
View File
@@ -34,6 +34,8 @@ export interface ListItem {
hostname: string; hostname: string;
address: string; address: string;
labels: object; labels: object;
state: string;
ip: string;
status: { status: {
cpu: { cpu: {
total: number; total: number;
@@ -60,7 +62,6 @@ export interface ListItem {
uptime: number; uptime: number;
boot_time: string; boot_time: string;
}; };
state: string;
}; };
id: number; id: number;
created_at: string; created_at: string;
+3 -3
View File
@@ -8,8 +8,8 @@ import Nodes from './components/nodes';
const items: TabsProps['items'] = [ const items: TabsProps['items'] = [
{ {
key: 'nodes', key: 'workers',
label: 'Nodes', label: 'Workers',
children: <Nodes /> children: <Nodes />
}, },
{ {
@@ -38,7 +38,7 @@ const Resources = () => {
<div style={{ marginTop: 70 }}> <div style={{ marginTop: 70 }}>
<Tabs <Tabs
type="card" type="card"
defaultActiveKey="nodes" defaultActiveKey="workers"
items={items} items={items}
accessKey={activeKey} accessKey={activeKey}
onChange={handleChangeTab} onChange={handleChangeTab}