refactor: playground chat

This commit is contained in:
jialin
2024-09-22 14:55:23 +08:00
parent 4f4f00f0c8
commit 1abaa164f5
15 changed files with 406 additions and 173 deletions
@@ -19,13 +19,13 @@ import { CHAT_API } from '../apis';
import { Roles } from '../config'; import { Roles } from '../config';
import '../style/ground-left.less'; import '../style/ground-left.less';
import '../style/system-message-wrap.less'; import '../style/system-message-wrap.less';
import ChatFooter from './chat-footer'; import MessageInput from './message-input';
import MessageItem from './message-item'; import MessageItem from './message-item';
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;
modelList: Global.BaseOption<string>[];
ref?: any; ref?: any;
} }
@@ -36,7 +36,7 @@ interface MessageItemProps {
} }
const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => { const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
const { parameters } = props; const { parameters, modelList } = props;
const messageId = useRef<number>(0); const messageId = useRef<number>(0);
const [messageList, setMessageList] = useState<MessageItemProps[]>([ const [messageList, setMessageList] = useState<MessageItemProps[]>([
{ {
@@ -81,11 +81,14 @@ const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
const setMessageId = () => { const setMessageId = () => {
messageId.current = messageId.current + 1; messageId.current = messageId.current + 1;
}; };
const handleNewMessage = (role?: any) => { const handleNewMessage = (message?: { role: string; content: string }) => {
messageList.push({ const newMessage = message || {
role: role:
_.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User, _.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User,
content: '', content: ''
};
messageList.push({
...newMessage,
uid: messageId.current + 1 uid: messageId.current + 1
}); });
setMessageId(); setMessageId();
@@ -234,6 +237,10 @@ const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
setCurrentIsFocus(false); setCurrentIsFocus(false);
}; };
const handleSelectModel = () => {};
const handlePresetPrompt = () => {};
useHotkeys( useHotkeys(
HotKeys.SUBMIT, HotKeys.SUBMIT,
() => { () => {
@@ -324,7 +331,17 @@ const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
</div> </div>
</div> </div>
<div className="ground-left-footer"> <div className="ground-left-footer">
<ChatFooter <MessageInput
loading={loading}
handleSubmit={handleSubmit}
addMessage={handleNewMessage}
handleAbortFetch={handleStopConversation}
clearAll={handleClear}
setModelSelections={handleSelectModel}
presetPrompt={handlePresetPrompt}
modelList={modelList}
/>
{/* <ChatFooter
onClear={handleClear} onClear={handleClear}
onNewMessage={handleNewMessage} onNewMessage={handleNewMessage}
onSubmit={handleSubmit} onSubmit={handleSubmit}
@@ -334,7 +351,7 @@ const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
selectedModel={parameters.model} selectedModel={parameters.model}
hasTokenResult={!!tokenResult} hasTokenResult={!!tokenResult}
feedback={<ReferenceParams usage={tokenResult}></ReferenceParams>} feedback={<ReferenceParams usage={tokenResult}></ReferenceParams>}
></ChatFooter> ></ChatFooter> */}
</div> </div>
<ViewCodeModal <ViewCodeModal
open={show} open={show}
@@ -5,7 +5,6 @@ import {
ClearOutlined, ClearOutlined,
ControlOutlined, ControlOutlined,
PictureOutlined, PictureOutlined,
SendOutlined,
SwapOutlined SwapOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
@@ -59,10 +58,15 @@ interface MessageInputProps {
modelList: Global.BaseOption<string>[]; modelList: Global.BaseOption<string>[];
handleSubmit: (params: { role: string; content: string }) => void; handleSubmit: (params: { role: string; content: string }) => void;
handleAbortFetch: () => void; handleAbortFetch: () => void;
setSpans: (value: { span: number; count: number }) => void; updateLayout?: (value: { span: number; count: number }) => void;
clearAll: () => void; clearAll: () => void;
setModelSelections: (modelList: Global.BaseOption<string>[]) => void; setModelSelections: (
modelList: (Global.BaseOption<string> & {
instanceId: symbol;
})[]
) => void;
presetPrompt: (list: { role: string; content: string }[]) => void; presetPrompt: (list: { role: string; content: string }[]) => void;
addMessage: (message: { role: string; content: string }) => void;
loading: boolean; loading: boolean;
} }
@@ -74,7 +78,8 @@ const MessageInput: React.FC<MessageInputProps> = ({
loading, loading,
modelList, modelList,
clearAll, clearAll,
setSpans updateLayout,
addMessage
}) => { }) => {
const { TextArea } = Input; const { TextArea } = Input;
const intl = useIntl(); const intl = useIntl();
@@ -105,7 +110,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
}; };
const handleLayoutChange = (value: { span: number; count: number }) => { const handleLayoutChange = (value: { span: number; count: number }) => {
console.log('layout change:', value); console.log('layout change:', value);
setSpans(value); updateLayout?.(value);
}; };
const handleToggleRole = () => { const handleToggleRole = () => {
@@ -125,7 +130,8 @@ const MessageInput: React.FC<MessageInputProps> = ({
const list = value?.map?.((val) => { const list = value?.map?.((val) => {
return { return {
value: val, value: val,
label: val label: val,
instanceId: Symbol(val)
}; };
}); });
setModelSelections(list); setModelSelections(list);
@@ -134,6 +140,16 @@ const MessageInput: React.FC<MessageInputProps> = ({
const handleOpenPrompt = () => { const handleOpenPrompt = () => {
setOpen(true); setOpen(true);
}; };
const handleAddMessage = () => {
console.log('add message');
addMessage({ ...message });
setMessage({
...message,
content: ''
});
};
useHotkeys( useHotkeys(
HotKeys.SUBMIT.join(','), HotKeys.SUBMIT.join(','),
() => { () => {
@@ -167,21 +183,25 @@ const MessageInput: React.FC<MessageInputProps> = ({
size="middle" size="middle"
onClick={handleOpenPrompt} onClick={handleOpenPrompt}
></Button> ></Button>
<Divider type="vertical" style={{ margin: 0 }} /> {updateLayout && (
{layoutOptions.map((option) => ( <>
<Button <Divider type="vertical" style={{ margin: 0 }} />
key={option.icon} {layoutOptions.map((option) => (
type="text" <Button
icon={<IconFont type={option.icon}></IconFont>} key={option.icon}
size="middle" type="text"
onClick={() => handleLayoutChange(option.value)} icon={<IconFont type={option.icon}></IconFont>}
></Button> size="middle"
))} onClick={() => handleLayoutChange(option.value)}
></Button>
))}
</>
)}
</div> </div>
<div className="actions"> <div className="actions">
<Select <Select
variant="borderless" variant="borderless"
style={{ width: 200 }} style={{ width: 180 }}
placeholder="select models" placeholder="select models"
options={modelList} options={modelList}
mode="multiple" mode="multiple"
@@ -190,14 +210,12 @@ const MessageInput: React.FC<MessageInputProps> = ({
maxTagTextLength={15} maxTagTextLength={15}
onChange={handleUpdateModelSelections} onChange={handleUpdateModelSelections}
></Select> ></Select>
<Button type="default" size="middle" onClick={handleAddMessage}>
{intl.formatMessage({ id: 'common.button.add' })}
</Button>
{!loading ? ( {!loading ? (
<Button <Button type="primary" onClick={handleSendMessage} size="middle">
style={{ width: 44 }} {intl.formatMessage({ id: 'common.button.submit' })}
type="primary"
onClick={handleSendMessage}
size="middle"
>
<SendOutlined />
</Button> </Button>
) : ( ) : (
<Button <Button
@@ -1,5 +1,6 @@
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import React from 'react'; import React from 'react';
import { ModelSelectionItem } from '../../config/types';
import ModelItem from './model-item'; import ModelItem from './model-item';
interface ActiveModelsProps { interface ActiveModelsProps {
@@ -7,8 +8,8 @@ interface ActiveModelsProps {
span: number; span: number;
count: number; count: number;
}; };
modelSelections: Global.BaseOption<string>[]; modelSelections: ModelSelectionItem[];
setModelRefs: (modelname: string, value: React.MutableRefObject<any>) => void; setModelRefs: (modelname: symbol, value: React.MutableRefObject<any>) => void;
} }
const ActiveModels: React.FC<ActiveModelsProps> = (props) => { const ActiveModels: React.FC<ActiveModelsProps> = (props) => {
@@ -16,12 +17,13 @@ const ActiveModels: React.FC<ActiveModelsProps> = (props) => {
return ( return (
<Row gutter={[16, 16]} style={{ height: '100%' }}> <Row gutter={[16, 16]} style={{ height: '100%' }}>
{modelSelections.map((model, index) => ( {modelSelections.map((model, index) => (
<Col span={spans.span} key={model.value}> <Col span={spans.span} key={`${model.value || 'empty'}-${model.uid}`}>
<ModelItem <ModelItem
key={model.value} key={`${model.value || 'empty'}-${model.uid}`}
ref={(el: React.MutableRefObject<any>) => ref={(el: React.MutableRefObject<any>) =>
setModelRefs(model.value, el) setModelRefs(model.instanceId, el)
} }
instanceId={model.instanceId}
modelList={modelSelections} modelList={modelSelections}
model={model.value} model={model.value}
/> />
@@ -1,22 +1,23 @@
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import CompareContext from '../../config/compare-context'; import CompareContext from '../../config/compare-context';
import { ModelSelectionItem } from '../../config/types';
import '../../style/multiple-chat.less'; import '../../style/multiple-chat.less';
import MessageInput from '../message-input'; import MessageInput from '../message-input';
import ActiveModels from './active-models'; import ActiveModels from './active-models';
interface MultiCompareProps { interface MultiCompareProps {
modelList: Global.BaseOption<string>[]; modelList: (Global.BaseOption<string> & { type?: string })[];
spans?: number; spans?: number;
} }
const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => { const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
const [loadingStatus, setLoadingStatus] = useState<Record<string, boolean>>( const [loadingStatus, setLoadingStatus] = useState<Record<symbol, boolean>>(
{} {}
); );
const [modelSelections, setModelSelections] = useState< const [modelSelections, setModelSelections] = useState<ModelSelectionItem[]>(
Global.BaseOption<string>[] []
>([]); );
const [globalParams, setGlobalParams] = useState<Record<string, any>>({ const [globalParams, setGlobalParams] = useState<Record<string, any>>({
seed: null, seed: null,
stop: null, stop: null,
@@ -31,69 +32,89 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
span: 12, span: 12,
count: 2 count: 2
}); });
const cacheModelInstanceList = useRef<any[]>([]);
const modelsCounterMap = useRef<Record<string, number>>({});
const modelRefs = useRef<any>({}); const modelRefs = useRef<any>({});
const boxHeight = 'calc(100vh - 72px)'; const boxHeight = 'calc(100vh - 72px)';
const isLoading = useMemo(() => { const isLoading = useMemo(() => {
console.log('loadingStatus========2', loadingStatus); const modelRefList = Object.getOwnPropertySymbols(loadingStatus);
return _.keys(loadingStatus).some( return modelRefList.some((instanceId: symbol) => loadingStatus[instanceId]);
(modelname: string) => loadingStatus[modelname]
);
}, [loadingStatus]); }, [loadingStatus]);
useEffect(() => { const modelFullList = useMemo(() => {
const list = modelList.slice?.(0, spans.count); return modelList.map((item) => {
setModelSelections(list); return {
}, [modelList, spans.count]); ...item,
disabled: modelSelections.some((model) => model.value === item.value)
useEffect(() => { };
modelRefs.current = {};
modelSelections.forEach((item) => {
modelRefs.current[item.value] = null;
}); });
}, [modelSelections]); }, [modelList, modelSelections]);
const setModelCounter = (model: string) => {
modelsCounterMap.current[model] = _.add(modelsCounterMap.current[model], 1);
return modelsCounterMap.current[model];
};
const pruneInstanceSymbol = (instanceId: symbol) => {
modelRefs.current[instanceId] = null;
loadingStatus[instanceId] = false;
};
const handleSubmit = (currentMessage: { role: string; content: string }) => { const handleSubmit = (currentMessage: { role: string; content: string }) => {
const modelRefList = _.keys(modelRefs.current); const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
modelRefList.forEach(async (modelname: any, index: number) => { modelRefList.forEach((instanceId: symbol) => {
const ref = modelRefs.current[modelname]; const ref = modelRefs.current[instanceId];
ref?.submit(currentMessage); ref?.submit(currentMessage);
}); });
}; };
const handleAddMessage = (message: { role: string; content: string }) => {
const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
modelRefList.forEach((instanceId: symbol) => {
const ref = modelRefs.current[instanceId];
ref?.setMessageList((preList: any) => {
return [...preList, { ...message }];
});
});
};
const handleAbortFetch = () => { const handleAbortFetch = () => {
_.keys(modelRefs.current).forEach((modelname: string) => { const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
const ref = modelRefs.current[modelname]; modelRefList.forEach((instanceId: symbol) => {
const ref = modelRefs.current[instanceId];
ref?.abortFetch(); ref?.abortFetch();
}); });
}; };
const setModelRefs = useCallback( const setModelRefs = useCallback(
(modelname: string, el: React.MutableRefObject<any>) => { (instanceId: symbol, el: React.MutableRefObject<any>) => {
modelRefs.current[modelname] = el; modelRefs.current[instanceId] = el;
}, },
[] []
); );
const handleSetLoadingStatus = (modeName: string, status: boolean) => { const handleSetLoadingStatus = (instanceId: symbol, status: boolean) => {
setLoadingStatus((preStatus) => { setLoadingStatus((preStatus) => {
const newState = { ...preStatus }; const newState = { ...preStatus };
newState[modeName] = status; newState[instanceId] = status;
return newState; return newState;
}); });
}; };
const handleClearAll = () => { const handleClearAll = () => {
_.keys(modelRefs.current).forEach((modelname: string) => { const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
const ref = modelRefs.current[modelname]; modelRefList.forEach((instanceId: symbol) => {
const ref = modelRefs.current[instanceId];
ref?.clear(); ref?.clear();
}); });
}; };
const handleDeleteModel = (modelname: string) => { const handleDeleteModel = (instanceId: symbol) => {
const newModelList = modelSelections.filter( const newModelList = modelSelections.filter(
(model) => model.value !== modelname (model) => model.instanceId !== instanceId
); );
pruneInstanceSymbol(instanceId);
const span = Math.floor(24 / (24 / spans.span - 1)); const span = Math.floor(24 / (24 / spans.span - 1));
setSpans({ setSpans({
span, span,
@@ -102,27 +123,108 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
setModelSelections(newModelList); setModelSelections(newModelList);
}; };
const handleUpdateModelSelections = (list: Global.BaseOption<string>[]) => { const handleUpdateModelSelections = (
// set spans.span list: (Global.BaseOption<string> & { instanceId: symbol })[]
const span = Math.floor(24 / list.length); ) => {
const newList = list.map((item) => {
return {
...item,
uid: setModelCounter(item.value),
instanceId: Symbol(item.value)
};
});
const updateList = _.concat(modelSelections, newList);
const span = Math.floor(24 / updateList.length);
setSpans({ setSpans({
span: span < 8 ? 8 : span, span: span < 8 ? 8 : span,
count: spans.count count: spans.count
}); });
setModelSelections(list); setModelSelections(updateList);
}; };
const handlePresetPrompt = (list: { role: string; content: string }[]) => { const handlePresetPrompt = (list: { role: string; content: string }[]) => {
const sysMsg = list.filter((item) => item.role === 'system'); const sysMsg = list.filter((item) => item.role === 'system');
const userMsg = list.filter((item) => item.role === 'user'); const userMsg = list.filter((item) => item.role === 'user');
const modelRefList = _.keys(modelRefs.current);
modelRefList.forEach(async (modelname: any) => { const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
const ref = modelRefs.current[modelname]; modelRefList.forEach(async (instanceId: symbol) => {
const ref = modelRefs.current[instanceId];
ref?.presetPrompt(userMsg); ref?.presetPrompt(userMsg);
ref?.setSystemMessage(_.get(sysMsg, '0.content', '')); ref?.setSystemMessage(_.get(sysMsg, '0.content', ''));
}); });
}; };
const handleUpdateModelList = (spans: { span: number; count: number }) => {
const list = modelSelections;
// less than count
if (list.length < spans.count) {
const restCount = spans.count - list.length;
const restList = _.slice(modelList, list.length, list.length + restCount);
const resultList = Array.from(
{ length: restCount - restList.length },
(_, index) => {
return {
label: '',
value: '',
type: 'empty'
};
}
);
const newResultList = _.concat(restList, resultList).map((item: any) => {
return {
...item,
uid: setModelCounter(item.value || 'empty'),
instanceId:
item.type === 'empty' ? Symbol('empty') : Symbol(item.value)
};
});
setModelSelections(_.concat(list, newResultList));
return;
}
// more than count
if (list.length > spans.count) {
const newList = list.slice(0, spans.count);
setModelSelections(newList);
return;
}
};
const updateLayout = (value: { span: number; count: number }) => {
setSpans(value);
handleUpdateModelList(value);
};
useEffect(() => {
modelRefs.current = {};
let list = _.take(modelList, spans.count);
if (list.length < spans.count && list.length > 0) {
const restCount = spans.count - list.length;
const restList = Array.from({ length: restCount }, (_, index) => {
return {
label: '',
value: '',
type: 'empty'
};
});
list = _.concat(list, restList);
}
const resultList = list.map((item: any) => {
return {
...item,
uid: setModelCounter(item.value || 'empty'),
instanceId: item.type === 'empty' ? Symbol('empty') : Symbol(item.value)
};
});
setModelSelections(resultList);
}, [modelList]);
// useEffect(() => {
// modelRefs.current = {};
// modelSelections.forEach((item) => {
// modelRefs.current[item.instanceId] = null;
// });
// }, [modelSelections]);
return ( return (
<div className="multiple-chat" style={{ height: boxHeight }}> <div className="multiple-chat" style={{ height: boxHeight }}>
<div className="chat-list"> <div className="chat-list">
@@ -147,12 +249,13 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
<MessageInput <MessageInput
loading={isLoading} loading={isLoading}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
addMessage={handleAddMessage}
handleAbortFetch={handleAbortFetch} handleAbortFetch={handleAbortFetch}
clearAll={handleClearAll} clearAll={handleClearAll}
setSpans={setSpans} updateLayout={updateLayout}
setModelSelections={handleUpdateModelSelections} setModelSelections={handleUpdateModelSelections}
presetPrompt={handlePresetPrompt} presetPrompt={handlePresetPrompt}
modelList={modelList} modelList={modelFullList}
/> />
</div> </div>
</div> </div>
@@ -23,6 +23,7 @@ import React, {
useContext, useContext,
useEffect, useEffect,
useImperativeHandle, useImperativeHandle,
useMemo,
useRef, useRef,
useState useState
} from 'react'; } from 'react';
@@ -30,6 +31,7 @@ import 'simplebar-react/dist/simplebar.min.css';
import { CHAT_API } from '../../apis'; import { CHAT_API } from '../../apis';
import { Roles } from '../../config'; import { Roles } from '../../config';
import CompareContext from '../../config/compare-context'; import CompareContext from '../../config/compare-context';
import { ModelSelectionItem } from '../../config/types';
import '../../style/model-item.less'; import '../../style/model-item.less';
import ParamsSettings from '../params-settings'; import ParamsSettings from '../params-settings';
import ReferenceParams from '../reference-params'; import ReferenceParams from '../reference-params';
@@ -38,7 +40,8 @@ import MessageContent from './message-content';
interface ModelItemProps { interface ModelItemProps {
model: string; model: string;
modelList: Global.BaseOption<string>[]; modelList: ModelSelectionItem[];
instanceId: symbol;
ref: any; ref: any;
} }
@@ -49,7 +52,7 @@ interface MessageItemProps {
} }
const ModelItem: React.FC<ModelItemProps> = forwardRef( const ModelItem: React.FC<ModelItemProps> = forwardRef(
({ model, modelList }, ref) => { ({ model, modelList, instanceId }, ref) => {
const { const {
spans, spans,
globalParams, globalParams,
@@ -63,7 +66,8 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
const [autoSize, setAutoSize] = useState<{ const [autoSize, setAutoSize] = useState<{
minRows: number; minRows: number;
maxRows: number; maxRows: number;
}>({ minRows: 1, maxRows: 1 }); focus: boolean;
}>({ minRows: 1, maxRows: 1, focus: false });
const [systemMessage, setSystemMessage] = useState<string>(''); const [systemMessage, setSystemMessage] = useState<string>('');
const [params, setParams] = useState<Record<string, any>>({}); const [params, setParams] = useState<Record<string, any>>({});
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -74,6 +78,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
const contentRef = useRef<any>(''); const contentRef = useRef<any>('');
const controllerRef = useRef<any>(null); const controllerRef = useRef<any>(null);
const currentMessageRef = useRef<MessageItemProps>({} as MessageItemProps); const currentMessageRef = useRef<MessageItemProps>({} as MessageItemProps);
const systemMessageRef = useRef<any>(null);
const setMessageId = () => { const setMessageId = () => {
messageId.current = messageId.current + 1; messageId.current = messageId.current + 1;
@@ -81,7 +86,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
const abortFetch = () => { const abortFetch = () => {
controllerRef.current?.abort?.(); controllerRef.current?.abort?.();
setLoadingStatus(params.model, false); setLoadingStatus(instanceId, false);
}; };
const joinMessage = (chunk: any) => { const joinMessage = (chunk: any) => {
@@ -118,7 +123,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
const { parameters, currentMessage } = currentParams; const { parameters, currentMessage } = currentParams;
if (!parameters.model) return; if (!parameters.model) return;
try { try {
setLoadingStatus(parameters.model, true); setLoadingStatus(instanceId, true);
setMessageId(); setMessageId();
controllerRef.current?.abort?.(); controllerRef.current?.abort?.();
@@ -179,16 +184,15 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
await readStreamData(reader, decoder, (chunk: any) => { await readStreamData(reader, decoder, (chunk: any) => {
joinMessage(chunk); joinMessage(chunk);
}); });
setLoadingStatus(params.model, false); setLoadingStatus(instanceId, false);
} catch (error) { } catch (error) {
console.log('error=====', error); setLoadingStatus(instanceId, false);
setLoadingStatus(params.model, false);
} }
}; };
const handleDropdownAction = useCallback(({ key }: { key: string }) => { const handleDropdownAction = useCallback(({ key }: { key: string }) => {
console.log('key:', key);
if (key === 'clear') { if (key === 'clear') {
setMessageList([]); setMessageList([]);
setSystemMessage('');
} }
if (key === 'viewCode') { if (key === 'viewCode') {
setShow(true); setShow(true);
@@ -239,7 +243,9 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
setTokenResult(null); setTokenResult(null);
setSystemMessage(''); setSystemMessage('');
currentMessageRef.current = {} as MessageItemProps; currentMessageRef.current = {} as MessageItemProps;
console.log('clear message', systemMessage);
}; };
const handleCloseViewCode = () => { const handleCloseViewCode = () => {
setShow(false); setShow(false);
}; };
@@ -270,23 +276,40 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
}; };
const handleDelete = () => { const handleDelete = () => {
handleDeleteModel(params.model); handleDeleteModel(instanceId);
}; };
const handleFocus = () => { const handleFocus = () => {
setAutoSize({ setAutoSize({
minRows: 4, minRows: 4,
maxRows: 4 maxRows: 4,
focus: true
}); });
setTimeout(() => {
systemMessageRef.current?.focus?.({
cursor: 'end'
});
}, 100);
}; };
const handleBlur = () => { const handleBlur = () => {
setAutoSize({ setAutoSize({
minRows: 1, minRows: 1,
maxRows: 1 maxRows: 1,
focus: false
}); });
}; };
const handleClearSystemMessage = () => {
setSystemMessage('');
};
const modelOptions = useMemo(() => {
return modelList.filter((item) => {
return item.type !== 'empty';
});
}, [modelList]);
useEffect(() => { useEffect(() => {
console.log('globalParams:', globalParams.model, globalParams); console.log('globalParams:', globalParams.model, globalParams);
setParams({ setParams({
@@ -319,8 +342,9 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
<div className="header"> <div className="header">
<span className="title"> <span className="title">
<Select <Select
style={{ minWidth: '100px' }}
variant="borderless" variant="borderless"
options={modelList} options={modelOptions}
onChange={handleModelChange} onChange={handleModelChange}
value={params.model} value={params.model}
></Select> ></Select>
@@ -390,19 +414,50 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
></Button> ></Button>
</span> </span>
</div> </div>
<div> <div className="sys-message">
<Input.TextArea {
variant="filled" <div style={{ display: autoSize.focus ? 'block' : 'none' }}>
placeholder="Type system message here" <Input.TextArea
style={{ borderRadius: '0', border: 'none' }} ref={systemMessageRef}
value={systemMessage} variant="filled"
autoSize={autoSize} placeholder="Type system message here"
onFocus={handleFocus} style={{
onBlur={handleBlur} borderRadius: '0',
allowClear={false} border: 'none'
onChange={(e) => setSystemMessage(e.target.value)} }}
></Input.TextArea> value={systemMessage}
<Divider style={{ margin: '0' }}></Divider> autoSize={{
minRows: autoSize.minRows,
maxRows: autoSize.maxRows
}}
onFocus={handleFocus}
onBlur={handleBlur}
allowClear={false}
onChange={(e) => setSystemMessage(e.target.value)}
></Input.TextArea>
<Divider style={{ margin: '0' }}></Divider>
</div>
}
{!autoSize.focus && (
<div className="sys-content-wrap" onClick={handleFocus}>
<div className="sys-content">
{systemMessage || (
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
Type system message here
</span>
)}
</div>
{systemMessage && (
<Button
className="clear-btn"
type="text"
icon={<CloseOutlined />}
size="small"
onClick={handleClearSystemMessage}
></Button>
)}
</div>
)}
</div> </div>
<div className="content"> <div className="content">
<MessageContent <MessageContent
@@ -1,6 +1,8 @@
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Modal, Typography } from 'antd'; import { Button, Modal, Typography } from 'antd';
import React from 'react'; import React from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import promptList from '../config/prompt'; import promptList from '../config/prompt';
import '../style/prompt-modal.less'; import '../style/prompt-modal.less';
@@ -33,46 +35,55 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
keyboard={false} keyboard={false}
width={660} width={660}
styles={{ styles={{
body: { content: {
maxHeight: '550px', padding: '0'
overflow: 'auto' },
header: {
padding: 'var(--ant-modal-content-padding)',
marginBottom: '0'
} }
}} }}
footer={null} footer={null}
> >
<div className="prompt-wrapper"> <SimpleBar style={{ maxHeight: '550px' }}>
{promptList.map((item, index) => { <div className="prompt-wrapper">
return ( {promptList.map((item, index) => {
<div key={index} className="prompt-item"> return (
<h3 className="title"> <div key={index} className="prompt-item">
<span className="text">{item.title}</span> <h3 className="title">
<Button <span className="text">{item.title}</span>
size="middle" <Button
type="default" size="middle"
onClick={() => handleSelect(item)} type="default"
> onClick={() => handleSelect(item)}
Use >
</Button> Use
</h3> </Button>
{item.data.map((data, i) => { </h3>
return ( {item.data.map((data, i) => {
<div key={i} className="data-item"> return (
<span className="role">{data.role}</span> <div key={i} className="data-item">
<span className="prompt"> <span className="role">{data.role}</span>
<Typography.Paragraph <span className="prompt">
style={{ margin: 0 }} <Typography.Paragraph
ellipsis={{ rows: 2, expandable: true, symbol: 'more' }} style={{ margin: 0 }}
> ellipsis={{
{data.content} rows: 2,
</Typography.Paragraph> expandable: true,
</span> symbol: 'more'
</div> }}
); >
})} {data.content}
</div> </Typography.Paragraph>
); </span>
})} </div>
</div> );
})}
</div>
);
})}
</div>
</SimpleBar>
</Modal> </Modal>
); );
}; };
@@ -8,10 +8,10 @@ interface CompareContextProps {
systemMessage?: string; systemMessage?: string;
globalParams: Record<string, any>; globalParams: Record<string, any>;
loadingStatus: Record<string, boolean>; loadingStatus: Record<string, boolean>;
handleDeleteModel: (modelname: string) => void; handleDeleteModel: (instanceId: symbol) => void;
setSystemMessage?: (message: string) => void; setSystemMessage?: (message: string) => void;
setGlobalParams: (value: Record<string, any>) => void; setGlobalParams: (value: Record<string, any>) => void;
setLoadingStatus: (modeName: string, status: boolean) => void; setLoadingStatus: (instanceId: symbol, status: boolean) => void;
} }
const CompareContext = React.createContext<CompareContextProps>( const CompareContext = React.createContext<CompareContextProps>(
{} as CompareContextProps {} as CompareContextProps
+5
View File
@@ -0,0 +1,5 @@
export interface ModelSelectionItem extends Global.BaseOption<string> {
uid: number;
instanceId: symbol;
type?: string;
}
+6 -9
View File
@@ -3,7 +3,7 @@ import HotKeys from '@/config/hotkeys';
import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons'; import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useSearchParams } from '@umijs/max'; import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Divider, Segmented, Space, Tabs, TabsProps } from 'antd'; import { Button, Segmented, Space, Tabs, TabsProps } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
@@ -45,7 +45,11 @@ const Playground: React.FC = () => {
key: 'chat', key: 'chat',
label: 'Chat', label: 'Chat',
children: ( children: (
<GroundLeft parameters={params} ref={groundLeftRef}></GroundLeft> <GroundLeft
parameters={params}
ref={groundLeftRef}
modelList={modelList}
></GroundLeft>
) )
}, },
{ {
@@ -146,13 +150,6 @@ const Playground: React.FC = () => {
collapse: collapse collapse: collapse
})} })}
> >
<div
className={classNames('divider-line', {
collapse: collapse
})}
>
<Divider type="vertical" />
</div>
<div className={classNames('params')}> <div className={classNames('params')}>
<div <div
className={classNames('params-box', { className={classNames('params-box', {
+2 -2
View File
@@ -1,6 +1,6 @@
.chat-footer { .chat-footer {
display: flex; // display: flex;
align-items: center; // align-items: center;
padding-block: 10px; padding-block: 10px;
padding-right: 32px; padding-right: 32px;
} }
+2 -2
View File
@@ -3,11 +3,11 @@
justify-content: space-between; justify-content: space-between;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
height: calc(100vh - 136px); height: calc(100vh - 72px);
.message-list-wrap { .message-list-wrap {
max-height: calc(100vh - 152px); max-height: calc(100vh - 152px);
overflow-y: auto; overflow-y: auto;
padding-right: 32px; padding-inline: var(--layout-content-inlinepadding);
} }
} }
+1 -16
View File
@@ -33,25 +33,10 @@
.actions { .actions {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 10px;
} }
} }
&:focus-within {
// border-color: var(--ant-input-active-border-color);
// box-shadow: var(--ant-input-active-shadow);
// background-color: var(--color-white-1);
// transition: all 0.2s ease;
// &:hover {
// background-color: var(--color-white-1);
// }
}
&:hover {
// background: var(--ant-color-fill-secondary);
// transition: background 0.2s ease;
}
textarea { textarea {
padding-inline: 6px 0; padding-inline: 6px 0;
padding-block: 0; padding-block: 0;
@@ -14,6 +14,42 @@
border-bottom: 1px solid var(--ant-color-border); border-bottom: 1px solid var(--ant-color-border);
} }
.sys-message {
position: relative;
}
.sys-content-wrap {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--ant-color-fill-tertiary);
padding-right: 20px;
cursor: pointer;
&:hover {
.clear-btn {
display: block;
}
}
}
.clear-btn {
display: none;
position: absolute;
right: 6px;
top: 6px;
}
.sys-content {
height: 36px;
line-height: 20px;
padding: 8px 14px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.content { .content {
flex: 1; flex: 1;
padding: 16px; padding: 16px;
+4 -2
View File
@@ -36,7 +36,7 @@
.params { .params {
overflow: hidden; overflow: hidden;
height: calc(100vh - 136px); height: calc(100vh - 72px);
.collapse { .collapse {
width: 0; width: 0;
@@ -50,6 +50,7 @@
width: 390px; width: 390px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
border-left: 1px solid var(--ant-color-split);
&.collapse { &.collapse {
padding-inline: 0; padding-inline: 0;
@@ -73,7 +74,8 @@
.playground-container { .playground-container {
.ant-pro-page-container-children-container { .ant-pro-page-container-children-container {
padding-right: 0; padding-inline: 0;
padding-block: 0;
} }
&.compare { &.compare {
@@ -2,6 +2,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
padding: var(--ant-modal-content-padding);
.title { .title {
display: flex; display: flex;
@@ -10,6 +11,7 @@
.text { .text {
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
font-size: var(--font-size-base);
} }
} }