|
|
|
|
@ -1,22 +1,23 @@
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
|
|
import CompareContext from '../../config/compare-context';
|
|
|
|
|
import { ModelSelectionItem } from '../../config/types';
|
|
|
|
|
import '../../style/multiple-chat.less';
|
|
|
|
|
import MessageInput from '../message-input';
|
|
|
|
|
import ActiveModels from './active-models';
|
|
|
|
|
|
|
|
|
|
interface MultiCompareProps {
|
|
|
|
|
modelList: Global.BaseOption<string>[];
|
|
|
|
|
modelList: (Global.BaseOption<string> & { type?: string })[];
|
|
|
|
|
spans?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
|
|
|
|
const [loadingStatus, setLoadingStatus] = useState<Record<string, boolean>>(
|
|
|
|
|
const [loadingStatus, setLoadingStatus] = useState<Record<symbol, boolean>>(
|
|
|
|
|
{}
|
|
|
|
|
);
|
|
|
|
|
const [modelSelections, setModelSelections] = useState<
|
|
|
|
|
Global.BaseOption<string>[]
|
|
|
|
|
>([]);
|
|
|
|
|
const [modelSelections, setModelSelections] = useState<ModelSelectionItem[]>(
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
const [globalParams, setGlobalParams] = useState<Record<string, any>>({
|
|
|
|
|
seed: null,
|
|
|
|
|
stop: null,
|
|
|
|
|
@ -31,69 +32,89 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
|
|
|
|
span: 12,
|
|
|
|
|
count: 2
|
|
|
|
|
});
|
|
|
|
|
const cacheModelInstanceList = useRef<any[]>([]);
|
|
|
|
|
const modelsCounterMap = useRef<Record<string, number>>({});
|
|
|
|
|
const modelRefs = useRef<any>({});
|
|
|
|
|
const boxHeight = 'calc(100vh - 72px)';
|
|
|
|
|
|
|
|
|
|
const isLoading = useMemo(() => {
|
|
|
|
|
console.log('loadingStatus========2', loadingStatus);
|
|
|
|
|
return _.keys(loadingStatus).some(
|
|
|
|
|
(modelname: string) => loadingStatus[modelname]
|
|
|
|
|
);
|
|
|
|
|
const modelRefList = Object.getOwnPropertySymbols(loadingStatus);
|
|
|
|
|
return modelRefList.some((instanceId: symbol) => loadingStatus[instanceId]);
|
|
|
|
|
}, [loadingStatus]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const list = modelList.slice?.(0, spans.count);
|
|
|
|
|
setModelSelections(list);
|
|
|
|
|
}, [modelList, spans.count]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
modelRefs.current = {};
|
|
|
|
|
modelSelections.forEach((item) => {
|
|
|
|
|
modelRefs.current[item.value] = null;
|
|
|
|
|
const modelFullList = useMemo(() => {
|
|
|
|
|
return modelList.map((item) => {
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
disabled: modelSelections.some((model) => model.value === item.value)
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}, [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 modelRefList = _.keys(modelRefs.current);
|
|
|
|
|
modelRefList.forEach(async (modelname: any, index: number) => {
|
|
|
|
|
const ref = modelRefs.current[modelname];
|
|
|
|
|
const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
|
|
|
|
|
modelRefList.forEach((instanceId: symbol) => {
|
|
|
|
|
const ref = modelRefs.current[instanceId];
|
|
|
|
|
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 = () => {
|
|
|
|
|
_.keys(modelRefs.current).forEach((modelname: string) => {
|
|
|
|
|
const ref = modelRefs.current[modelname];
|
|
|
|
|
const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
|
|
|
|
|
modelRefList.forEach((instanceId: symbol) => {
|
|
|
|
|
const ref = modelRefs.current[instanceId];
|
|
|
|
|
ref?.abortFetch();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setModelRefs = useCallback(
|
|
|
|
|
(modelname: string, el: React.MutableRefObject<any>) => {
|
|
|
|
|
modelRefs.current[modelname] = el;
|
|
|
|
|
(instanceId: symbol, el: React.MutableRefObject<any>) => {
|
|
|
|
|
modelRefs.current[instanceId] = el;
|
|
|
|
|
},
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const handleSetLoadingStatus = (modeName: string, status: boolean) => {
|
|
|
|
|
const handleSetLoadingStatus = (instanceId: symbol, status: boolean) => {
|
|
|
|
|
setLoadingStatus((preStatus) => {
|
|
|
|
|
const newState = { ...preStatus };
|
|
|
|
|
newState[modeName] = status;
|
|
|
|
|
newState[instanceId] = status;
|
|
|
|
|
return newState;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClearAll = () => {
|
|
|
|
|
_.keys(modelRefs.current).forEach((modelname: string) => {
|
|
|
|
|
const ref = modelRefs.current[modelname];
|
|
|
|
|
const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
|
|
|
|
|
modelRefList.forEach((instanceId: symbol) => {
|
|
|
|
|
const ref = modelRefs.current[instanceId];
|
|
|
|
|
ref?.clear();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDeleteModel = (modelname: string) => {
|
|
|
|
|
const handleDeleteModel = (instanceId: symbol) => {
|
|
|
|
|
const newModelList = modelSelections.filter(
|
|
|
|
|
(model) => model.value !== modelname
|
|
|
|
|
(model) => model.instanceId !== instanceId
|
|
|
|
|
);
|
|
|
|
|
pruneInstanceSymbol(instanceId);
|
|
|
|
|
const span = Math.floor(24 / (24 / spans.span - 1));
|
|
|
|
|
setSpans({
|
|
|
|
|
span,
|
|
|
|
|
@ -102,27 +123,108 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
|
|
|
|
setModelSelections(newModelList);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleUpdateModelSelections = (list: Global.BaseOption<string>[]) => {
|
|
|
|
|
// set spans.span
|
|
|
|
|
const span = Math.floor(24 / list.length);
|
|
|
|
|
const handleUpdateModelSelections = (
|
|
|
|
|
list: (Global.BaseOption<string> & { instanceId: symbol })[]
|
|
|
|
|
) => {
|
|
|
|
|
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({
|
|
|
|
|
span: span < 8 ? 8 : span,
|
|
|
|
|
count: spans.count
|
|
|
|
|
});
|
|
|
|
|
setModelSelections(list);
|
|
|
|
|
setModelSelections(updateList);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePresetPrompt = (list: { role: string; content: string }[]) => {
|
|
|
|
|
const sysMsg = list.filter((item) => item.role === 'system');
|
|
|
|
|
const userMsg = list.filter((item) => item.role === 'user');
|
|
|
|
|
const modelRefList = _.keys(modelRefs.current);
|
|
|
|
|
modelRefList.forEach(async (modelname: any) => {
|
|
|
|
|
const ref = modelRefs.current[modelname];
|
|
|
|
|
|
|
|
|
|
const modelRefList = Object.getOwnPropertySymbols(modelRefs.current);
|
|
|
|
|
modelRefList.forEach(async (instanceId: symbol) => {
|
|
|
|
|
const ref = modelRefs.current[instanceId];
|
|
|
|
|
ref?.presetPrompt(userMsg);
|
|
|
|
|
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 (
|
|
|
|
|
<div className="multiple-chat" style={{ height: boxHeight }}>
|
|
|
|
|
<div className="chat-list">
|
|
|
|
|
@ -147,12 +249,13 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
|
|
|
|
<MessageInput
|
|
|
|
|
loading={isLoading}
|
|
|
|
|
handleSubmit={handleSubmit}
|
|
|
|
|
addMessage={handleAddMessage}
|
|
|
|
|
handleAbortFetch={handleAbortFetch}
|
|
|
|
|
clearAll={handleClearAll}
|
|
|
|
|
setSpans={setSpans}
|
|
|
|
|
updateLayout={updateLayout}
|
|
|
|
|
setModelSelections={handleUpdateModelSelections}
|
|
|
|
|
presetPrompt={handlePresetPrompt}
|
|
|
|
|
modelList={modelList}
|
|
|
|
|
modelList={modelFullList}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|