chore: images create

This commit is contained in:
jialin
2024-11-22 10:00:34 +08:00
parent c3473673da
commit dde937f460
52 changed files with 1883 additions and 941 deletions
+13 -3
View File
@@ -65,9 +65,19 @@ export const createImages = async (
},
options?: any
) => {
return request(`${CREAT_IMAGE_API}`, {
const res = await fetch(`${CREAT_IMAGE_API}`, {
method: 'POST',
data: params,
cancelToken: options?.cancelToken
body: JSON.stringify(params),
signal: options.signal,
headers: {
'Content-Type': 'application/json'
}
});
if (!res.ok) {
return {
error: true,
data: await res.json()
};
}
return res.json();
};
@@ -13,6 +13,7 @@ interface AudioInputProps {
onAnalyse?: (analyseData: any, frequencyBinCount: any) => void;
onAudioPermission: (audioPermission: boolean) => void;
onRecord?: (isRecording: boolean) => void;
onStop?: () => void;
voiceActivity?: boolean;
type?: 'text' | 'primary' | 'default';
}
@@ -55,7 +56,7 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
const handleStopRecording = () => {
setIsRecording(false);
audioRecorder.current?.stop();
props.onRecord?.(false);
// props.onRecord?.(false);
};
// get all audio tracks
@@ -136,6 +137,7 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
const stopRecording = () => {
audioRecorder.current?.stop();
setIsRecording(false);
props.onRecord?.(false);
};
const handleAudioData = (audioData: any) => {
@@ -0,0 +1,290 @@
import FieldWrapper from '@/components/seal-form/field-wrapper';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { INPUT_WIDTH } from '@/constants';
import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form, InputNumber, Slider, Tooltip } from 'antd';
import _ from 'lodash';
import {
forwardRef,
memo,
useCallback,
useEffect,
useId,
useImperativeHandle,
useMemo
} from 'react';
import { ParamsSchema } from '../config/types';
import CustomLabelStyles from '../style/custom-label.less';
type ParamsSettingsFormProps = {
top_n?: number;
model?: string;
};
type ParamsSettingsProps = {
ref?: any;
selectedModel?: string;
showModelSelector?: boolean;
params?: Record<string, any>;
model?: string;
modelList: Global.BaseOption<string>[];
onValuesChange?: (changeValues: any, value: Record<string, any>) => void;
setParams: (params: any) => void;
globalParams?: Record<string, any>;
paramsConfig?: ParamsSchema[];
initialValues?: Record<string, any>;
extra?: React.ReactNode[];
};
const ParamsSettings: React.FC<ParamsSettingsProps> = forwardRef(
(
{
setParams,
onValuesChange,
selectedModel,
globalParams,
initialValues,
paramsConfig,
modelList,
params,
showModelSelector = true,
extra
},
ref
) => {
const intl = useIntl();
const [form] = Form.useForm();
const formId = useId();
useImperativeHandle(ref, () => ({
form
}));
useEffect(() => {
if (showModelSelector) {
form.setFieldsValue({
model: selectedModel || _.get(modelList, '[0].value'),
...initialValues
});
setParams({
model: selectedModel || _.get(modelList, '[0].value'),
...initialValues
});
} else {
form.setFieldsValue({
model: selectedModel || '',
...initialValues
});
setParams({
model: selectedModel || '',
...initialValues
});
}
}, [modelList, showModelSelector, selectedModel, initialValues]);
const handleOnFinish = (values: any) => {
console.log('handleOnFinish', values);
};
const handleOnFinishFailed = (errorInfo: any) => {
console.log('handleOnFinishFailed', errorInfo);
};
const handleValuesChange = useCallback(
(changedValues: any, allValues: any) => {
setParams?.(allValues);
onValuesChange?.(changedValues, allValues);
},
[onValuesChange, setParams]
);
const handleFieldValueChange = useCallback(
(val: any, field: string) => {
const values = form.getFieldsValue();
form.setFieldsValue({
...values,
[field]: val
});
setParams({
...values,
[field]: val
});
onValuesChange?.(
{ [field]: val },
{
...values,
[field]: val
}
);
},
[form, setParams, onValuesChange]
);
useEffect(() => {
form.setFieldsValue(globalParams);
}, [globalParams]);
const renderLabel = useCallback(
(args: { field: string; label: string; description: string }) => {
return (
<span
className={CustomLabelStyles.label}
style={{ width: INPUT_WIDTH.mini }}
>
<span className="text">
{args.description ? (
<Tooltip title={args.description}>
<span> {args.label}</span>
<span className="m-l-5">
<InfoCircleOutlined />
</span>
</Tooltip>
) : (
<span>{args.label}</span>
)}
</span>
<InputNumber
className="label-val"
variant="outlined"
size="small"
value={form.getFieldValue(args.field)}
controls={false}
onChange={(val) => handleFieldValueChange(val, args.field)}
></InputNumber>
</span>
);
},
[form, handleFieldValueChange]
);
const renderFields = useMemo(() => {
console.log('paramsConfig:', paramsConfig);
if (!paramsConfig?.length) {
return null;
}
return paramsConfig.map((item: ParamsSchema) => {
if (item.type === 'InputNumber') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealInput.Number
{...item.attrs}
style={{ width: '100%' }}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealInput.Number>
</Form.Item>
);
}
if (item.type === 'TextArea') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealInput.TextArea
{...item.attrs}
style={{ width: '100%' }}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealInput.TextArea>
</Form.Item>
);
}
if (item.type === 'Select') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealSelect
{...item.attrs}
options={item.options}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealSelect>
</Form.Item>
);
}
if (item.type === 'Slider') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<FieldWrapper
label={renderLabel({
field: item.name,
label: item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text,
description: item.description?.isLocalized
? intl.formatMessage({ id: item.description?.text })
: item.description?.text || ''
})}
style={{ padding: '20px 2px 0' }}
variant="borderless"
>
<Slider
{...item.attrs}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}
value={form.getFieldValue(item.name) || undefined}
onChange={(val) => handleFieldValueChange(val, item.name)}
></Slider>
</FieldWrapper>
</Form.Item>
);
}
return null;
});
}, [paramsConfig, params]);
return (
<Form
name={formId}
form={form}
onValuesChange={handleValuesChange}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
>
<div>
{
<>
<h3 className="m-b-20 m-l-10 font-size-14 line-24">
<span>
{intl.formatMessage({ id: 'playground.parameters' })}
</span>
</h3>
<Form.Item<ParamsSettingsFormProps>
name="model"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.select'
},
{ name: intl.formatMessage({ id: 'playground.model' }) }
)
}
]}
>
<SealSelect
showSearch={true}
options={modelList}
label={intl.formatMessage({ id: 'playground.model' })}
></SealSelect>
</Form.Item>
</>
}
{renderFields}
{extra}
</div>
</Form>
);
}
);
export default memo(ParamsSettings);
@@ -1,18 +1,21 @@
import AlertInfo from '@/components/alert-info';
import ScatterChart from '@/components/echarts/scatter';
import HighlightCode from '@/components/highlight-code';
import IconFont from '@/components/icon-font';
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import useRequestToken from '@/hooks/use-request-token';
import {
ClearOutlined,
LoadingOutlined,
HolderOutlined,
PlusOutlined,
ThunderboltOutlined,
UploadOutlined
SendOutlined
} from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Tooltip } from 'antd';
import { Button, Segmented, Tabs } from 'antd';
import classNames from 'classnames';
import { PCA } from 'ml-pca';
import 'overlayscrollbars/overlayscrollbars.css';
import { Resizable } from 're-resizable';
import {
forwardRef,
memo,
@@ -23,16 +26,14 @@ import {
useRef,
useState
} from 'react';
import { UMAP } from 'umap-js';
import { handleEmbedding } from '../apis';
import { OpenAIViewCode } from '../config';
import { ParamsSchema } from '../config/types';
import '../style/ground-left.less';
import '../style/rerank.less';
import '../style/system-message-wrap.less';
import DynamicParams from './dynamic-params';
import FileList from './file-list';
import InputList from './input-list';
import RerankerParams from './reranker-params';
import UploadFile from './upload-file';
import ViewCodeModal from './view-code-modal';
interface MessageProps {
@@ -41,42 +42,11 @@ interface MessageProps {
ref?: any;
}
const paramsConfig: ParamsSchema[] = [
// {
// type: 'Select',
// name: 'truncate',
// label: {
// text: 'Truncate',
// isLocalized: false
// },
// options: [
// {
// label: 'None',
// value: 'none'
// },
// {
// label: 'Start',
// value: 'start'
// },
// {
// label: 'End',
// value: 'end'
// }
// ],
// rules: [
// {
// required: true,
// message: 'Please select truncate'
// }
// ]
// }
];
const paramsConfig: ParamsSchema[] = [];
const initialValues = {
truncate: 'none'
};
const initialValues = {};
const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
const { modelList } = props;
const acceptType =
'.txt, .doc, .docx, .xls, .xlsx, .csv, .md, .pdf, .eml, .msg, .ppt, .pptx, .xml, .epub, .html';
@@ -100,6 +70,10 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const [fileList, setFileList] = useState<
{ text: string; name: string; uid: number | string }[]
>([]);
const [outputType, setOutputType] = useState<string>('chart');
const [outputHeight, setOutputHeight] = useState<number>(180);
const [embeddingData, setEmbeddingData] = useState<string>('');
const [lessTwoInput, setLessTwoInput] = useState<boolean>(false);
const [textList, setTextList] = useState<
{ text: string; uid: number | string; name: string }[]
@@ -136,56 +110,23 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
});
const inputEmpty = useMemo(() => {
const list = [...textList, ...fileList].filter((item) => item.text);
const list = [...textList, ...fileList];
return list.length < 2;
}, [textList, fileList]);
const cosine = useCallback((x: number[], y: number[]) => {
let result = 0;
let normX = 0;
let normY = 0;
for (let i = 0; i < x.length; i++) {
result += x[i] * y[i];
normX += x[i] ** 2;
normY += y[i] ** 2;
}
if (normX === 0 && normY === 0) {
return 0;
} else if (normX === 0 || normY === 0) {
return 1;
} else {
return 1 - result / Math.sqrt(normX * normY);
}
}, []);
const generateEmbedding = useCallback(
(embeddings: any[]) => {
try {
const umap = new UMAP({
// random: random,
minDist: 0,
nComponents: 3,
nEpochs: 200,
distanceFn: cosine,
nNeighbors: embeddings.length - 1
});
const dataList = embeddings.map((item) => {
return item.embedding;
});
const embedding = umap.fit([...dataList]);
console.log('embedding:----------------', embedding);
const pca = new PCA(dataList, {});
console.log('dataList====', dataList, embeddings);
const pca = new PCA(dataList);
const pcadata = pca.predict(dataList, { nComponents: 2 }).to2DArray();
console.log('pcadata++++++++++++++++', pcadata);
const input = [
...textList.map((item) => item.text),
...fileList.map((item) => item.text)
...textList.map((item) => item.text).filter((item) => item),
...fileList.map((item) => item.text).filter((item) => item)
];
const list = pcadata.map((item: number[], index: number) => {
@@ -195,13 +136,8 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
text: input[index]
};
});
console.log('embedding____________:', {
list,
input,
textList,
fileList
});
setScatterData(list);
setEmbeddingData(JSON.stringify(embeddings, null, 2));
} catch (e) {
console.log('error:', e);
}
@@ -220,7 +156,26 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const submitMessage = async (current?: { role: string; content: string }) => {
if (!parameters.model) return;
try {
const validTextList = textList.filter((item) => item.text);
const validFileList = fileList.filter((item) => item.text);
const inputList = [
...validTextList.map((item) => item.text),
...validFileList.map((item) => item.text)
];
if (inputList.length < 2) {
setLessTwoInput(true);
return;
}
setTextList(validTextList);
setFileList(validFileList);
setLessTwoInput(false);
setLoading(true);
setMessageId();
setTokenResult(null);
@@ -234,26 +189,19 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
{
model: parameters.model,
encoding_format: 'float',
input: [
...textList.map((item) => item.text),
...fileList.map((item) => item.text)
]
input: inputList
},
{
token: requestToken.current.token
}
);
console.log('result:', result);
setTokenResult(result.usage);
const embeddingsList = result.data || [];
console.log('embeddings:', embeddingsList);
generateEmbedding(embeddingsList);
} catch (error: any) {
console.log('error========', error);
setTokenResult({
error: true,
errorMessage: error.response?.data?.error?.message
@@ -280,6 +228,18 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
});
};
const handleScaleOutputSize = (
e: any,
direction: string,
ref: any,
d: any
) => {
console.log('handleScaleOutputSize', e, direction, ref, d);
if (d.height + outputHeight <= 300 && d.height + outputHeight >= 180) {
setOutputHeight(d.height + outputHeight);
}
};
const handleDeleteFile = (uid: number | string) => {
setFileList((preList) => {
return preList.filter((item) => item.uid !== uid);
@@ -310,8 +270,47 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
]);
setFileList([]);
setScatterData([]);
setTokenResult(null);
setLessTwoInput(false);
};
const handleOutputTypeChange = (value: string) => {
setOutputType(value);
};
const outputItems = useMemo(() => {
return [
{
key: 'chart',
label: 'Chart',
children: (
<ScatterChart
seriesData={scatterData}
height={outputHeight}
width="100%"
xAxisData={[]}
></ScatterChart>
)
},
{
key: 'json',
label: 'JSON',
children: (
<div style={{ padding: 10, backgroundColor: '#fafafa' }}>
<HighlightCode
height={outputHeight - 20}
theme="light"
code={embeddingData}
lang="json"
copyable={true}
style={{ marginBottom: 0 }}
></HighlightCode>
</div>
)
}
];
}, [outputHeight, scatterData, embeddingData]);
useEffect(() => {
setMessageId();
setScatterData([]);
@@ -339,7 +338,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
return (
<div className="ground-left-wrapper rerank">
<div className="ground-left" style={{ justifyContent: 'flex-start' }}>
<div className="ground-left">
<div
className="center"
ref={scroller}
@@ -348,25 +347,18 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="documents">
<div className="flex-between m-b-8 doc-header">
<h3 className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-0">
<span>Documents</span>
<div className="flex gap-20">
<span>
{intl.formatMessage({
id: 'playground.embedding.documents'
})}
</span>
</div>
</h3>
<div className="flex gap-10">
<UploadFile
handleUpdateFileList={handleUpdateFileList}
accept={acceptType}
>
<Tooltip title={<span>Support: {acceptType}</span>}>
<Button
size="middle"
icon={<UploadOutlined></UploadOutlined>}
>
Upload File
</Button>
</Tooltip>
</UploadFile>
<Button size="middle" onClick={handleAddText}>
<PlusOutlined />
Add Text
{intl.formatMessage({ id: 'playground.embedding.addtext' })}
</Button>
<Button
icon={<ClearOutlined />}
@@ -375,15 +367,31 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
>
{intl.formatMessage({ id: 'common.button.clear' })}
</Button>
<Button
size="middle"
type="primary"
disabled={inputEmpty}
onClick={handleSendMessage}
>
{loading ? <LoadingOutlined /> : <ThunderboltOutlined />}
{intl.formatMessage({ id: 'common.button.run' })}
</Button>
{!loading ? (
<Button
size="middle"
type="primary"
disabled={inputEmpty}
onClick={handleSendMessage}
icon={<SendOutlined rotate={0} className="font-size-14" />}
style={{ width: 60 }}
></Button>
) : (
<Button
style={{ width: 80 }}
size="middle"
type="primary"
onClick={handleStopConversation}
icon={
<IconFont
type="icon-stop1"
className="font-size-12"
></IconFont>
}
>
{intl.formatMessage({ id: 'common.button.stop' })}
</Button>
)}
</div>
</div>
<div className="docs-wrapper">
@@ -399,6 +407,12 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
onDelete={handleDeleteFile}
></FileList>
</div>
{lessTwoInput && (
<AlertInfo
type="danger"
message="Please input at least two documents"
></AlertInfo>
)}
</div>
</div>
</div>
@@ -411,23 +425,75 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
}}
>
<h3 className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-16">
<span>Output</span>
<div className="flex gap-20">
<span>
{intl.formatMessage({ id: 'playground.embedding.output' })}
</span>
<AlertInfo
type="danger"
message={tokenResult?.errorMessage}
></AlertInfo>
</div>
<Segmented
onChange={handleOutputTypeChange}
value={outputType}
options={[
{
label: intl.formatMessage({
id: 'playground.embedding.chart'
}),
value: 'chart'
},
{ label: 'JSON', value: 'json' }
]}
></Segmented>
</h3>
<div
style={{
border: '1px solid var(--ant-color-border)',
borderRadius: 'var(--border-radius-base)',
overflow: 'hidden',
width: '100%'
}}
className="scatter"
>
<ScatterChart
seriesData={scatterData}
height={160}
width="100%"
xAxisData={[]}
></ScatterChart>
<div className="embed-chart">
<Resizable
enable={{
top: true
}}
defaultSize={{
height: 180
}}
handleComponent={{
top: (
<Button
size="small"
className="drag-handler"
color="default"
variant="filled"
icon={
<HolderOutlined
rotate={90}
style={{ fontSize: 'var(--font-size-14)' }}
/>
}
></Button>
)
}}
maxHeight={300}
minHeight={180}
onResizeStop={handleScaleOutputSize}
>
<div
style={{
border: '1px solid var(--ant-color-border)',
borderRadius: 'var(--border-radius-base)',
overflow: 'hidden',
width: '100%'
}}
className="scatter "
>
<Tabs
defaultActiveKey={outputType}
activeKey={outputType}
centered
renderTabBar={() => <></>}
items={outputItems}
></Tabs>
</div>
</Resizable>
</div>
</div>
</div>
@@ -438,7 +504,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
ref={paramsRef}
>
<div className="box">
<RerankerParams
<DynamicParams
setParams={setParams}
paramsConfig={paramsConfig}
initialValues={initialValues}
@@ -450,13 +516,13 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
</div>
<ViewCodeModal
{...OpenAIViewCode.embeddings}
open={show}
apiType="embedding"
payLoad={{
input: [
...textList.map((item) => item.text),
...fileList.map((item) => item.text)
].filter((text) => text)
...textList.map((item) => item.text).filter((item) => item),
...fileList.map((item) => item.text).filter((item) => item)
]
}}
parameters={{
...parameters
@@ -468,4 +534,4 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
);
});
export default memo(GroundReranker);
export default memo(GroundEmbedding);
+326 -60
View File
@@ -1,26 +1,33 @@
import AlertInfo from '@/components/alert-info';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import useRequestToken from '@/hooks/use-request-token';
import ThumbImg from '@/pages/playground/components/thumb-img';
import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data';
import { FileImageOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Form } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import 'overlayscrollbars/overlayscrollbars.css';
import React, {
forwardRef,
memo,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState
} from 'react';
import { createImages } from '../apis';
import { CREAT_IMAGE_API } from '../apis';
import { OpenAIViewCode } from '../config';
import { ImageParamsConfig as paramsConfig } from '../config/params-config';
import { MessageItem } from '../config/types';
import { MessageItem, ParamsSchema } from '../config/types';
import '../style/ground-left.less';
import '../style/system-message-wrap.less';
import DynamicParams from './dynamic-params';
import MessageInput from './message-input';
import ReferenceParams from './reference-params';
import RerankerParams from './reranker-params';
import ViewCodeModal from './view-code-modal';
interface MessageProps {
@@ -33,18 +40,98 @@ const initialValues = {
n: 1,
size: '512x512',
quality: 'standard',
style: 'vivid'
style: ''
};
const extraConfig: ParamsSchema[] = [
{
type: 'Select',
name: 'quality',
options: [
{ label: 'standard', value: 'standard' },
{ label: 'hd', value: 'hd' }
],
label: {
text: 'playground.params.quality',
isLocalized: true
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'style',
options: [
{ label: 'vivid', value: 'vivid' },
{ label: 'natural', value: 'natural' }
],
label: {
text: 'playground.params.style',
isLocalized: true
},
rules: [
{
required: false
}
]
}
];
const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
const { modelList } = props;
const messageId = useRef<number>(0);
const [messageList, setMessageList] = useState<
{ dataUrl: string; height: number; uid: number }[]
>([]);
const [imageList, setImageList] = useState<
{
dataUrl: string;
height: number | string;
width: string | number;
uid: number;
span?: number;
progress?: number;
}[]
>([
{
dataUrl:
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
height: 'auto',
width: 'auto',
uid: 0,
span: 12,
progress: 10
},
{
dataUrl:
'https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp',
height: 'auto',
width: 'auto',
uid: 1,
span: 12,
progress: 15
},
{
dataUrl:
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
height: 'auto',
width: 'auto',
uid: 3,
span: 12,
progress: 10
},
{
dataUrl:
'https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp',
height: 'auto',
width: 'auto',
uid: 4,
span: 12,
progress: 15
}
]);
const intl = useIntl();
const requestSource = useRequestToken();
const [searchParams] = useSearchParams();
const selectModel = searchParams.get('model') || '';
const [parameters, setParams] = useState<any>({});
@@ -57,6 +144,9 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
const messageListLengthCache = useRef<number>(0);
const requestToken = useRef<any>(null);
const [currentPrompt, setCurrentPrompt] = useState<string>('');
const form = useRef<any>(null);
const size = Form.useWatch('size', form.current?.form);
const { initialize, updateScrollerPosition } = useOverlayScroller();
const { initialize: innitializeParams } = useOverlayScroller();
@@ -73,79 +163,150 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
};
});
const setImageSize = useCallback(() => {
let size: Record<string, string | number> = {
with: 256,
height: 256,
span: 12
};
if (parameters.n === 1) {
size.width = '100%';
size.height = '100%';
size.span = 24;
}
if (parameters.n === 2) {
size.width = '50%';
size.height = 256;
size.span = 12;
}
if (parameters.n === 3) {
size.width = '33%';
size.height = 256;
size.span = 12;
}
if (parameters.n === 4) {
size.width = '25%';
size.height = 256;
size.span = 12;
}
return size;
}, [parameters.n]);
const finalParameters = useMemo(() => {
if (parameters.size === 'custom') {
return {
..._.omit(parameters, ['width', 'height']),
size:
parameters.width && parameters.height
? `${parameters.width}x${parameters.height}`
: ''
};
}
return {
..._.omit(parameters, ['width', 'height'])
};
}, [parameters]);
const setMessageId = () => {
messageId.current = messageId.current + 1;
return messageId.current;
};
const handleStopConversation = () => {
requestToken.current?.cancel?.();
requestToken.current?.abort?.();
setLoading(false);
};
const submitMessage = async (current?: { role: string; content: string }) => {
if (!parameters.model) return;
try {
await form.current?.form?.validateFields();
if (!parameters.model) return;
const size: any = setImageSize();
setLoading(true);
setMessageId();
setCurrentPrompt(current?.content || '');
setMessageList(
Array(parameters.n)
.fill({})
.map((item, index: number) => {
return {
dataUrl: '',
height: 256,
width: 256,
uid: index
};
})
);
requestToken.current?.cancel?.();
requestToken.current = requestSource();
let newImageList = Array(parameters.n)
.fill({})
.map((item, index: number) => {
return {
dataUr: '',
...size,
progress: 0,
height: 'auto',
width: 'auto',
uid: index
};
});
setImageList(newImageList);
requestToken.current?.abort?.();
requestToken.current = new AbortController();
const params = {
stream: true,
prompt: current?.content || currentPrompt || '',
...parameters
..._.omitBy(finalParameters, (value: string) => !value)
};
const result = await createImages(params, {
cancelToken: requestToken.current.token
const result: any = await fetchChunkedData({
data: params,
url: CREAT_IMAGE_API,
signal: requestToken.current.signal
});
const imgList = _.map(result.data, (item: any, index: number) => {
return {
dataUrl: `data:image/png;base64,${item.b64_json}`,
created: result.created,
height: 256,
width: 256,
uid: index
};
});
setMessageList(imgList);
console.log('result:', imgList);
if (result?.error) {
setTokenResult({
error: true,
errorMessage:
result?.data?.error?.message || result?.data?.message || ''
});
return;
}
setMessageId();
const { reader, decoder } = result;
await readStreamData(reader, decoder, (chunk: any) => {
if (chunk?.error) {
setTokenResult({
error: true,
errorMessage: chunk?.error?.message || chunk?.message || ''
});
return;
}
const data = chunk?.data || [];
data.forEach((item: any) => {
const imgItem = newImageList[item.index];
newImageList[item.index] = {
dataUrl: `data:image/png;base64,${item.b64_json}`,
height: '100%',
width: '100%',
uid: imgItem.uid,
span: imgItem.span,
progress: _.round(item.progress, 0)
};
});
setImageList([...newImageList]);
});
console.log('result:', newImageList);
} catch (error) {
// console.log('error:', error);
requestToken.current?.cancel?.();
setMessageList([]);
console.log('error:', error);
requestToken.current?.abort?.();
setImageList([]);
} finally {
setLoading(false);
}
};
const handleClear = () => {
if (!messageList.length) {
if (!imageList.length) {
return;
}
setMessageId();
setMessageList([]);
setImageList([]);
setTokenResult(null);
};
const handleSendMessage = (message: Omit<MessageItem, 'uid'>) => {
console.log('message:', message);
const currentMessage = message.content ? message : undefined;
submitMessage(currentMessage);
};
@@ -154,6 +315,78 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
setShow(false);
};
const renderExtra = useMemo(() => {
return extraConfig.map((item: ParamsSchema) => {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealSelect
{...item.attrs}
options={item.options}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealSelect>
</Form.Item>
);
});
}, [extraConfig, intl]);
const renderCustomSize = useMemo(() => {
if (size === 'custom') {
return (
<div className="flex gap-10" key="custom">
<Form.Item
name="width"
key="width"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.input'
},
{
name: intl.formatMessage({ id: 'playground.params.width' })
}
)
}
]}
>
<SealInput.Number
style={{ width: '100%' }}
label={intl.formatMessage({ id: 'playground.params.width' })}
></SealInput.Number>
</Form.Item>
<Form.Item
name="height"
key="height"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.input'
},
{
name: intl.formatMessage({ id: 'playground.params.height' })
}
)
}
]}
>
<SealInput.Number
style={{ width: '100%' }}
label="Height"
></SealInput.Number>
</Form.Item>
</div>
);
}
return null;
}, [size, intl]);
useEffect(() => {
if (scroller.current) {
initialize(scroller.current);
@@ -170,42 +403,70 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
if (loading) {
updateScrollerPosition();
}
}, [messageList, loading]);
}, [imageList, loading]);
useEffect(() => {
if (messageList.length > messageListLengthCache.current) {
if (imageList.length > messageListLengthCache.current) {
updateScrollerPosition();
}
messageListLengthCache.current = messageList.length;
}, [messageList.length]);
messageListLengthCache.current = imageList.length;
console.log('imageList:', imageList);
}, [imageList.length]);
return (
<div className="ground-left-wrapper">
<div className="ground-left">
<div className="message-list-wrap" ref={scroller}>
<div
className="message-list-wrap"
ref={scroller}
style={{ paddingBottom: 16 }}
>
<>
<div className="content">
<div className="content" style={{ height: '100%' }}>
<ThumbImg
style={{ paddingInline: 0 }}
style={{
padding: 0,
height: '100%',
justifyContent: 'center',
flexDirection: 'column',
flexWrap: 'unset',
alignItems: 'center'
}}
editable={false}
dataList={messageList}
dataList={imageList}
loading={loading}
responseable={true}
gutter={[16, 16]}
autoSize={true}
></ThumbImg>
{!imageList.length && (
<div className="flex-column font-size-14 flex-center gap-20 justify-center hold-wrapper">
<span>
<FileImageOutlined className="font-size-32 text-secondary" />
</span>
<span>
{intl.formatMessage({ id: 'playground.params.empty.tips' })}
</span>
</div>
)}
</div>
</>
</div>
{tokenResult && (
<div style={{ height: 40 }}>
<ReferenceParams usage={tokenResult}></ReferenceParams>
<AlertInfo
type="danger"
message={tokenResult?.errorMessage}
></AlertInfo>
</div>
)}
<div className="ground-left-footer">
<MessageInput
placeholer="Type <kbd>/</kbd> to input prompt"
actions={['clear']}
actions={[]}
loading={loading}
disabled={!parameters.model}
isEmpty={!messageList.length}
isEmpty={!imageList.length}
handleSubmit={handleSendMessage}
handleAbortFetch={handleStopConversation}
shouldResetMessage={false}
@@ -220,23 +481,28 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
ref={paramsRef}
>
<div className="box">
<RerankerParams
<DynamicParams
ref={form}
setParams={setParams}
paramsConfig={paramsConfig}
initialValues={initialValues}
params={parameters}
selectedModel={selectModel}
modelList={modelList}
extra={[renderCustomSize, ...renderExtra]}
/>
</div>
</div>
<ViewCodeModal
{...OpenAIViewCode.images}
open={show}
payLoad={{
prompt: currentPrompt
}}
parameters={parameters}
parameters={{
...finalParameters
}}
onCancel={handleCloseViewCode}
title={intl.formatMessage({ id: 'playground.viewcode' })}
></ViewCodeModal>
@@ -15,7 +15,7 @@ import {
useState
} from 'react';
import { CHAT_API } from '../apis';
import { Roles, generateMessages } from '../config';
import { OpenAIViewCode, Roles, generateMessages } from '../config';
import { MessageItem } from '../config/types';
import '../style/ground-left.less';
import '../style/system-message-wrap.less';
@@ -274,10 +274,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="content">
<MessageContent
spans={{
span: 24,
count: 1
}}
messageList={messageList}
setMessageList={setMessageList}
editable={true}
@@ -298,7 +294,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
)}
<div className="ground-left-footer">
<MessageInput
scope="chat"
loading={loading}
disabled={!parameters.model}
isEmpty={!messageList.length}
@@ -329,6 +324,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
</div>
<ViewCodeModal
{...OpenAIViewCode.chat}
open={show}
payLoad={{
messages: viewCodeMessage
@@ -1,8 +1,8 @@
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import useRequestToken from '@/hooks/use-request-token';
import { ClearOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons';
import { ClearOutlined, PlusOutlined, SendOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Progress, Spin, Tag } from 'antd';
import { Button, Input, Spin, Tag } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import 'overlayscrollbars/overlayscrollbars.css';
@@ -20,9 +20,8 @@ import { MessageItem, ParamsSchema } from '../config/types';
import '../style/ground-left.less';
import '../style/rerank.less';
import '../style/system-message-wrap.less';
import DynamicParams from './dynamic-params';
import InputList from './input-list';
import MessageInput from './message-input';
import RerankerParams from './reranker-params';
import ViewRerankCode from './view-rerank-code';
interface MessageProps {
@@ -52,7 +51,7 @@ const paramsConfig: ParamsSchema[] = [
];
const initialValues = {
top_n: 1
top_n: 3
};
const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
@@ -111,6 +110,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
name: ''
}
]);
const [sortIndexMap, setSortIndexMap] = useState<number[]>([]);
const { initialize, updateScrollerPosition: updateDocumentScrollerPosition } =
useOverlayScroller();
@@ -145,51 +145,33 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
};
const renderPercent = useCallback((data: any) => {
if (!data.showExtra) {
if (!data.showExtra || !data.percent) {
return null;
}
const percent = data.percent;
return (
<>
<Progress
size={{
height: 4
}}
type="line"
status="normal"
strokeLinecap={'square'}
showInfo={false}
percentPosition={{ align: 'end', type: 'outer' }}
strokeColor={`linear-gradient(90deg, #388bff 0%, rgba(255,255,255,1) ${percent}%)`}
trailColor="transparent"
percent={percent}
style={{
position: 'absolute',
left: 0,
bottom: -2,
width: 'calc(100% - 2px)',
lineHeight: '12px',
borderRadius: '0 0 0 6px',
overflow: 'hidden'
}}
></Progress>
<span
className="flex-center hover-hidden"
style={{
position: 'absolute',
right: 10,
top: 8,
padding: '0 4px',
backgroundColor: 'transparent',
opacity: 0.7
}}
>
<Tag color={'geekblue'}>Rank: {data.rank}</Tag>
<Tag style={{ margin: 0 }} color={'cyan'}>
Score: {_.round(data.score, 2)}
<div className="rank-wrapper">
<div className="percent-wrapper">
<div
className="pregress-bar"
style={{
backgroundImage: `linear-gradient(90deg, #388bff 0%, #cce1ff 100%)`,
width: `${percent}%`,
height: '4px',
borderRadius: '2px'
}}
></div>
</div>
<span className="flex-center hover-hidden rank-tag">
<Tag color={'geekblue'} bordered={false}>
{intl.formatMessage({ id: 'playground.rerank.rank' })}: {data.rank}
</Tag>
<Tag color={'cyan'} bordered={false}>
{intl.formatMessage({ id: 'playground.rerank.score' })}:{' '}
{_.round(data.score, 2)}
</Tag>
</span>
</>
</div>
);
}, []);
@@ -203,12 +185,13 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
setLoading(false);
};
const submitMessage = async (current?: { role: string; content: string }) => {
const submitMessage = async (current?: { content: string }) => {
if (!parameters.model) return;
try {
setLoading(true);
setMessageId();
setTokenResult(null);
setSortIndexMap([]);
requestToken.current?.cancel?.();
requestToken.current = requestSource();
@@ -242,11 +225,22 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const maxValue = sortList[sortList.length - 1].relevance_score;
const minValue = sortList[0].relevance_score;
let newTextList = [...textList];
// reset state
let newTextList = textList.map((item) => {
item.percent = undefined;
item.score = undefined;
item.rank = undefined;
return item;
});
let sortMap: number[] = [];
result.results?.forEach((item: any, sIndex: number) => {
sortMap.push(item.index);
newTextList[item.index] = {
...newTextList[item.index],
uid: setMessageId(),
rank: sIndex + 1,
score: item.relevance_score,
showExtra: true,
@@ -257,6 +251,9 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
})
};
});
newTextList = _.sortBy(newTextList, 'rank');
setSortIndexMap(sortMap);
setTextList(newTextList);
setMessageList([
@@ -270,7 +267,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
value: item.relevance_score
});
return {
uid: item.index,
uid: setMessageId(),
text: `${item.document?.text?.slice(0, 500) || ''}`,
docIndex: item.index,
title: documentList[item.index]?.name || '',
@@ -279,7 +276,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
normalizValue: percent
};
}),
uid: messageId.current
uid: setMessageId()
}
]);
} catch (error: any) {
@@ -304,6 +301,11 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
submitMessage(message);
};
const handleSearch = (val: string) => {
console.log('val:', val);
submitMessage({ content: val });
};
const handleCloseViewCode = () => {
setShow(false);
};
@@ -326,11 +328,30 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
inputListRef.current?.handleAdd();
};
const handleTextListChange = (
list: { text: string; uid: number | string; name: string }[]
) => {
setTextList(list);
};
const handleTextListChange = useCallback(
(list: { text: string; uid: number | string; name: string }[]) => {
const newList = list?.map((item: any) => {
item.percent = undefined;
item.score = undefined;
item.rank = undefined;
return item;
});
setTextList(newList);
},
[]
);
const handleOnSort = useCallback(
(list: { text: string; uid: number | string; name: string }[]) => {
const newList = list?.map((item) => {
return {
...item,
uid: setMessageId()
};
});
setTextList(newList);
},
[]
);
const handleClearDocuments = () => {
setTextList([
@@ -381,50 +402,43 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="ground-left-wrapper rerank">
<div className="ground-left">
<div className="ground-left-footer">
<MessageInput
actions={[]}
defaultSize={{
minRows: 1,
maxRows: 2
}}
submitIcon={<SearchOutlined className="font-size-16" />}
loading={loading}
disabled={!parameters.model}
isEmpty={true}
shouldResetMessage={false}
handleSubmit={handleSendMessage}
handleAbortFetch={handleStopConversation}
clearAll={handleClear}
modelList={[]}
placeholer={intl.formatMessage({
id: 'playground.input.keyword.holder'
})}
tools={
<span style={{ paddingLeft: 6, fontSize: 14, fontWeight: 500 }}>
Query
</span>
}
style={{
borderTop: 'none',
width: 'unset',
marginInline: 32,
marginTop: 16,
border: '1px solid var(--ant-color-border)',
borderRadius: 'var(--border-radius-base)',
paddingInline: 10
}}
/>
<h3
className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-0"
style={{ padding: '0 32px', marginTop: 16 }}
>
<span>{intl.formatMessage({ id: 'playground.rerank.query' })}</span>
</h3>
<div style={{ margin: '16px 32px 10px' }}>
<Input.Search
onSearch={handleSearch}
enterButton={<SendOutlined rotate={0} className="font-size-14" />}
placeholder={intl.formatMessage({
id: 'playground.rerank.query.holder'
})}
></Input.Search>
</div>
</div>
<div className="center" ref={scroller}>
<div className="documents">
<div className="flex-between m-b-8 doc-header">
<h3 className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-0">
<span>Documents</span>
<span>
{intl.formatMessage({ id: 'playground.embedding.documents' })}
</span>
</h3>
<span className="m-l-10 font-size-12">
{' '}
{tokenResult?.total_tokens && (
<span style={{ color: 'var(--ant-orange)' }}>
{intl.formatMessage({ id: 'playground.tokenusage' })}:{' '}
{tokenResult?.total_tokens}
</span>
)}
</span>
<div className="flex gap-10">
<Button size="middle" onClick={handleAddText}>
<PlusOutlined />
Add Text
{intl.formatMessage({ id: 'playground.embedding.addtext' })}
</Button>
<Button
icon={<ClearOutlined />}
@@ -437,33 +451,25 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
</div>
<div className="docs-wrapper">
<InputList
sortIndex={sortIndexMap}
ref={inputListRef}
textList={textList}
showLabel={false}
sortable={false}
height={46}
onChange={handleTextListChange}
onSort={handleOnSort}
extra={renderPercent}
></InputList>
</div>
</div>
<div>
{messageList.length ? (
<div className="result-header flex-center">
<h3 className="font-size-14 m-b-0">Results</h3>
{tokenResult?.total_tokens && (
<span style={{ color: 'var(--ant-orange)' }}>
{intl.formatMessage({ id: 'playground.tokenusage' })}:{' '}
{tokenResult?.total_tokens}
</span>
)}
</div>
) : null}
</div>
<div></div>
<div
className="message-list-wrap"
style={{ paddingInline: 0, paddingTop: 0 }}
>
<>
<div className="content">
{/*<RerankMessage dataList={messageList} />*/}
{loading && (
<Spin size="small">
<div style={{ height: '46px' }}></div>
@@ -481,7 +487,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
ref={paramsRef}
>
<div className="box">
<RerankerParams
<DynamicParams
setParams={setParams}
params={parameters}
paramsConfig={paramsConfig}
@@ -494,9 +500,11 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
<ViewRerankCode
open={show}
documentList={[...textList, ...fileList]
.map((item) => item.text)
.filter((text) => text)}
payload={{
documents: [...textList, ...fileList]
.map((item) => item.text)
.filter((text) => text)
}}
parameters={{
...parameters,
query: contentRef.current
+105 -25
View File
@@ -29,8 +29,8 @@ import '../style/ground-left.less';
import '../style/speech-to-text.less';
import '../style/system-message-wrap.less';
import AudioInput from './audio-input';
import DynamicParams from './dynamic-params';
import MessageContent from './multiple-chat/message-content';
import RerankerParams from './reranker-params';
import ViewCodeModal from './view-code-modal';
interface MessageProps {
@@ -70,6 +70,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
analyser: null
});
const [isRecording, setIsRecording] = useState(false);
const [recordEnd, setRecordEnd] = useState(false);
const { initialize, updateScrollerPosition } = useOverlayScroller();
const { initialize: innitializeParams } = useOverlayScroller();
@@ -251,6 +252,9 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
duration: data.duration
};
});
setTimeout(() => {
setRecordEnd(true);
}, 200);
},
[]
);
@@ -264,6 +268,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const res = await readAudioFile(data.file.originFileObj);
console.log('res=======', res);
setAudioData(res);
setRecordEnd(true);
},
[]
);
@@ -281,6 +286,25 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
setAudioData(null);
}, []);
const handleOnGenerate = useCallback(() => {
setMessageList([
{
content: 'Generating text content...',
title: '',
role: '',
uid: messageId.current
}
]);
setRecordEnd(false);
setIsRecording(false);
}, []);
const handleOnDiscard = useCallback(() => {
setRecordEnd(false);
setAudioData(null);
setIsRecording(false);
}, []);
const renderAniamtion = () => {
if (!audioPermissionOn) {
return null;
@@ -334,32 +358,62 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="ground-left-footer" style={{ flex: 1 }}>
<div className="speech-to-text">
<div className="speech-box">
<Tooltip title="Upload an audio file">
<UploadAudio
type="default"
accept=".mp3,.mp4,.wav"
onChange={handleUploadChange}
></UploadAudio>
</Tooltip>
<AudioInput
type="default"
voiceActivity={true}
onAudioData={handleOnAudioData}
onAudioPermission={handleOnAudioPermission}
onAnalyse={handleOnAnalyse}
onRecord={handleOnRecord}
></AudioInput>
{isRecording ? (
<>
<AudioInput
type="default"
voiceActivity={true}
onAudioData={handleOnAudioData}
onAudioPermission={handleOnAudioPermission}
onAnalyse={handleOnAnalyse}
onRecord={handleOnRecord}
></AudioInput>
</>
) : (
<>
{/* <Tooltip title="discard">
<Button
onClick={handleOnDiscard}
icon={<DeleteRowOutlined />}
shape="circle"
></Button>
</Tooltip>
<Tooltip title="generate text content">
<Button
type="primary"
onClick={handleOnGenerate}
shape="circle"
icon={<ThunderboltOutlined></ThunderboltOutlined>}
></Button>
</Tooltip> */}
<Tooltip title="Upload an audio file">
<UploadAudio
type="default"
accept=".mp3,.mp4,.wav"
onChange={handleUploadChange}
></UploadAudio>
</Tooltip>
<AudioInput
type="default"
voiceActivity={true}
onAudioData={handleOnAudioData}
onAudioPermission={handleOnAudioPermission}
onAnalyse={handleOnAnalyse}
onRecord={handleOnRecord}
></AudioInput>
</>
)}
</div>
{audioData ? (
<div className="flex-between flex-center">
<div style={{ flex: 1 }}>
<div className="flex-between flex-center justify-center">
<div style={{ width: 600 }}>
<AudioPlayer
url={audioData.url}
name={audioData.name}
duration={audioData.duration}
></AudioPlayer>
<div
{/* <div
style={{
paddingRight: 5,
display: 'flex',
@@ -376,7 +430,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
Generata Text Content
</Button>
</Tooltip>
</div>
</div> */}
</div>
</div>
) : (
@@ -420,15 +474,31 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
</div>
)}
</div>
<div className="message-list-wrap" ref={scroller}>
<>
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between'
}}
>
<div
className="message-list-wrap"
ref={scroller}
style={{
borderTop: messageList.length
? '1px solid var(--ant-color-split)'
: '1px solid var(--ant-color-split)'
}}
>
<div className="content" style={{ height: '100%' }}>
<>
<MessageContent
actions={['copy']}
actions={[]}
messageList={messageList[0] ? [messageList[0]] : []}
setMessageList={setMessageList}
editable={false}
showTitle={false}
loading={loading}
/>
{loading && (
@@ -438,7 +508,17 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
)}
</>
</div>
</>
</div>
<div style={{ padding: '16px 32px', textAlign: 'right' }}>
<Tooltip title="generate text content">
<Button
disabled={!audioData}
type="primary"
onClick={handleOnGenerate}
icon={<ThunderboltOutlined></ThunderboltOutlined>}
></Button>
</Tooltip>
</div>
</div>
</div>
<div
@@ -448,7 +528,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
ref={paramsRef}
>
<div className="box">
<RerankerParams
<DynamicParams
setParams={setParams}
paramsConfig={paramsConfig}
initialValues={initialValues}
+30 -22
View File
@@ -1,6 +1,8 @@
import IconFont from '@/components/icon-font';
import SpeechContent from '@/components/speech-content';
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data';
import { ThunderboltOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Spin } from 'antd';
import classNames from 'classnames';
@@ -21,9 +23,9 @@ import { TTSParamsConfig as paramsConfig } from '../config/params-config';
import { MessageItem } from '../config/types';
import '../style/ground-left.less';
import '../style/system-message-wrap.less';
import RerankerParams from './dynamic-params';
import MessageInput from './message-input';
import ReferenceParams from './reference-params';
import RerankerParams from './reranker-params';
import ViewCodeModal from './view-code-modal';
interface MessageProps {
@@ -59,6 +61,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const paramsRef = useRef<any>(null);
const messageListLengthCache = useRef<number>(0);
const checkvalueRef = useRef<any>(true);
const [currentPrompt, setCurrentPrompt] = useState<string>('');
const { initialize, updateScrollerPosition } = useOverlayScroller();
const { initialize: innitializeParams } = useOverlayScroller();
@@ -232,20 +235,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const handleSelectModel = () => {};
const handlePresetPrompt = (list: { role: string; content: string }[]) => {
const sysMsg = list.filter((item) => item.role === 'system');
const userMsg = list
.filter((item) => item.role === 'user')
.map((item) => {
setMessageId();
return {
...item,
uid: messageId.current
};
});
setSystemMessage(sysMsg[0]?.content || '');
setMessageList(userMsg);
};
const handleOnCheckChange = (e: any) => {
console.log('handleOnCheckChange', e);
checkvalueRef.current = e.target.checked;
@@ -279,16 +268,35 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="ground-left-wrapper">
<div className="ground-left">
<div className="message-list-wrap" ref={scroller}>
<>
<div
style={{
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<div className="content">
<SpeechContent dataList={messageList} loading={loading} />
{messageList.length ? (
<SpeechContent dataList={messageList} loading={loading} />
) : (
<div className="flex-column font-size-14 flex-center gap-20">
<span>
<IconFont
type="icon-audio "
className="font-size-32 text-secondary"
></IconFont>
</span>
<span>Generated speech will appear here</span>
</div>
)}
{loading && (
<Spin size="small">
<div style={{ height: '46px' }}></div>
</Spin>
)}
</div>
</>
</div>
</div>
{tokenResult && (
<div style={{ height: 40 }}>
@@ -297,8 +305,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
)}
<div className="ground-left-footer">
<MessageInput
scope="chat"
actions={['clear', 'check']}
actions={['check']}
checkLabel={intl.formatMessage({
id: 'playground.toolbar.autoplay'
})}
@@ -311,7 +318,8 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
handleAbortFetch={handleStopConversation}
clearAll={handleClear}
setModelSelections={handleSelectModel}
presetPrompt={handlePresetPrompt}
shouldResetMessage={false}
submitIcon={<ThunderboltOutlined></ThunderboltOutlined>}
modelList={modelList}
/>
</div>
@@ -337,7 +345,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
<ViewCodeModal
open={show}
payLoad={{
messages: viewCodeMessage
prompt: currentPrompt
}}
parameters={parameters}
onCancel={handleCloseViewCode}
+173 -6
View File
@@ -2,26 +2,182 @@ import RowTextarea from '@/components/seal-form/row-textarea';
import { DeleteOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Tooltip } from 'antd';
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import _ from 'lodash';
import React, {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef
} from 'react';
import '../style/input-list.less';
interface InputListProps {
ref?: any;
height?: number;
extra?: (data: any) => React.ReactNode;
showLabel?: boolean;
sortIndex?: number[];
textList: {
text: string;
uid: number | string;
name: string;
}[];
sortable?: boolean;
onChange?: (
textList: { text: string; uid: number | string; name: string }[]
) => void;
onSort?: (
textList: { text: string; uid: number | string; name: string }[]
) => void;
}
const InputList: React.FC<InputListProps> = forwardRef(
({ textList, onChange, extra }, ref) => {
(
{
textList,
showLabel = true,
sortIndex = [],
sortable,
height,
onSort,
onChange,
extra
},
ref
) => {
const intl = useIntl();
const messageId = useRef(0);
const containerRef = useRef<any>(null);
const childListRef = useRef<any[]>([]);
const getContainerChildList = () => {
childListRef.current = Array.from(containerRef.current?.children || []);
};
const getOffsetUsingBoundingClientRect = useCallback(
(element: HTMLElement, targetElement: HTMLElement) => {
const currentRect = element.getBoundingClientRect();
const targetRect = targetElement.getBoundingClientRect();
return {
x: targetRect.left - currentRect.left,
y: targetRect.top - currentRect.top
};
},
[]
);
// move item from fromIndex to toIndex
const moveItem = useCallback((child: any, toIndex: number) => {
const container = containerRef.current;
if (!container) return;
const children = Array.from(container.children);
if (toIndex >= children.length) {
if (container.firstChild) {
container.insertBefore(child, container.firstChild);
} else {
container.appendChild(child);
}
} else {
container.insertBefore(child, children[toIndex]);
}
}, []);
const moveElement = (arr: any[], fromIndex: number, toIndex: number) => {
if (
fromIndex === toIndex ||
fromIndex < 0 ||
toIndex < 0 ||
fromIndex >= arr.length ||
toIndex > arr.length
) {
return arr;
}
const element = arr.splice(fromIndex, 1)[0];
arr.splice(toIndex > fromIndex ? toIndex - 1 : toIndex, 0, element);
return arr;
};
const sort = useCallback(() => {
if (!sortable) return;
getContainerChildList();
const container = containerRef.current;
if (!container) return;
const newOrder = [...textList];
const offsets = sortIndex.map((fromIndex, toIndex) => {
const currentElement = childListRef.current[fromIndex];
const targetElement = childListRef.current[toIndex];
if (!currentElement || !targetElement) return null;
const offset = getOffsetUsingBoundingClientRect(
currentElement,
targetElement
);
return {
element: currentElement,
offset: {
x: offset.x,
y: offset.y
},
fromIndex,
toIndex
};
});
const moveSequentially = async () => {
for (const [index, data] of offsets.entries()) {
if (!data) continue;
const { element, offset, fromIndex, toIndex } = data;
console.log('sort+++++++', {
textList,
element,
offset,
fromIndex,
toIndex
});
await new Promise((resolve) => {
element.style.opacity = 0.5;
element.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
element.style.transition = 'transform 0.8s,opacity 0.8s';
element.addEventListener(
'transitionend',
() => {
moveItem(element, toIndex);
moveElement(newOrder, fromIndex, toIndex);
getContainerChildList();
element.style.opacity = 1;
element.style.transform = '';
console.log('sort++++++++++end');
resolve(null);
},
{ once: true }
);
});
}
onSort?.(newOrder);
};
moveSequentially();
}, [
sortable,
sortIndex,
textList,
onSort,
getContainerChildList,
getOffsetUsingBoundingClientRect
]);
const setMessageId = () => {
messageId.current = messageId.current + 1;
@@ -54,6 +210,14 @@ const InputList: React.FC<InputListProps> = forwardRef(
dataList[index].text = value;
onChange?.(dataList);
};
const debounceSort = _.debounce(sort, 100);
useEffect(() => {
if (sortIndex?.length) {
console.log('sort++++2+++');
debounceSort();
}
}, [sortIndex]);
useImperativeHandle(ref, () => ({
handleAdd,
@@ -62,15 +226,18 @@ const InputList: React.FC<InputListProps> = forwardRef(
}));
return (
<div className="input-list">
<div className="input-list" ref={containerRef}>
{textList.map((text, index) => {
return (
<div key={text.uid} className="input-item">
<div key={text.uid} className="input-item" data-uid={text.uid}>
<div className="input-wrap">
<RowTextarea
label={`${index + 1}.`}
height={height}
label={showLabel ? `${index + 1}` : null}
value={text.text}
placeholder="Input your text"
placeholder={intl.formatMessage({
id: 'playground.embedding.inputyourtext'
})}
onChange={(e) => handleTextChange(e.target.value, text)}
></RowTextarea>
</div>
@@ -477,6 +477,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
<ThumbImg
dataList={message.imgs || []}
onDelete={handleDeleteImg}
editable={true}
></ThumbImg>
<div className="input-box">
{actions.includes('paste') ? (
@@ -15,6 +15,7 @@ interface MessageItemProps {
data: MessageItem;
editable?: boolean;
loading?: boolean;
showTitle?: boolean;
actions?: MessageItemAction[];
updateMessage?: (message: MessageItem) => void;
onDelete?: () => void;
@@ -26,6 +27,7 @@ const ContentItem: React.FC<MessageItemProps> = ({
loading,
data,
editable,
showTitle = true,
actions = ['upload', 'delete', 'copy']
}) => {
const intl = useIntl();
@@ -169,10 +171,16 @@ const ContentItem: React.FC<MessageItemProps> = ({
return (
<div className="content-item">
<div className="content-item-role">
<div className="role">
{data.title ?? intl.formatMessage({ id: `playground.${data.role}` })}
</div>
<div
className="content-item-role"
style={{ display: !showTitle && !actions.length ? 'none' : 'flex' }}
>
{showTitle && (
<div className="role">
{data.title ??
intl.formatMessage({ id: `playground.${data.role}` })}
</div>
)}
<div className="actions">
{actions.includes('upload') && data.role === Roles.User && (
<UploadImg handleUpdateImgList={handleUpdateImgList}></UploadImg>
@@ -7,6 +7,7 @@ interface MessageContentProps {
loading?: boolean;
actions?: MessageItemAction[];
editable?: boolean;
showTitle?: boolean;
messageList: MessageItem[];
setMessageList?: (list: any) => void;
}
@@ -15,6 +16,7 @@ const MessageContent: React.FC<MessageContentProps> = ({
setMessageList,
messageList,
editable,
showTitle = true,
actions = ['upload', 'delete', 'copy']
}) => {
const updateMessage = (index: number, message: MessageItem) => {
@@ -39,6 +41,7 @@ const MessageContent: React.FC<MessageContentProps> = ({
data={item}
editable={editable}
actions={actions}
showTitle={showTitle}
onDelete={() => handleDelete(index)}
updateMessage={(data) => updateMessage(index, data)}
/>
@@ -24,7 +24,7 @@ import React, {
} from 'react';
import 'simplebar-react/dist/simplebar.min.css';
import { CHAT_API } from '../../apis';
import { Roles, generateMessages } from '../../config';
import { OpenAIViewCode, Roles, generateMessages } from '../../config';
import CompareContext from '../../config/compare-context';
import { MessageItem, ModelSelectionItem } from '../../config/types';
import '../../style/model-item.less';
@@ -452,7 +452,6 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
<div className="content" ref={modelScrollRef}>
<div>
<MessageContent
spans={spans}
messageList={messageList}
setMessageList={setMessageList}
editable={true}
@@ -465,6 +464,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
</div>
</div>
<ViewCodeModal
{...OpenAIViewCode.chat}
open={show}
payLoad={{
messages: viewCodeMessage
@@ -1,252 +0,0 @@
import FieldWrapper from '@/components/seal-form/field-wrapper';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { INPUT_WIDTH } from '@/constants';
import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form, InputNumber, Slider, Tooltip } from 'antd';
import _ from 'lodash';
import { memo, useCallback, useEffect, useId, useMemo } from 'react';
import { ParamsSchema } from '../config/types';
import CustomLabelStyles from '../style/custom-label.less';
type ParamsSettingsFormProps = {
top_n?: number;
model?: string;
};
type ParamsSettingsProps = {
selectedModel?: string;
showModelSelector?: boolean;
params?: Record<string, any>;
model?: string;
modelList: Global.BaseOption<string>[];
onValuesChange?: (changeValues: any, value: Record<string, any>) => void;
setParams: (params: any) => void;
globalParams?: Record<string, any>;
paramsConfig?: ParamsSchema[];
initialValues?: Record<string, any>;
};
const ParamsSettings: React.FC<ParamsSettingsProps> = ({
setParams,
onValuesChange,
selectedModel,
globalParams,
initialValues,
paramsConfig,
modelList,
params,
showModelSelector = true
}) => {
const intl = useIntl();
const [form] = Form.useForm();
const formId = useId();
useEffect(() => {
if (showModelSelector) {
form.setFieldsValue({
model: selectedModel || _.get(modelList, '[0].value'),
...initialValues
});
setParams({
model: selectedModel || _.get(modelList, '[0].value'),
...initialValues
});
} else {
form.setFieldsValue({
model: selectedModel || '',
...initialValues
});
setParams({
model: selectedModel || '',
...initialValues
});
}
}, [modelList, showModelSelector, selectedModel, initialValues]);
const handleOnFinish = (values: any) => {
console.log('handleOnFinish', values);
};
const handleOnFinishFailed = (errorInfo: any) => {
console.log('handleOnFinishFailed', errorInfo);
};
const handleValuesChange = useCallback(
(changedValues: any, allValues: any) => {
setParams?.(allValues);
onValuesChange?.(changedValues, allValues);
},
[onValuesChange, setParams]
);
const handleFieldValueChange = useCallback(
(val: any, field: string) => {
const values = form.getFieldsValue();
form.setFieldsValue({
...values,
[field]: val
});
setParams({
...values,
[field]: val
});
onValuesChange?.(
{ [field]: val },
{
...values,
[field]: val
}
);
},
[form, setParams, onValuesChange]
);
useEffect(() => {
form.setFieldsValue(globalParams);
}, [globalParams]);
const renderLabel = useCallback(
(args: { field: string; label: string; description: string }) => {
return (
<span
className={CustomLabelStyles.label}
style={{ width: INPUT_WIDTH.mini }}
>
<span className="text">
{args.description ? (
<Tooltip title={args.description}>
<span> {args.label}</span>
<span className="m-l-5">
<InfoCircleOutlined />
</span>
</Tooltip>
) : (
<span>{args.label}</span>
)}
</span>
<InputNumber
className="label-val"
variant="outlined"
size="small"
value={form.getFieldValue(args.field)}
controls={false}
onChange={(val) => handleFieldValueChange(val, args.field)}
></InputNumber>
</span>
);
},
[form, handleFieldValueChange]
);
const renderFields = useMemo(() => {
console.log('paramsConfig:', paramsConfig);
if (!paramsConfig?.length) {
return null;
}
return paramsConfig.map((item: ParamsSchema) => {
if (item.type === 'InputNumber') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealInput.Number
{...item.attrs}
style={{ width: '100%' }}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealInput.Number>
</Form.Item>
);
}
if (item.type === 'Select') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<SealSelect
{...item.attrs}
options={item.options}
label={
item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text
}
></SealSelect>
</Form.Item>
);
}
if (item.type === 'Slider') {
return (
<Form.Item name={item.name} rules={item.rules} key={item.name}>
<FieldWrapper
label={renderLabel({
field: item.name,
label: item.label.isLocalized
? intl.formatMessage({ id: item.label.text })
: item.label.text,
description: item.description?.isLocalized
? intl.formatMessage({ id: item.description?.text })
: item.description?.text || ''
})}
style={{ padding: '20px 2px 0' }}
variant="borderless"
>
<Slider
{...item.attrs}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}
value={form.getFieldValue(item.name) || undefined}
onChange={(val) => handleFieldValueChange(val, item.name)}
></Slider>
</FieldWrapper>
</Form.Item>
);
}
return null;
});
}, [paramsConfig, params]);
return (
<Form
name={formId}
form={form}
onValuesChange={handleValuesChange}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
>
<div>
{
<>
<h3 className="m-b-20 m-l-10 font-size-14 line-24">
<span>{intl.formatMessage({ id: 'playground.parameters' })}</span>
</h3>
<Form.Item<ParamsSettingsFormProps>
name="model"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.select'
},
{ name: intl.formatMessage({ id: 'playground.model' }) }
)
}
]}
>
<SealSelect
showSearch={true}
options={modelList}
label={intl.formatMessage({ id: 'playground.model' })}
></SealSelect>
</Form.Item>
</>
}
{renderFields}
</div>
</Form>
);
};
export default memo(ParamsSettings);
+119 -39
View File
@@ -1,6 +1,6 @@
import AutoImage from '@/components/auto-image';
import { CloseCircleOutlined } from '@ant-design/icons';
import { Spin } from 'antd';
import { Col, Progress, Row } from 'antd';
import _ from 'lodash';
import React, { useCallback } from 'react';
import '../style/thumb-img.less';
@@ -11,7 +11,20 @@ const ThumbImg: React.FC<{
onDelete?: (uid: number) => void;
loading?: boolean;
style?: React.CSSProperties;
}> = ({ dataList, editable, onDelete, loading, style }) => {
responseable?: boolean;
gutter?: number | number[] | object;
justify?: any;
autoSize?: boolean;
}> = ({
dataList,
editable,
responseable,
gutter,
onDelete,
loading,
autoSize,
style
}) => {
const handleOnDelete = useCallback(
(uid: number) => {
onDelete?.(uid);
@@ -23,52 +36,119 @@ const ThumbImg: React.FC<{
return null;
}
const renderImageItem = (item: any) => {
return (
<span
key={item.uid}
className="thumb-img"
style={{
width: item.width,
height: item.height
}}
>
<>
{loading ? (
<span
className="progress-wrap"
style={{
width: '100%',
height: '100%',
display: 'flex',
border: '1px solid var(--ant-color-split)',
borderRadius: 'var(--border-radius-base)',
justifyContent: 'center',
alignItems: 'center',
padding: '10px',
overflow: 'hidden'
}}
>
<Progress percent={item.progress} type="circle" />
</span>
) : (
<span className="img">
<AutoImage
autoSize={autoSize}
src={item.dataUrl}
width={item.width || 100}
height={item.height || 100}
/>
</span>
)}
</>
{editable && (
<span className="del" onClick={() => handleOnDelete(item.uid)}>
<CloseCircleOutlined />
</span>
)}
</span>
);
};
return (
<>
{
<div className="thumb-list-wrap" style={{ ...style }}>
{_.map(dataList, (item: any) => {
return (
<span
key={item.uid}
className="thumb-img"
{responseable ? (
<>
<Row
gutter={gutter || []}
className="flex-center"
style={{
width: item.width || 100,
height: item.height || 100
height: dataList.length > 2 ? '50%' : '100%',
flex: 'none',
width: '100%',
justifyContent:
dataList.length === 1 ? 'center' : 'flex-start'
}}
>
<span className="img">
{loading ? (
<span
style={{
width: '100%',
height: '100%',
display: 'flex',
border: '1px solid var(--ant-color-split)',
borderRadius: 'var(--border-radius-base)'
}}
{_.map(_.slice(dataList, 0, 2), (item: any, index: string) => {
return (
<Col
span={item.span}
key={`1-${index}`}
className="flex-center justify-center"
style={{ height: '100%', width: '100%' }}
>
<Spin
{renderImageItem(item)}
</Col>
);
})}
</Row>
{dataList.length > 2 && (
<Row
gutter={gutter || []}
style={{
height: '50%',
flex: 'none',
width: '100%',
justifyContent:
dataList.length === 1 ? 'center' : 'flex-start'
}}
className="flex-center"
>
{_.map(_.slice(dataList, 2), (item: any, index: string) => {
return (
<Col
span={item.span}
key={`2-${index}`}
className="flex-center justify-center"
style={{ width: '100%', height: '100%' }}
></Spin>
</span>
) : (
<AutoImage src={item.dataUrl} height={item.height || 100} />
)}
</span>
{editable && (
<span
className="del"
onClick={() => handleOnDelete(item.uid)}
>
<CloseCircleOutlined />
</span>
)}
</span>
);
})}
style={{ height: '100%', width: '100%' }}
>
{renderImageItem(item)}
</Col>
);
})}
</Row>
)}
</>
) : (
<>
{_.map(dataList, (item: any) => {
return renderImageItem(item);
})}
</>
)}
</div>
}
</>
@@ -12,8 +12,10 @@ type ViewModalProps = {
payLoad: Record<string, any>;
parameters: any;
title: string;
api: string;
clientType: string;
logcommand?: string;
open: boolean;
apiType?: string;
onCancel: () => void;
};
@@ -33,21 +35,18 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const {
title,
open,
api,
clientType,
logcommand,
onCancel,
messageList,
payLoad,
parameters = {},
apiType = 'chat'
parameters = {}
} = props || {};
const intl = useIntl();
const [lang, setLang] = useState(langMap.shell);
const BaseURL = `${window.location.origin}/v1-openai`;
const ClientType = apiType === 'chat' ? 'chat.completions' : 'embeddings';
const api = apiType === 'chat' ? 'chat/completions' : 'embeddings';
const logcommand =
apiType === 'chat' ? 'choices[0].message.content' : 'data[0].embedding';
const formatPyParams = (params: any) => {
return _.keys(params).reduce((acc: string, key: string) => {
@@ -63,8 +62,13 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
};
const codeValue = useMemo(() => {
const consoleLog = logcommand
? `console.log(response.${logcommand});\n`
: '';
const printLog = logcommand ? `print(response.${logcommand})` : '';
if (lang === langMap.shell) {
const messages = messageList;
const code = `curl ${window.location.origin}/v1-openai/${api} \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify(
{
...parameters,
@@ -76,7 +80,6 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
return code;
}
if (lang === langMap.javascript) {
const messages = messageList;
const code = `const OpenAI = require("openai");\n\nconst openai = new OpenAI({\n "apiKey": "YOUR_GPUSTACK_API_KEY",\n "baseURL": "${BaseURL}"\n});\n\nasync function main(){\n const params = ${JSON.stringify(
{
...parameters,
@@ -84,7 +87,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
},
null,
4
)};\nconst response = await openai.${ClientType}.create(params);\n console.log(response.${logcommand});\n}\nmain();`;
)};\nconst response = await openai.${clientType}.create(params);\n ${consoleLog}}\nmain();`;
return code;
}
@@ -102,12 +105,12 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
},
''
);
const params = apiType === 'chat' ? formatPyParams(payLoad) : '';
const code = `from openai import OpenAI\n\nclient = OpenAI(\n base_url="${BaseURL}", \n api_key="YOUR_GPUSTACK_API_KEY"\n)\n\nresponse = client.${ClientType}.create(\n${formattedParams} ${params})\nprint(response.${logcommand})`;
const params = formatPyParams(payLoad);
const code = `from openai import OpenAI\n\nclient = OpenAI(\n base_url="${BaseURL}", \n api_key="YOUR_GPUSTACK_API_KEY"\n)\n\nresponse = client.${clientType}.create(\n${formattedParams}${params})\n${printLog}`;
return code;
}
return '';
}, [lang, payLoad, parameters]);
}, [lang, payLoad, parameters, api, clientType, logcommand]);
const handleOnChangeLang = (value: string) => {
setLang(value);
@@ -6,11 +6,11 @@ import { Button, Modal } from 'antd';
import React, { useEffect, useState } from 'react';
type ViewModalProps = {
documentList: string[];
parameters: any;
title: string;
open: boolean;
apiType?: string;
payload?: Record<string, any>;
onCancel: () => void;
};
@@ -27,13 +27,7 @@ const langOptions = [
];
const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const {
title,
open,
onCancel,
documentList = [],
parameters = {}
} = props || {};
const { title, open, onCancel, payload, parameters = {} } = props || {};
const intl = useIntl();
const [codeValue, setCodeValue] = useState('');
@@ -46,7 +40,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const code = `curl ${window.location.origin}/v1/rerank \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify(
{
...parameters,
documents: documentList
...payload
},
null,
2
@@ -55,7 +49,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
} else if (lang === langMap.javascript) {
const data = {
...parameters,
documents: documentList
...payload
};
const headers = {
'Content-type': 'application/json',
@@ -66,7 +60,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
} else if (lang === langMap.python) {
const data = {
...parameters,
documents: documentList
...payload
};
const headers = {
'Content-type': 'application/json',
@@ -88,7 +82,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
useEffect(() => {
generateCode();
}, [lang, parameters, documentList]);
}, [lang, parameters, payload]);
return (
<>
+17
View File
@@ -103,3 +103,20 @@ export const generateMessages = (messageList: Omit<MessageItem, 'uid'>[]) => {
return result;
};
export const OpenAIViewCode = {
chat: {
api: 'chat/completions',
clientType: 'chat.completions',
logcommand: 'choices[0].message.content'
},
embeddings: {
api: 'embeddings',
clientType: 'embeddings',
logcommand: 'data[0].embedding'
},
images: {
api: 'images/generations',
clientType: 'images.generate'
}
};
+88 -50
View File
@@ -13,8 +13,8 @@ export const TTSParamsConfig: ParamsSchema[] = [
{ label: 'Shimmer', value: 'Shimmer' }
],
label: {
text: 'Voice',
isLocalized: false
text: 'playground.params.voice',
isLocalized: true
},
rules: [
{
@@ -35,8 +35,8 @@ export const TTSParamsConfig: ParamsSchema[] = [
{ label: 'pcm', value: 'pcm' }
],
label: {
text: 'Response Format',
isLocalized: false
text: 'playground.params.format',
isLocalized: true
},
rules: [
{
@@ -55,8 +55,8 @@ export const TTSParamsConfig: ParamsSchema[] = [
{ label: '4x', value: 4 }
],
label: {
text: 'Speed',
isLocalized: false
text: 'playground.params.speed',
isLocalized: true
},
rules: [
{
@@ -64,6 +64,25 @@ export const TTSParamsConfig: ParamsSchema[] = [
}
]
}
// {
// type: 'TextArea',
// name: 'prompt',
// label: {
// text: 'Prompt',
// isLocalized: false
// },
// attrs: {
// autoSize: {
// minRows: 2,
// maxRows: 3
// }
// },
// rules: [
// {
// required: false
// }
// ]
// }
];
export const RealtimeParamsConfig: ParamsSchema[] = [
@@ -79,13 +98,12 @@ export const RealtimeParamsConfig: ParamsSchema[] = [
{ label: 'Deutsch', value: 'de' }
],
label: {
text: 'Language',
isLocalized: false
text: 'playground.params.language',
isLocalized: true
},
rules: [
{
required: true,
message: 'Language is required'
required: true
}
]
}
@@ -96,12 +114,12 @@ export const ImageParamsConfig: ParamsSchema[] = [
type: 'InputNumber',
name: 'n',
label: {
text: 'Counts',
isLocalized: false
text: 'playground.params.counts',
isLocalized: true
},
attrs: {
min: 1,
max: 10
max: 4
},
rules: [
{
@@ -117,45 +135,12 @@ export const ImageParamsConfig: ParamsSchema[] = [
{ label: '512x512', value: '512x512' },
{ label: '1024x1024', value: '1024x1024' },
{ label: '1792x1024', value: '1792x1024' },
{ label: '1024x1792', value: '1024x1792' }
{ label: '1024x1792', value: '1024x1792' },
{ label: 'playground.params.custom', value: 'custom', locale: true }
],
label: {
text: 'Size',
isLocalized: false
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'quality',
options: [
{ label: 'standard', value: 'standard' },
{ label: 'hd', value: 'hd' }
],
label: {
text: 'Quality',
isLocalized: false
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'style',
options: [
{ label: 'vivid', value: 'vivid' },
{ label: 'natural', value: 'natural' }
],
label: {
text: 'Style',
isLocalized: false
text: 'playground.params.size',
isLocalized: true
},
rules: [
{
@@ -163,4 +148,57 @@ export const ImageParamsConfig: ParamsSchema[] = [
}
]
}
// {
// type: 'Select',
// name: 'quality',
// options: [
// { label: 'standard', value: 'standard' },
// { label: 'hd', value: 'hd' }
// ],
// label: {
// text: 'playground.params.quality',
// isLocalized: true
// },
// rules: [
// {
// required: false
// }
// ]
// },
// {
// type: 'Select',
// name: 'style',
// options: [
// { label: 'vivid', value: 'vivid' },
// { label: 'natural', value: 'natural' }
// ],
// label: {
// text: 'playground.params.style',
// isLocalized: true
// },
// rules: [
// {
// required: false
// }
// ]
// }
// {
// type: 'TextArea',
// name: 'prompt',
// label: {
// text: 'Prompt',
// isLocalized: false
// },
// attrs: {
// autoSize: {
// minRows: 2,
// maxRows: 3
// }
// },
// rules: [
// {
// required: false
// }
// ]
// }
];
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+1
View File
@@ -20,6 +20,7 @@ type SchemaType =
| 'Textarea'
| 'Select'
| 'Slider'
| 'TextArea'
| 'Checkbox';
export interface ParamsSchema {
+2 -1
View File
@@ -30,7 +30,8 @@ const PlaygroundEmbedding: React.FC = () => {
const getModelListByEmbedding = async () => {
try {
const params = {
embedding_only: true
embedding_only: true,
reranker: false
};
const res = await queryModelsList(params);
const list = _.map(res.data || [], (item: any) => {
@@ -54,6 +54,19 @@
.content {
flex: 1;
}
.hold-wrapper {
height: 100%;
}
.hold-text-icon {
display: flex;
padding: 4px;
justify-content: center;
align-items: center;
background-color: var(--ant-color-fill-tertiary);
border-radius: var(--border-radius-base);
}
}
}
@@ -63,4 +76,19 @@
flex-direction: column;
justify-content: center;
}
.embed-chart {
position: relative;
.drag-handler {
position: absolute;
padding: 0;
height: fit-content;
top: -10px;
font-size: var(--font-size-middle);
left: 50%;
transform: translateX(-50%);
background-color: transparent;
}
}
}
@@ -14,6 +14,7 @@
transition: background-color 0.3s ease;
border-radius: var(--border-radius-base);
border: 1px solid var(--ant-color-border);
background-color: #fff;
.btn-group {
display: none;
+37
View File
@@ -43,3 +43,40 @@
}
}
}
.rank-wrapper {
position: absolute;
left: 14px;
right: 14px;
bottom: 0;
line-height: 12px;
display: flex;
justify-content: space-between;
overflow: hidden;
align-items: flex-end;
.percent-wrapper {
flex: 1;
padding: 2px 0;
}
.rank-tag {
padding: 0 4px;
margin-left: 10px;
background-color: transparent;
opacity: 0.7;
width: 160px;
display: flex;
justify-content: space-between;
.ant-tag {
margin: 0;
justify-content: center;
display: flex;
height: 16px;
border-radius: 4px;
align-items: center;
transform: scale(0.85);
}
}
}
+9 -2
View File
@@ -1,14 +1,21 @@
.thumb-img {
position: relative;
display: flex;
max-width: 100%;
max-height: 100%;
justify-content: center;
.img {
display: flex;
width: 100%;
height: 100%;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
overflow: hidden;
border-radius: var(--border-radius-base);
cursor: pointer;
justify-content: center;
align-items: center;
}
.del {