feat: add models compare
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_xpmv3m9655d.js'
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_fcpq8y25444.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:local(.wrapper-box) {
|
||||
@borderRadius: 8px;
|
||||
@borderRadius: var(--border-radius-base);
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
@@ -5,13 +5,16 @@ import {
|
||||
ClearOutlined,
|
||||
ControlOutlined,
|
||||
PictureOutlined,
|
||||
SendOutlined
|
||||
SendOutlined,
|
||||
SwapOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, Select } from 'antd';
|
||||
import { Button, Divider, Input, Select } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { Roles } from '../config';
|
||||
import '../style/message-input.less';
|
||||
import PromptModal from './prompt-modal';
|
||||
|
||||
const layoutOptions = [
|
||||
{
|
||||
@@ -36,7 +39,7 @@ const layoutOptions = [
|
||||
label: '4 columns',
|
||||
icon: 'icon-cols_4',
|
||||
value: {
|
||||
span: 6,
|
||||
span: 12,
|
||||
count: 4
|
||||
},
|
||||
tips: 'four models compare'
|
||||
@@ -54,31 +57,47 @@ const layoutOptions = [
|
||||
|
||||
interface MessageInputProps {
|
||||
modelList: Global.BaseOption<string>[];
|
||||
handleSubmit: (value: string) => void;
|
||||
handleSubmit: (params: { role: string; content: string }) => void;
|
||||
handleAbortFetch: () => void;
|
||||
setParamsSettings: (value: Record<string, any>) => void;
|
||||
setSpans: (value: { span: number; count: number }) => void;
|
||||
clearAll: () => void;
|
||||
setModelSelections: (modelList: Global.BaseOption<string>[]) => void;
|
||||
presetPrompt: (list: { role: string; content: string }[]) => void;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const MessageInput: React.FC<MessageInputProps> = ({
|
||||
handleSubmit,
|
||||
handleAbortFetch,
|
||||
setParamsSettings,
|
||||
setModelSelections,
|
||||
presetPrompt,
|
||||
loading,
|
||||
modelList,
|
||||
clearAll,
|
||||
setSpans
|
||||
}) => {
|
||||
const { TextArea } = Input;
|
||||
const intl = useIntl();
|
||||
const platform = platformCall();
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [message, setMessage] = useState<{ role: string; content: string }>({
|
||||
role: Roles.User,
|
||||
content: ''
|
||||
});
|
||||
const handleInputChange = (value: string) => {
|
||||
setMessage(value);
|
||||
console.log('input change:', value);
|
||||
setMessage({
|
||||
...message,
|
||||
content: value
|
||||
});
|
||||
};
|
||||
const handleSendMessage = () => {
|
||||
handleSubmit(message);
|
||||
handleSubmit({ ...message });
|
||||
setMessage({
|
||||
...message,
|
||||
content: ''
|
||||
});
|
||||
};
|
||||
const onStop = () => {
|
||||
setDisabled(false);
|
||||
@@ -88,6 +107,33 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
console.log('layout change:', value);
|
||||
setSpans(value);
|
||||
};
|
||||
|
||||
const handleToggleRole = () => {
|
||||
setMessage({
|
||||
...message,
|
||||
role: message.role === Roles.User ? Roles.Assistant : Roles.User
|
||||
});
|
||||
};
|
||||
|
||||
const handleClearAll = (e: any) => {
|
||||
e.stopPropagation();
|
||||
clearAll();
|
||||
};
|
||||
|
||||
const handleUpdateModelSelections = (value: string[]) => {
|
||||
console.log('update model selections:', value);
|
||||
const list = value?.map?.((val) => {
|
||||
return {
|
||||
value: val,
|
||||
label: val
|
||||
};
|
||||
});
|
||||
setModelSelections(list);
|
||||
};
|
||||
|
||||
const handleOpenPrompt = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
useHotkeys(
|
||||
HotKeys.SUBMIT.join(','),
|
||||
() => {
|
||||
@@ -99,10 +145,29 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
<div className="messageInput">
|
||||
<div className="tool-bar">
|
||||
<div className="actions">
|
||||
<Button
|
||||
type="text"
|
||||
size="middle"
|
||||
onClick={handleToggleRole}
|
||||
icon={<SwapOutlined rotate={90} />}
|
||||
>
|
||||
{intl.formatMessage({ id: `playground.${message.role}` })}
|
||||
</Button>
|
||||
<Divider type="vertical" style={{ margin: 0 }} />
|
||||
<Button type="text" icon={<PictureOutlined />} size="middle"></Button>
|
||||
<Button type="text" icon={<ClearOutlined />} size="middle"></Button>
|
||||
<Button type="text" icon={<ControlOutlined />} size="middle"></Button>
|
||||
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClearOutlined />}
|
||||
size="middle"
|
||||
onClick={handleClearAll}
|
||||
></Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ControlOutlined />}
|
||||
size="middle"
|
||||
onClick={handleOpenPrompt}
|
||||
></Button>
|
||||
<Divider type="vertical" style={{ margin: 0 }} />
|
||||
{layoutOptions.map((option) => (
|
||||
<Button
|
||||
key={option.icon}
|
||||
@@ -123,13 +188,20 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
maxCount={6}
|
||||
maxTagCount={0}
|
||||
maxTagTextLength={15}
|
||||
onChange={handleUpdateModelSelections}
|
||||
></Select>
|
||||
{!loading ? (
|
||||
<Button type="primary" onClick={handleSendMessage} size="middle">
|
||||
<Button
|
||||
style={{ width: 44 }}
|
||||
type="primary"
|
||||
onClick={handleSendMessage}
|
||||
size="middle"
|
||||
>
|
||||
<SendOutlined />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
style={{ width: 44 }}
|
||||
type="primary"
|
||||
onClick={onStop}
|
||||
size="middle"
|
||||
@@ -139,13 +211,18 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<TextArea
|
||||
placeholder="Send your message"
|
||||
placeholder="Type your message here"
|
||||
autoSize={{ minRows: 3, maxRows: 3 }}
|
||||
onChange={(e) => handleInputChange(e.target.value)}
|
||||
value={message}
|
||||
value={message.content}
|
||||
size="large"
|
||||
variant="borderless"
|
||||
></TextArea>
|
||||
<PromptModal
|
||||
open={open}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSelect={presetPrompt}
|
||||
></PromptModal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Col, Row } from 'antd';
|
||||
import React from 'react';
|
||||
import ModelItem from './model-item';
|
||||
|
||||
interface ActiveModelsProps {
|
||||
spans: {
|
||||
span: number;
|
||||
count: number;
|
||||
};
|
||||
modelSelections: Global.BaseOption<string>[];
|
||||
setModelRefs: (modelname: string, value: React.MutableRefObject<any>) => void;
|
||||
}
|
||||
|
||||
const ActiveModels: React.FC<ActiveModelsProps> = (props) => {
|
||||
const { spans, modelSelections, setModelRefs } = props;
|
||||
return (
|
||||
<Row gutter={[16, 16]} style={{ height: '100%' }}>
|
||||
{modelSelections.map((model, index) => (
|
||||
<Col span={spans.span} key={model.value}>
|
||||
<ModelItem
|
||||
key={model.value}
|
||||
ref={(el: React.MutableRefObject<any>) =>
|
||||
setModelRefs(model.value, el)
|
||||
}
|
||||
modelList={modelSelections}
|
||||
model={model.value}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ActiveModels);
|
||||
@@ -1,5 +1,9 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import React from 'react';
|
||||
import { Roles } from '../../config';
|
||||
import '../../style/content-item.less';
|
||||
|
||||
const ContentItem: React.FC<{ data: { role: string; content: string } }> = ({
|
||||
data
|
||||
@@ -8,7 +12,13 @@ const ContentItem: React.FC<{ data: { role: string; content: string } }> = ({
|
||||
return (
|
||||
<div className="content-item">
|
||||
<div className="content-item-role">
|
||||
{' '}
|
||||
<span className="m-r-5">
|
||||
{Roles.User === data.role ? (
|
||||
<UserOutlined></UserOutlined>
|
||||
) : (
|
||||
<IconFont type="icon-AIzhineng"></IconFont>
|
||||
)}
|
||||
</span>
|
||||
{intl.formatMessage({ id: `playground.${data.role}` })}
|
||||
</div>
|
||||
<div className="content-item-content">{data.content}</div>
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { Col, Row } from 'antd';
|
||||
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import CompareContext from '../../config/compare-context';
|
||||
import '../../style/multiple-chat.less';
|
||||
import MessageInput from '../message-input';
|
||||
import ModelItem from './model-item';
|
||||
import ActiveModels from './active-models';
|
||||
|
||||
interface MultiCompareProps {
|
||||
modelList: Global.BaseOption<string>[];
|
||||
parmasSettings?: Record<string, any>;
|
||||
spans?: number;
|
||||
}
|
||||
|
||||
const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
||||
const [loadingStatus, setLoadingStatus] = useState<boolean[]>([]);
|
||||
const [parmasSettings, setParamsSettings] = useState<Record<string, any>>({});
|
||||
const [systemMessage, setSystemMessage] = useState<string>('');
|
||||
const [currentMessage, setCurrentMessage] = useState<
|
||||
{
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}[]
|
||||
const [loadingStatus, setLoadingStatus] = useState<Record<string, boolean>>(
|
||||
{}
|
||||
);
|
||||
const [modelSelections, setModelSelections] = useState<
|
||||
Global.BaseOption<string>[]
|
||||
>([]);
|
||||
const [globalParams, setGlobalParams] = useState<Record<string, any>>({
|
||||
seed: null,
|
||||
@@ -34,88 +31,127 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList }) => {
|
||||
span: 12,
|
||||
count: 2
|
||||
});
|
||||
const modelRefs = useRef<any[]>([]);
|
||||
const modelRefs = useRef<any>({});
|
||||
const boxHeight = 'calc(100vh - 72px)';
|
||||
|
||||
const isLoading = useMemo(() => {
|
||||
return loadingStatus.some((status) => status);
|
||||
console.log('loadingStatus========2', loadingStatus);
|
||||
return _.keys(loadingStatus).some(
|
||||
(modelname: string) => loadingStatus[modelname]
|
||||
);
|
||||
}, [loadingStatus]);
|
||||
|
||||
const modelSelections = useMemo(() => {
|
||||
useEffect(() => {
|
||||
const list = modelList.slice?.(0, spans.count);
|
||||
return list;
|
||||
setModelSelections(list);
|
||||
}, [modelList, spans.count]);
|
||||
|
||||
useEffect(() => {
|
||||
modelRefs.current = modelSelections.map(() => {
|
||||
return {};
|
||||
modelRefs.current = {};
|
||||
modelSelections.forEach((item) => {
|
||||
modelRefs.current[item.value] = null;
|
||||
});
|
||||
}, [modelSelections]);
|
||||
|
||||
const handleSubmit = (message: string) => {
|
||||
let msg: any[] = [];
|
||||
if (message) {
|
||||
msg = [
|
||||
{
|
||||
role: 'user',
|
||||
content: message
|
||||
}
|
||||
];
|
||||
}
|
||||
modelRefs.current.forEach(async (ref, index) => {
|
||||
ref?.setMessageList((preList: any) => {
|
||||
return [...preList, ...msg];
|
||||
});
|
||||
setLoadingStatus((preStatus) => {
|
||||
const newState = [...preStatus];
|
||||
newState[index] = true;
|
||||
return newState;
|
||||
});
|
||||
await ref?.submit();
|
||||
setLoadingStatus((preStatus) => {
|
||||
const newState = [...preStatus];
|
||||
newState[index] = false;
|
||||
return newState;
|
||||
});
|
||||
const handleSubmit = (currentMessage: { role: string; content: string }) => {
|
||||
const modelRefList = _.keys(modelRefs.current);
|
||||
modelRefList.forEach(async (modelname: any, index: number) => {
|
||||
const ref = modelRefs.current[modelname];
|
||||
ref?.submit(currentMessage);
|
||||
});
|
||||
};
|
||||
|
||||
const handleAbortFetch = () => {
|
||||
modelRefs.current.forEach((ref) => {
|
||||
_.keys(modelRefs.current).forEach((modelname: string) => {
|
||||
const ref = modelRefs.current[modelname];
|
||||
ref?.abortFetch();
|
||||
});
|
||||
};
|
||||
|
||||
const setModelRefs = (index: number, ref: any) => {
|
||||
modelRefs.current[index] = ref;
|
||||
const setModelRefs = useCallback(
|
||||
(modelname: string, el: React.MutableRefObject<any>) => {
|
||||
modelRefs.current[modelname] = el;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSetLoadingStatus = (modeName: string, status: boolean) => {
|
||||
setLoadingStatus((preStatus) => {
|
||||
const newState = { ...preStatus };
|
||||
newState[modeName] = status;
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
const handleClearAll = () => {
|
||||
_.keys(modelRefs.current).forEach((modelname: string) => {
|
||||
const ref = modelRefs.current[modelname];
|
||||
ref?.clear();
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteModel = (modelname: string) => {
|
||||
const newModelList = modelSelections.filter(
|
||||
(model) => model.value !== modelname
|
||||
);
|
||||
const span = Math.floor(24 / (24 / spans.span - 1));
|
||||
setSpans({
|
||||
span,
|
||||
count: spans.count
|
||||
});
|
||||
setModelSelections(newModelList);
|
||||
};
|
||||
|
||||
const handleUpdateModelSelections = (list: Global.BaseOption<string>[]) => {
|
||||
// set spans.span
|
||||
const span = Math.floor(24 / list.length);
|
||||
setSpans({
|
||||
span: span < 8 ? 8 : span,
|
||||
count: spans.count
|
||||
});
|
||||
setModelSelections(list);
|
||||
};
|
||||
|
||||
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];
|
||||
ref?.presetPrompt(userMsg);
|
||||
ref?.setSystemMessage(_.get(sysMsg, '0.content', ''));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="multiple-chat">
|
||||
<div className="multiple-chat" style={{ height: boxHeight }}>
|
||||
<div className="chat-list">
|
||||
<Row gutter={[16, 16]} style={{ height: '100%' }}>
|
||||
{modelSelections.map((model, index) => (
|
||||
<Col span={spans.span} key={model.value}>
|
||||
<ModelItem
|
||||
ref={(el: any) => setModelRefs(index, el)}
|
||||
modelList={modelSelections}
|
||||
globalParams={{
|
||||
...globalParams,
|
||||
model: model.value
|
||||
}}
|
||||
systemMessage={systemMessage}
|
||||
setGlobalParams={setGlobalParams}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<CompareContext.Provider
|
||||
value={{
|
||||
spans,
|
||||
globalParams,
|
||||
loadingStatus,
|
||||
setGlobalParams,
|
||||
setLoadingStatus: handleSetLoadingStatus,
|
||||
handleDeleteModel: handleDeleteModel
|
||||
}}
|
||||
>
|
||||
<ActiveModels
|
||||
spans={spans}
|
||||
modelSelections={modelSelections}
|
||||
setModelRefs={setModelRefs}
|
||||
></ActiveModels>
|
||||
</CompareContext.Provider>
|
||||
</div>
|
||||
<div>
|
||||
<MessageInput
|
||||
loading={isLoading}
|
||||
handleSubmit={handleSubmit}
|
||||
handleAbortFetch={handleAbortFetch}
|
||||
setParamsSettings={setParamsSettings}
|
||||
clearAll={handleClearAll}
|
||||
setSpans={setSpans}
|
||||
setModelSelections={handleUpdateModelSelections}
|
||||
presetPrompt={handlePresetPrompt}
|
||||
modelList={modelList}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,49 @@
|
||||
import React from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import ContentItem from './content-item';
|
||||
|
||||
interface MessageContentProps {
|
||||
loading: boolean;
|
||||
spans: {
|
||||
span: number;
|
||||
count: number;
|
||||
};
|
||||
messageList: {
|
||||
role: string;
|
||||
uid?: string;
|
||||
uid?: any;
|
||||
content: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const MessageContent: React.FC<MessageContentProps> = ({ messageList }) => {
|
||||
const MessageContent: React.FC<MessageContentProps> = ({
|
||||
messageList,
|
||||
spans,
|
||||
loading
|
||||
}) => {
|
||||
const maxHeight = useMemo(() => {
|
||||
const total = 72 + 110 + 46 + 16 + 32;
|
||||
if (spans.span < 4) {
|
||||
return `calc(100vh - ${total}px)`;
|
||||
}
|
||||
return `calc(100vh - ${total * 2 + 16}px)`;
|
||||
}, [spans.span]);
|
||||
return (
|
||||
<div>
|
||||
{messageList.map((item, index) => (
|
||||
<ContentItem key={index} data={item} />
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
{messageList.length ? (
|
||||
<SimpleBar style={{ maxHeight: 'calc(100% - 46px)' }}>
|
||||
<div className="message-content-list">
|
||||
{messageList.map((item, index) => (
|
||||
<ContentItem key={index} data={item} />
|
||||
))}
|
||||
</div>
|
||||
</SimpleBar>
|
||||
) : (
|
||||
<span>{loading}</span>
|
||||
)}
|
||||
<Spin spinning={!!loading} size="small" style={{ width: '100%' }} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data';
|
||||
import {
|
||||
ClearOutlined,
|
||||
CloseOutlined,
|
||||
@@ -6,71 +7,200 @@ import {
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Checkbox, Dropdown, Popover, Select } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Input,
|
||||
Popover,
|
||||
Select
|
||||
} from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import useChatCompletion from '../../hooks/use-chat-completion';
|
||||
import { CHAT_API } from '../../apis';
|
||||
import { Roles } from '../../config';
|
||||
import CompareContext from '../../config/compare-context';
|
||||
import '../../style/model-item.less';
|
||||
import ParamsSettings from '../params-settings';
|
||||
import ReferenceParams from '../reference-params';
|
||||
import ViewCodeModal from '../view-code-modal';
|
||||
import MessageContent from './message-content';
|
||||
|
||||
interface ModelItemProps {
|
||||
model?: string;
|
||||
globalParams: Record<string, any>;
|
||||
setGlobalParams: (value: Record<string, any>) => void;
|
||||
model: string;
|
||||
modelList: Global.BaseOption<string>[];
|
||||
systemMessage: string;
|
||||
ref: any;
|
||||
}
|
||||
|
||||
interface MessageItemProps {
|
||||
role: string;
|
||||
content: string;
|
||||
uid: string | number;
|
||||
}
|
||||
|
||||
const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
||||
({ model, systemMessage, modelList, globalParams, setGlobalParams }, ref) => {
|
||||
({ model, modelList }, ref) => {
|
||||
const {
|
||||
spans,
|
||||
globalParams,
|
||||
setGlobalParams,
|
||||
setLoadingStatus,
|
||||
handleDeleteModel,
|
||||
loadingStatus
|
||||
} = useContext(CompareContext);
|
||||
const intl = useIntl();
|
||||
const isApplyToAllModels = useRef(false);
|
||||
const [autoSize, setAutoSize] = useState<{
|
||||
minRows: number;
|
||||
maxRows: number;
|
||||
}>({ minRows: 1, maxRows: 1 });
|
||||
const [systemMessage, setSystemMessage] = useState<string>('');
|
||||
const [params, setParams] = useState<Record<string, any>>({});
|
||||
// const [messageList, setMessageList] = useState<
|
||||
// {
|
||||
// role: 'user' | 'assistant';
|
||||
// content: string;
|
||||
// }[]
|
||||
// >([]);
|
||||
const { messageList, submitMessage, abortFetch, setMessageList, loading } =
|
||||
useChatCompletion(systemMessage, params);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const messageId = useRef<number>(0);
|
||||
const [messageList, setMessageList] = useState<MessageItemProps[]>([]);
|
||||
const [tokenResult, setTokenResult] = useState<any>(null);
|
||||
const [show, setShow] = useState(false);
|
||||
const contentRef = useRef<any>('');
|
||||
const controllerRef = useRef<any>(null);
|
||||
const currentMessageRef = useRef<MessageItemProps>({} as MessageItemProps);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
submit: submitMessage,
|
||||
abortFetch,
|
||||
setMessageList,
|
||||
loading
|
||||
};
|
||||
});
|
||||
const setMessageId = () => {
|
||||
messageId.current = messageId.current + 1;
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
label: intl.formatMessage({ id: 'common.button.clear' }),
|
||||
key: 'clear',
|
||||
icon: <ClearOutlined />
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'playground.viewcode' }),
|
||||
key: 'viewcode',
|
||||
icon: <IconFont type="icon-code" />
|
||||
const abortFetch = () => {
|
||||
controllerRef.current?.abort?.();
|
||||
setLoadingStatus(params.model, false);
|
||||
};
|
||||
|
||||
const joinMessage = (chunk: any) => {
|
||||
if (!chunk) {
|
||||
return;
|
||||
}
|
||||
];
|
||||
if (_.get(chunk, 'choices.0.finish_reason')) {
|
||||
setTokenResult({
|
||||
...chunk?.usage
|
||||
});
|
||||
return;
|
||||
}
|
||||
contentRef.current =
|
||||
contentRef.current + _.get(chunk, 'choices.0.delta.content', '');
|
||||
console.log('currentMessage==========5', messageList);
|
||||
setMessageList([
|
||||
...messageList,
|
||||
{
|
||||
...currentMessageRef.current
|
||||
},
|
||||
{
|
||||
role: Roles.Assistant,
|
||||
content: contentRef.current,
|
||||
uid: messageId.current
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
const handleModelChange = (value: string) => {
|
||||
setParams({
|
||||
...params,
|
||||
model: value
|
||||
});
|
||||
const submitMessage = async (currentParams: {
|
||||
parameters: Record<string, any>;
|
||||
currentMessage: { role: string; content: string };
|
||||
}) => {
|
||||
console.log('currentMessage==========3', currentParams);
|
||||
const { parameters, currentMessage } = currentParams;
|
||||
if (!parameters.model) return;
|
||||
try {
|
||||
setLoadingStatus(parameters.model, true);
|
||||
setMessageId();
|
||||
|
||||
controllerRef.current?.abort?.();
|
||||
controllerRef.current = new AbortController();
|
||||
const signal = controllerRef.current.signal;
|
||||
currentMessageRef.current = {
|
||||
...currentMessage,
|
||||
uid: messageId.current
|
||||
};
|
||||
setMessageList((preList) => {
|
||||
return [
|
||||
...preList,
|
||||
{
|
||||
...currentMessageRef.current
|
||||
}
|
||||
];
|
||||
});
|
||||
console.log('currentMessage==========4', messageList);
|
||||
const messages = _.map(
|
||||
[
|
||||
...messageList,
|
||||
{
|
||||
...currentMessageRef.current
|
||||
}
|
||||
],
|
||||
(item: MessageItemProps) => {
|
||||
return {
|
||||
role: item.role,
|
||||
content: item.content
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
contentRef.current = '';
|
||||
const chatParams = {
|
||||
messages: systemMessage
|
||||
? [
|
||||
{
|
||||
role: Roles.System,
|
||||
content: systemMessage
|
||||
},
|
||||
...messages
|
||||
]
|
||||
: [...messages],
|
||||
...parameters,
|
||||
stream: true
|
||||
};
|
||||
const result = await fetchChunkedData({
|
||||
data: chatParams,
|
||||
url: CHAT_API,
|
||||
signal
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const { reader, decoder } = result;
|
||||
await readStreamData(reader, decoder, (chunk: any) => {
|
||||
joinMessage(chunk);
|
||||
});
|
||||
setLoadingStatus(params.model, false);
|
||||
} catch (error) {
|
||||
console.log('error=====', error);
|
||||
setLoadingStatus(params.model, false);
|
||||
}
|
||||
};
|
||||
const handleDropdownAction = useCallback(({ key }: { key: string }) => {
|
||||
console.log('key:', key);
|
||||
if (key === 'clear') {
|
||||
setMessageList([]);
|
||||
}
|
||||
if (key === 'viewCode') {
|
||||
setShow(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (currentMessage: {
|
||||
role: string;
|
||||
content: string;
|
||||
}) => {
|
||||
console.log('currentMessage==========2', currentMessage);
|
||||
submitMessage({ parameters: params, currentMessage });
|
||||
};
|
||||
|
||||
const handleApplyToAllModels = (e: any) => {
|
||||
@@ -104,17 +234,67 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
||||
}
|
||||
};
|
||||
|
||||
const handleDropdownAction = ({ key }: { key: string }) => {
|
||||
console.log('key:', key);
|
||||
const handleClearMessage = () => {
|
||||
setMessageList([]);
|
||||
setTokenResult(null);
|
||||
setSystemMessage('');
|
||||
currentMessageRef.current = {} as MessageItemProps;
|
||||
};
|
||||
const handleCloseViewCode = () => {
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
const handleModelChange = (value: string) => {
|
||||
setParams({
|
||||
...params,
|
||||
model: value
|
||||
});
|
||||
handleClearMessage();
|
||||
};
|
||||
|
||||
const handlePresetMessageList = (list: MessageItemProps[]) => {
|
||||
currentMessageRef.current = {} as MessageItemProps;
|
||||
const messages = _.map(
|
||||
list,
|
||||
(item: { role: string; content: string }) => {
|
||||
setMessageId();
|
||||
return {
|
||||
role: item.role,
|
||||
content: item.content,
|
||||
uid: messageId.current
|
||||
};
|
||||
}
|
||||
);
|
||||
setTokenResult(null);
|
||||
setMessageList(messages);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
handleDeleteModel(params.model);
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
setAutoSize({
|
||||
minRows: 4,
|
||||
maxRows: 4
|
||||
});
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setAutoSize({
|
||||
minRows: 1,
|
||||
maxRows: 1
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log('globalParams:', globalParams.model, globalParams);
|
||||
setParams({
|
||||
...params,
|
||||
model: model,
|
||||
...globalParams
|
||||
});
|
||||
}, [globalParams]);
|
||||
}, [globalParams, model]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -122,6 +302,18 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
||||
};
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
submit: handleSubmit,
|
||||
abortFetch,
|
||||
setMessageList,
|
||||
clear: handleClearMessage,
|
||||
presetPrompt: handlePresetMessageList,
|
||||
setSystemMessage,
|
||||
loading
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="model-item">
|
||||
<div className="header">
|
||||
@@ -133,9 +325,29 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
||||
value={params.model}
|
||||
></Select>
|
||||
</span>
|
||||
<ReferenceParams usage={tokenResult}></ReferenceParams>
|
||||
<span className="action">
|
||||
<Dropdown
|
||||
menu={{ items: actions, onSelect: handleDropdownAction }}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
label: intl.formatMessage({ id: 'common.button.clear' }),
|
||||
key: 'clear',
|
||||
icon: <ClearOutlined />,
|
||||
onClick: () => {
|
||||
handleDropdownAction({ key: 'clear' });
|
||||
}
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'playground.viewcode' }),
|
||||
key: 'viewcode',
|
||||
icon: <IconFont type="icon-code" />,
|
||||
onClick: () => {
|
||||
handleDropdownAction({ key: 'viewCode' });
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<Button
|
||||
@@ -170,14 +382,43 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
||||
size="small"
|
||||
></Button>
|
||||
</Popover>
|
||||
<Button type="text" icon={<CloseOutlined />} size="small"></Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CloseOutlined />}
|
||||
size="small"
|
||||
onClick={handleDelete}
|
||||
></Button>
|
||||
</span>
|
||||
</div>
|
||||
<SimpleBar style={{ height: 'calc(100% - 46px)' }}>
|
||||
<div className="content">
|
||||
<MessageContent messageList={messageList} />
|
||||
</div>
|
||||
</SimpleBar>
|
||||
<div>
|
||||
<Input.TextArea
|
||||
variant="filled"
|
||||
placeholder="Type system message here"
|
||||
style={{ borderRadius: '0', border: 'none' }}
|
||||
value={systemMessage}
|
||||
autoSize={autoSize}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
allowClear={false}
|
||||
onChange={(e) => setSystemMessage(e.target.value)}
|
||||
></Input.TextArea>
|
||||
<Divider style={{ margin: '0' }}></Divider>
|
||||
</div>
|
||||
<div className="content">
|
||||
<MessageContent
|
||||
spans={spans}
|
||||
messageList={messageList}
|
||||
loading={loadingStatus[params.model]}
|
||||
/>
|
||||
</div>
|
||||
<ViewCodeModal
|
||||
open={show}
|
||||
systemMessage={systemMessage}
|
||||
messageList={messageList}
|
||||
parameters={params}
|
||||
onCancel={handleCloseViewCode}
|
||||
title={intl.formatMessage({ id: 'playground.viewcode' })}
|
||||
></ViewCodeModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Modal, Typography } from 'antd';
|
||||
import React from 'react';
|
||||
import promptList from '../config/prompt';
|
||||
import '../style/prompt-modal.less';
|
||||
|
||||
type ViewModalProps = {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onSelect: (list: { role: string; content: string }[]) => void;
|
||||
};
|
||||
|
||||
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
const { open, onCancel } = props || {};
|
||||
const intl = useIntl();
|
||||
const handleSelect = (item: {
|
||||
title: string;
|
||||
data: { role: string; content: string }[];
|
||||
}) => {
|
||||
props.onSelect(item.data);
|
||||
onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Prompts"
|
||||
open={open}
|
||||
centered={true}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose={true}
|
||||
closeIcon={true}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={660}
|
||||
styles={{
|
||||
body: {
|
||||
maxHeight: '550px',
|
||||
overflow: 'auto'
|
||||
}
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<div className="prompt-wrapper">
|
||||
{promptList.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="prompt-item">
|
||||
<h3 className="title">
|
||||
<span className="text">{item.title}</span>
|
||||
<Button
|
||||
size="middle"
|
||||
type="default"
|
||||
onClick={() => handleSelect(item)}
|
||||
>
|
||||
Use
|
||||
</Button>
|
||||
</h3>
|
||||
{item.data.map((data, i) => {
|
||||
return (
|
||||
<div key={i} className="data-item">
|
||||
<span className="role">{data.role}</span>
|
||||
<span className="prompt">
|
||||
<Typography.Paragraph
|
||||
style={{ margin: 0 }}
|
||||
ellipsis={{ rows: 2, expandable: true, symbol: 'more' }}
|
||||
>
|
||||
{data.content}
|
||||
</Typography.Paragraph>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AddWorker);
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
interface CompareContextProps {
|
||||
spans: {
|
||||
span: number;
|
||||
count: number;
|
||||
};
|
||||
systemMessage?: string;
|
||||
globalParams: Record<string, any>;
|
||||
loadingStatus: Record<string, boolean>;
|
||||
handleDeleteModel: (modelname: string) => void;
|
||||
setSystemMessage?: (message: string) => void;
|
||||
setGlobalParams: (value: Record<string, any>) => void;
|
||||
setLoadingStatus: (modeName: string, status: boolean) => void;
|
||||
}
|
||||
const CompareContext = React.createContext<CompareContextProps>(
|
||||
{} as CompareContextProps
|
||||
);
|
||||
|
||||
export default CompareContext;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Roles } from '.';
|
||||
export default [
|
||||
{
|
||||
title: 'Grammar correction',
|
||||
data: [
|
||||
{
|
||||
role: Roles.System,
|
||||
content:
|
||||
'You will be provided with statements, and your task is to convert them to standard English.'
|
||||
},
|
||||
{
|
||||
role: Roles.User,
|
||||
content: 'She no went to the market.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Summarize for a 2nd grader',
|
||||
data: [
|
||||
{
|
||||
role: Roles.System,
|
||||
content:
|
||||
'Summarize content you are provided with for a second-grade student.'
|
||||
},
|
||||
{
|
||||
role: Roles.User,
|
||||
content:
|
||||
'Jupiter is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets in the Solar System combined. Jupiter is one of the brightest objects visible to the naked eye in the night sky, and has been known to ancient civilizations since before recorded history. It is named after the Roman god Jupiter.[19] When viewed from Earth, Jupiter can be bright enough for its reflected light to cast visible shadows,[20] and is on average the third-brightest natural object in the night sky after the Moon and Venus.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Keywords',
|
||||
data: [
|
||||
{
|
||||
role: Roles.System,
|
||||
content:
|
||||
'You will be provided with a block of text, and your task is to extract a list of keywords from it.'
|
||||
},
|
||||
{
|
||||
role: Roles.User,
|
||||
content:
|
||||
"Black-on-black ware is a 20th- and 21st-century pottery tradition developed by the Puebloan Native American ceramic artists in Northern New Mexico. Traditional reduction-fired blackware has been made for centuries by pueblo artists. Black-on-black ware of the past century is produced with a smooth surface, with the designs applied through selective burnishing or the application of refractory slip. Another style involves carving or incising designs and selectively polishing the raised areas. For generations several families from Kha'po Owingeh and P'ohwhóge Owingeh pueblos have been making black-on-black ware with the techniques passed down from matriarch potters. Artists from other pueblos have also produced black-on-black ware. Several contemporary artists have created works honoring the pottery of their ancestors."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Spreadsheet creator',
|
||||
data: [
|
||||
{
|
||||
role: Roles.User,
|
||||
content:
|
||||
'Create a two-column CSV of top science fiction movies along with the year of release.'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -10,19 +10,10 @@ interface MessageItemProps {
|
||||
uid: number;
|
||||
}
|
||||
|
||||
const useChatCompletion = (
|
||||
systemMessage: string,
|
||||
parameters: Record<string, any>
|
||||
) => {
|
||||
const useChatCompletion = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const messageId = useRef<number>(0);
|
||||
const [messageList, setMessageList] = useState<MessageItemProps[]>([
|
||||
{
|
||||
role: 'user',
|
||||
content: '',
|
||||
uid: messageId.current
|
||||
}
|
||||
]);
|
||||
const [messageList, setMessageList] = useState<MessageItemProps[]>([]);
|
||||
const contentRef = useRef<any>('');
|
||||
const controllerRef = useRef<any>(null);
|
||||
|
||||
@@ -53,12 +44,16 @@ const useChatCompletion = (
|
||||
]);
|
||||
};
|
||||
|
||||
const submitMessage = async () => {
|
||||
const submitMessage = async (pramas: {
|
||||
parameters: Record<string, any>;
|
||||
systemMessage: string;
|
||||
}) => {
|
||||
const { parameters, systemMessage } = pramas;
|
||||
if (!parameters.model) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
setMessageId();
|
||||
|
||||
console.log('messagelist=========2=', messageList);
|
||||
controllerRef.current?.abort?.();
|
||||
controllerRef.current = new AbortController();
|
||||
const signal = controllerRef.current.signal;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.content-item {
|
||||
margin-bottom: 12px;
|
||||
|
||||
&-role {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&-content {
|
||||
word-break: break-word;
|
||||
padding: 8px;
|
||||
border-radius: var(--border-radius-mini);
|
||||
background-color: var(--ant-color-fill-tertiary);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: calc(100vh - 72px);
|
||||
|
||||
.chat-list {
|
||||
flex: 1;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
.prompt-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.text {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: var(--border-radius-base);
|
||||
min-height: 150px;
|
||||
padding: 10px 12px;
|
||||
gap: 8px;
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.role {
|
||||
display: flex;
|
||||
width: 60px;
|
||||
flex-basis: 60px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
flex: 1;
|
||||
padding: 8px;
|
||||
border-radius: var(--border-radius-mini);
|
||||
background-color: var(--ant-color-fill-tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user