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
+1 -1
View File
@@ -14,7 +14,7 @@ const isProduction = env === 'production';
const t = Date.now();
export default defineConfig({
proxy: {
...proxy()
...proxy('http://csvhs44p420c73bdb41g-80.agent.damodel.com')
},
history: {
type: 'hash'
+1 -1
View File
@@ -54,11 +54,11 @@
"query-string": "^9.0.0",
"rc-resize-observer": "^1.4.0",
"rc-virtual-list": "^3.14.8",
"re-resizable": "^6.10.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hotkeys-hook": "^4.5.0",
"simplebar-react": "^3.2.6",
"umap-js": "^1.4.0",
"umi-presets-pro": "^2.0.3",
"wavesurfer.js": "^7.8.8",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
+13 -20
View File
@@ -125,6 +125,9 @@ dependencies:
rc-virtual-list:
specifier: ^3.14.8
version: 3.14.8(react-dom@18.2.0)(react@18.2.0)
re-resizable:
specifier: ^6.10.1
version: 6.10.1(react-dom@18.2.0)(react@18.2.0)
react:
specifier: ^18.2.0
version: 18.2.0
@@ -137,9 +140,6 @@ dependencies:
simplebar-react:
specifier: ^3.2.6
version: 3.2.6(react@18.2.0)
umap-js:
specifier: ^1.4.0
version: 1.4.0
umi-presets-pro:
specifier: ^2.0.3
version: 2.0.3(@babel/core@7.25.7)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.21.6)(dva@2.5.0-beta.2)(rc-field-form@2.4.0)(react-dom@18.2.0)(react@18.2.0)(umi@4.3.24)
@@ -11643,10 +11643,6 @@ packages:
is-decimal: 1.0.4
dev: false
/is-any-array@0.1.1:
resolution: {integrity: sha512-qTiELO+kpTKqPgxPYbshMERlzaFu29JDnpB8s3bjg+JkxBpw29/qqSaOdKv2pCdaG92rLGeG/zG2GauX58hfoA==, tarball: https://registry.npmjs.org/is-any-array/-/is-any-array-0.1.1.tgz}
dev: false
/is-any-array@2.0.1:
resolution: {integrity: sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==, tarball: https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz}
dev: false
@@ -12983,13 +12979,6 @@ packages:
resolution: {integrity: sha512-7GO+Xk382d+HhVgjebaQnJ4Tcm8+uaL9Mc76UdxhBAU0aFWs6JDjINZLnrlZ0KDiIi9Erc52bD26QOvYsG7Nnw==, tarball: https://registry.npmjs.org/ml-dataset-iris/-/ml-dataset-iris-1.2.1.tgz}
dev: false
/ml-levenberg-marquardt@2.1.1:
resolution: {integrity: sha512-2+HwUqew4qFFFYujYlQtmFUrxCB4iJAPqnUYro3P831wj70eJZcANwcRaIMGUVaH9NDKzfYuA4N5u67KExmaRA==, tarball: https://registry.npmjs.org/ml-levenberg-marquardt/-/ml-levenberg-marquardt-2.1.1.tgz}
dependencies:
is-any-array: 0.1.1
ml-matrix: 6.12.0
dev: false
/ml-matrix@6.12.0:
resolution: {integrity: sha512-AGfR+pWaC0GmzjUnB6BfwhndPEUGz0i7QUYdqNuw1zhTov/vSRJ9pP2hs6BoGpaSbtXgrKjZz2zjD1M0xuur6A==, tarball: https://registry.npmjs.org/ml-matrix/-/ml-matrix-6.12.0.tgz}
dependencies:
@@ -16147,6 +16136,16 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/re-resizable@6.10.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-m33nSWRH57UZLmep5M/LatkZ2NRqimVD/bOOpvymw5Zf33+eTSEixsUugscOZzAtK0/nx+OSuOf8VbKJx/4ptw==, tarball: https://registry.npmjs.org/re-resizable/-/re-resizable-6.10.1.tgz}
peerDependencies:
react: ^16.13.1 || ^17.0.0 || ^18.0.0
react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-dom@18.1.0(react@18.1.0):
resolution: {integrity: sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-18.1.0.tgz}
peerDependencies:
@@ -18462,12 +18461,6 @@ packages:
resolution: {integrity: sha512-fYmIy7fKTSFAhG3fuPlubeGaMoAd6r0rSnfEsO5nEY55i26KSLt9EH7PLQiiqPUhNqYIJvSkTy1oArIcXAbPbA==, tarball: https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.38.tgz}
dev: false
/umap-js@1.4.0:
resolution: {integrity: sha512-xxpviF9wUO6Nxrx+C58SoDgea+h2PnVaRPKDelWv0HotmY6BeWeh0kAPJoumfqUkzUvowGsYfMbnsWI0b9do+A==, tarball: https://registry.npmjs.org/umap-js/-/umap-js-1.4.0.tgz}
dependencies:
ml-levenberg-marquardt: 2.1.1
dev: false
/umi-presets-pro@2.0.3(@babel/core@7.25.7)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.21.6)(dva@2.5.0-beta.2)(rc-field-form@2.4.0)(react-dom@18.2.0)(react@18.2.0)(umi@4.3.24):
resolution: {integrity: sha512-sHKynw/fi7UeUftzTRPRsrV5GHV4BWvWYhyvwkg8s+shmt0ROPW/52y4gxBziEvetxl8yWMKUCWk50OUDOcrww==, tarball: https://registry.npmjs.org/umi-presets-pro/-/umi-presets-pro-2.0.3.tgz}
dependencies:
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

+14
View File
@@ -89,6 +89,16 @@
justify-content: center;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
// align-items: center
.flex-center {
display: flex;
@@ -170,6 +180,10 @@
font-size: 24px;
}
.font-size-32 {
font-size: 32px;
}
.m-b-0 {
margin-bottom: 0;
}
+39
View File
@@ -0,0 +1,39 @@
import { WarningOutlined } from '@ant-design/icons';
import { Typography } from 'antd';
import React from 'react';
interface AlertInfoProps {
type: 'danger' | 'warning';
message: string;
rows?: number;
icon?: React.ReactNode;
}
const AlertInfo: React.FC<AlertInfoProps> = (props) => {
const { message, type, rows = 1 } = props;
if (!message) {
return null;
}
return (
<Typography.Paragraph
type={type}
ellipsis={{
rows: rows,
tooltip: message
}}
style={{
textAlign: 'center',
padding: '2px 5px',
borderRadius: 'var(--border-radius-base)',
margin: 0,
backgroundColor: 'var(--ant-color-error-bg)'
}}
>
<WarningOutlined className="m-r-8" />
{message}
</Typography.Paragraph>
);
};
export default React.memo(AlertInfo);
+2 -1
View File
@@ -61,7 +61,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
setPlayOn(false);
setAudioState((prestate: any) => {
return {
currentTime: 0,
currentTime: audioRef.current?.ended ? 0 : prestate.currentTime,
duration: prestate.duration
};
});
@@ -87,6 +87,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const handleLoadedMetadata = useCallback(
(data: any) => {
console.log('loadmetadata++++++++');
const duration = Math.ceil(audioRef.current?.duration || 0);
setAudioState({
currentTime: 0,
+27 -6
View File
@@ -1,3 +1,4 @@
import fallbackImg from '@/assets/images/img_fallback.png';
import {
DownloadOutlined,
EyeOutlined,
@@ -13,9 +14,16 @@ import { round } from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import './index.less';
const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
const { height = 100, ...rest } = props;
const [width, setWidth] = useState(0);
const AutoImage: React.FC<
ImageProps & {
height: number | string;
width?: number | string;
autoSize?: boolean;
}
> = (props) => {
const { height = 100, width: w, autoSize, ...rest } = props;
const [width, setWidth] = useState(w || 0);
const [isError, setIsError] = useState(false);
const getImgRatio = useCallback((url: string): Promise<{ ratio: number }> => {
return new Promise((resolve) => {
@@ -31,8 +39,15 @@ const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
}, []);
const handleOnLoad = useCallback(async () => {
if (autoSize) {
return;
}
const { ratio } = await getImgRatio(props.src || '');
setWidth(height * ratio);
if (typeof height === 'number') {
setWidth(height * ratio);
} else {
throw new Error('Height must be a number');
}
}, [getImgRatio, height, props.src]);
const onDownload = () => {
@@ -47,6 +62,10 @@ const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
link.remove();
};
const handleOnError = () => {
setIsError(true);
};
useEffect(() => {
handleOnLoad();
}, [handleOnLoad]);
@@ -54,8 +73,10 @@ const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
return (
<AntImage
{...rest}
height={height}
width={width}
height={isError ? 'auto' : height}
width={isError ? '100%' : width}
onError={handleOnError}
fallback={fallbackImg}
preview={{
mask: <EyeOutlined />,
toolbarRender: (
+2
View File
@@ -12,6 +12,7 @@ import type {
TooltipComponentOption
} from 'echarts/components';
import {
DataZoomComponent,
DatasetComponent,
GridComponent,
LegendComponent,
@@ -44,6 +45,7 @@ echarts.use([
GridComponent,
DatasetComponent,
TransformComponent,
DataZoomComponent,
BarChart,
LineChart,
ScatterChart,
+38 -32
View File
@@ -17,35 +17,47 @@ const options: any = {
borderRadius: 4
},
xAxis: {
min: -1,
max: 1,
scale: false,
slient: true,
splitNumber: 15,
splitLine: {
lineStyle: {
color: 'rgba(5,5,5,0.06)'
color: '#F2F2F2'
}
},
axisLine: {
show: false
show: false,
lineStyle: {
color: '#F2F2F2'
}
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
boundaryGap: [0.05, 0.05]
},
yAxis: {
min: -1,
max: 1,
scale: false,
slient: true,
splitNumber: 10,
boundaryGap: [0.05, 0.05],
splitLine: {
lineStyle: {
color: 'rgba(5,5,5,0.06)'
color: '#F2F2F2'
}
},
axisLine: {
show: false
show: false,
lineStyle: {
color: '#F2F2F2'
}
},
axisTick: {
show: false
@@ -54,6 +66,7 @@ const options: any = {
show: false
}
},
symbol: 'roundRect',
label: {
show: true,
@@ -67,16 +80,7 @@ const options: any = {
};
const Scatter: React.FC<ChartProps> = (props) => {
const {
seriesData,
xAxisData,
height,
width,
showEmpty,
labelFormatter,
legendData,
title
} = props;
const { seriesData, xAxisData, height, width, showEmpty, title } = props;
const chart = useRef<any>(null);
@@ -118,18 +122,21 @@ const Scatter: React.FC<ChartProps> = (props) => {
[]
);
const dataOptions = useMemo((): any => {
if (!seriesData.length) {
options.xAxis.min = 0;
options.xAxis.max = 1;
options.yAxis.min = 0;
options.yAxis.max = 1;
} else {
options.xAxis.min = null;
options.xAxis.max = null;
options.yAxis.min = null;
options.yAxis.max = null;
const renderNameInTooltip = useCallback((dataList: any[]) => {
if (!dataList.length || dataList.length < 2) {
return null;
}
const renderText = (item: any) => {
return `<span class="tooltip-item-name">
<span style="display:flex;justify-content:center;align-items: center;color:#fff;
margin-right:0;border-radius:4px;width:14px;
height:14px;background-color:${item?.itemStyle?.color};"
>${item.name}</span>
</span>`;
};
return renderText;
}, []);
const dataOptions = useMemo((): any => {
const seriseDataList = seriesData.map((item: any, index: number) => {
return {
...item,
@@ -147,15 +154,11 @@ const Scatter: React.FC<ChartProps> = (props) => {
formatter(params: any, callback?: (val: any) => any) {
const dataList = findOverlappingPoints(seriseDataList, params.data);
let result = '';
const renderText: any = renderNameInTooltip(dataList);
dataList.forEach((item: any) => {
result += `
<span class="tooltip-item" style="justify-content: flex-start;">
<span class="tooltip-item-name">
<span style="display:flex;justify-content:center;align-items: center;color:#fff;
margin-right:0;border-radius:4px;width:14px;
height:14px;background-color:${item.itemStyle?.color};"
>${item.name}</span>
</span>
${renderText ? renderText(item) : ''}
<span class="tooltip-value">${item.text}</span>
</span>`;
});
@@ -168,6 +171,9 @@ const Scatter: React.FC<ChartProps> = (props) => {
},
series: {
type: 'scatter',
labelLayout: {
hideOverlap: true
},
data: seriseDataList
}
};
@@ -9,6 +9,7 @@ interface CodeViewerProps {
ignoreIllegals?: boolean;
copyable?: boolean;
height?: string | number;
style?: React.CSSProperties;
}
const DarkViewer: React.FC<CodeViewerProps> = (props) => {
const {
@@ -22,6 +23,7 @@ const DarkViewer: React.FC<CodeViewerProps> = (props) => {
return (
<CodeViewer
style={props.style}
height={height}
code={code}
lang={lang}
@@ -9,6 +9,7 @@ interface CodeViewerProps {
ignoreIllegals?: boolean;
copyable?: boolean;
height?: string | number;
style?: React.CSSProperties;
}
const LightViewer: React.FC<CodeViewerProps> = (props) => {
const {
@@ -17,11 +18,13 @@ const LightViewer: React.FC<CodeViewerProps> = (props) => {
autodetect,
ignoreIllegals,
copyable,
style,
height = 'auto'
} = props || {};
return (
<CodeViewer
style={style}
height={height}
code={code}
lang={lang}
@@ -12,6 +12,7 @@ interface CodeViewerProps {
copyable?: boolean;
height?: string | number;
theme?: 'light' | 'dark';
style?: React.CSSProperties;
}
const CodeViewer: React.FC<CodeViewerProps> = (props) => {
const {
@@ -20,7 +21,8 @@ const CodeViewer: React.FC<CodeViewerProps> = (props) => {
autodetect = true,
ignoreIllegals = true,
copyable = true,
height = 'auto'
height = 'auto',
style
} = props || {};
const highlightedCode = useMemo(() => {
@@ -69,7 +71,8 @@ const CodeViewer: React.FC<CodeViewerProps> = (props) => {
}
)}
style={{
height: height
height: height,
...style
}}
>
<code
+4
View File
@@ -9,8 +9,10 @@ const HighlightCode: React.FC<{
copyable?: boolean;
theme?: 'light' | 'dark';
height?: string | number;
style?: React.CSSProperties;
}> = (props) => {
const {
style,
code,
lang = 'bash',
copyable = true,
@@ -26,9 +28,11 @@ const HighlightCode: React.FC<{
code={code}
copyable={copyable}
height={height}
style={style}
/>
) : (
<CodeViewerLight
style={style}
lang={lang}
code={code}
copyable={copyable}
@@ -13,6 +13,10 @@
border-radius: @borderRadius;
background-color: var(--color-white-1);
&.seal-input-number {
padding-right: 0;
}
&.borderless {
border: none;
box-shadow: none;
@@ -97,6 +101,12 @@
padding-block: 20px 0;
max-width: 100%;
&:hover {
:global(.ant-input-number-handler-wrap) {
width: 34px !important;
}
}
&.no-wrapper-style {
padding-block: 0;
}
@@ -201,6 +211,7 @@
height: @inputheight !important;
}
// ==================== input ====================
:global(.ant-input),
:global(.ant-input-password) {
// width: 100%;
@@ -221,6 +232,7 @@
}
:global(.ant-input-number) {
position: static;
display: flex;
align-items: center;
border: none;
@@ -228,6 +240,15 @@
padding: 0;
height: @inputheight !important;
background-color: @bgColor;
&:hover .ant-input-number-handler-wrap,
&-focused .ant-input-number-handler-wrap {
width: 34px !important;
}
&:hover {
cursor: text;
}
}
:global(.ant-input-outlined) {
@@ -293,13 +314,7 @@
}
:global(.ant-input-number-handler-wrap) {
top: -20px;
top: 0;
height: @wrapheight - 2px;
}
// :global(.ant-input-prefix) {
// position: absolute;
// top: 1px;
// left: -4px;
// }
}
@@ -68,6 +68,7 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
description={description}
disabled={props.disabled}
onClick={handleClickWrapper}
className="seal-input-number"
>
<InputNumber
{...rest}
+5 -4
View File
@@ -10,11 +10,12 @@ interface SystemMessageProps {
value: string;
placeholder?: string;
label?: React.ReactNode;
height?: number;
onChange: (e: any) => void;
}
const RowTextarea: React.FC<SystemMessageProps> = (props) => {
const { value, onChange, style, label, placeholder } = props;
const { value, onChange, style, label, placeholder, height = 46 } = props;
const intl = useIntl();
const rowTextAreaRef = React.useRef<any>(null);
const [autoSize, setAutoSize] = useState<{
@@ -64,7 +65,7 @@ const RowTextarea: React.FC<SystemMessageProps> = (props) => {
style={{ display: autoSize.focus ? 'block' : 'none' }}
className="textarea-wrapper"
>
<span className="textarea-label">{label}</span>
{label && <span className="textarea-label">{label}</span>}
<Input.TextArea
className="custome-scrollbar"
ref={rowTextAreaRef}
@@ -87,8 +88,8 @@ const RowTextarea: React.FC<SystemMessageProps> = (props) => {
}
{!autoSize.focus && (
<div className="content-wrap" onClick={handleFocus}>
<div className="content">
<span className="title">{label}</span>
<div className="content" style={{ height: height }}>
{label && <span className="title">{label}</span>}
{value || (
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
{placeholder}
+19 -1
View File
@@ -1,7 +1,9 @@
import { isNotEmptyValue } from '@/utils/index';
import { useIntl } from '@umijs/max';
import type { SelectProps } from 'antd';
import { Form, Select } from 'antd';
import { useEffect, useRef, useState } from 'react';
import { cloneDeep } from 'lodash';
import { useEffect, useMemo, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import { SealFormItemProps } from './types';
@@ -12,9 +14,11 @@ const SealSelect: React.FC<SelectProps & SealFormItemProps> = (props) => {
children,
required,
description,
options,
isInFormItems = true,
...rest
} = props;
const intl = useIntl();
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
let status = '';
@@ -23,6 +27,19 @@ const SealSelect: React.FC<SelectProps & SealFormItemProps> = (props) => {
status = statusData?.status || '';
}
const _options = useMemo(() => {
if (!options?.length) {
return [];
}
const list = cloneDeep(options);
return list.map((item: any) => {
if (item.locale) {
item.label = intl.formatMessage({ id: item.label as string });
}
return item;
});
}, [options, intl]);
useEffect(() => {
if (isNotEmptyValue(props.value)) {
setIsFocus(true);
@@ -70,6 +87,7 @@ const SealSelect: React.FC<SelectProps & SealFormItemProps> = (props) => {
<Select
{...rest}
ref={inputRef}
options={_options}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onChange={handleChange}
@@ -2,7 +2,7 @@
position: relative;
&.focus {
padding-top: 9px;
// padding-top: 9px;
}
.content-wrap {
@@ -15,7 +15,7 @@
&:hover {
.clear-btn {
display: block;
display: flex;
}
}
}
@@ -29,7 +29,8 @@
display: none;
position: absolute;
right: 6px;
top: 6px;
top: 50%;
transform: translateY(-50%);
}
.textarea-label {
@@ -41,8 +42,8 @@
.content {
flex: 1;
width: 100px;
height: 40px;
line-height: 24px;
height: 46px;
line-height: 30px;
padding: 8px 14px;
padding-right: 4px;
text-overflow: ellipsis;
+2 -2
View File
@@ -9,11 +9,11 @@ interface SpeechContentProps {
const SpeechContent: React.FC<SpeechContentProps> = (props) => {
console.log('SpeechContent', props);
return (
<div>
<>
{props.dataList.map((item) => (
<SpeechItem key={item.uid} {...item} />
))}
</div>
</>
);
};
+2
View File
@@ -24,11 +24,13 @@ declare namespace Global {
interface BaseListItem<T> {
key: string;
locale?: boolean;
value: T;
}
interface BaseOption<T> {
label: string;
locale?: boolean;
value: T;
}
+22 -1
View File
@@ -48,5 +48,26 @@ export default {
'playground.compare.applytoall': 'Apply to all models',
'playground.model.noavailable': 'No available models',
'playground.model.noavailable.tips':
'Please deploy a model first, and it should not be an embedding-only model.'
'Please deploy a model first, and it should not be an embedding-only model.',
'playground.params.counts': 'Counts',
'playground.params.quality': 'Quality',
'playground.params.style': 'Style',
'playground.params.size': 'Size',
'playground.params.voice': 'Voice',
'playground.params.format': 'Format',
'playground.params.speed': 'Speed',
'playground.params.language': 'Language',
'playground.params.width': 'Width',
'playground.params.height': 'Height',
'playground.params.custom': 'Custom',
'playground.params.empty.tips': 'Generated images will appear here',
'playground.embedding.documents': 'Documents',
'playground.embedding.addtext': 'Add Text',
'playground.embedding.inputyourtext': 'Input your text',
'playground.embedding.output': 'Output',
'playground.embedding.chart': 'Chart',
'playground.rerank.query': 'Query',
'playground.rerank.rank': 'Rank',
'playground.rerank.score': 'Score',
'playground.rerank.query.holder': 'Input your query'
};
+22 -1
View File
@@ -48,5 +48,26 @@ export default {
'playground.compare.applytoall': '应用到所有模型',
'playground.model.noavailable': '无可用模型',
'playground.model.noavailable.tips':
'请先部署模型,且不是 Embedding Only 的模型'
'请先部署模型,且不是 Embedding Only 的模型',
'playground.params.counts': '数量',
'playground.params.quality': '质量',
'playground.params.style': '风格',
'playground.params.size': '尺寸',
'playground.params.voice': '声音',
'playground.params.format': '格式',
'playground.params.speed': '播放速度',
'playground.params.language': '语言',
'playground.params.width': '宽度',
'playground.params.height': '高度',
'playground.params.custom': '自定义',
'playground.params.empty.tips': '生成的图片将出现在这里',
'playground.embedding.documents': '文档',
'playground.embedding.addtext': '添加文本',
'playground.embedding.inputyourtext': '输入你的文本',
'playground.embedding.output': '输出',
'playground.embedding.chart': '图表',
'playground.rerank.query': '查询',
'playground.rerank.rank': '排序',
'playground.rerank.score': '分数',
'playground.rerank.query.holder': '输入查询'
};
+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 {
+25 -112
View File
@@ -1,5 +1,3 @@
import _ from 'lodash';
export const isNotEmptyValue = (value: any) => {
if (Array.isArray(value)) {
return value.length > 0;
@@ -32,116 +30,6 @@ export const convertFileSize = (sizeInBytes: number, prec?: number) => {
}
};
export const generateRandomArray = (config?: {
min: number;
max: number;
length: number;
offset: number;
}) => {
const { min, max, length, offset } = config || {
min: 10,
max: 80,
length: 10,
offset: 10
};
const data = [];
let prevValue = Math.floor(Math.random() * (max - min + 1)) + min;
for (let i = 0; i < length; i++) {
let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // Fluctuation range [-10, 10]
newValue = Math.max(min, Math.min(max, newValue)); // Ensure within [10, 100]
data.push(newValue);
prevValue = newValue;
}
return data;
};
export const generateFluctuatingData2 = ({
total = 100,
noiseLevel = 10,
max = 50,
min = 5
}) => {
const x = [];
const y = [];
for (let i = 0; i < total; i++) {
// Generate a basic linear trend using a sine function
const phaseShift = Math.random() * 2 * Math.PI;
const trend =
max * Math.sin((3 * Math.PI * i + phaseShift) / total) + max / 2;
// Generate noise
const noise = (Math.random() * 2 - 1) * noiseLevel;
// Add trend and noise
const value = trend + noise;
x.push(i);
y.push(Math.max(min, value));
}
return y;
};
export const generateFluctuatingData = ({
total = 50,
trendType = 'linear',
max = 1,
f = 1,
phase = 0,
min = 0
}) => {
/**
* Generate a set of data for a line chart with a natural and aesthetically pleasing trend.
*
* Parameters:
* total (number): Number of data points to generate
* trendType (string): Type of data trend, options are 'linear', 'sine', 'exponential'
* max (number): Fluctuation amplitude
* f (number): Fluctuation frequency
* phase (number): Fluctuation phase
* min (number): Minimum value of the data
*
* Returns:
* x (number[]): x-axis data
* y (number[]): y-axis data
*/
const x = Array.from({ length: total }, (_, i) => (i * 10) / (total - 1));
let y;
switch (trendType) {
case 'linear':
y = x.map(
(val) => val * (Math.random() * 2 - 1) * 3 + Math.random() * 8 - 4
);
break;
case 'sine':
y = x.map(
(val) =>
max * Math.sin(2 * Math.PI * f * val + phase) + Math.random() * 2 - 1
);
break;
case 'exponential':
y = x.map(
(val) => Math.exp(val * Math.random() * 0.2) + Math.random() * 4 - 2
);
break;
default:
throw new Error(
'Invalid trendType parameter. Please choose "linear", "sine", or "exponential".'
);
}
// Adjust the data to the minimum value
const minY = Math.min(...y);
y = y.map((val) => _.round(val - minY + min, 2));
return y;
};
export const platformCall = () => {
const platform = navigator.userAgent;
const isMac = () => {
@@ -190,6 +78,7 @@ export const formatNumber = (num: number) => {
};
export function loadLanguageConfig(language: string) {
// @ts-ignore
const requireContext = require.context(`./${language}`, false, /\.ts$/);
const languageConfig: Record<string, string> = {};
@@ -214,3 +103,27 @@ export function readBlob(blob: Blob): Promise<string> {
reader.readAsText(blob, 'utf-8');
});
}
export const cosineSimilarity = (vec1: number[], vec2: number[]) => {
if (vec1.length !== vec2.length) {
throw new Error('both vectors must have the same length');
}
const dotProduct = vec1.reduce(
(sum, value, index) => sum + value * vec2[index],
0
);
const magnitudeA = Math.sqrt(
vec1.reduce((sum, value) => sum + value * value, 0)
);
const magnitudeB = Math.sqrt(
vec2.reduce((sum, value) => sum + value * value, 0)
);
if (magnitudeA === 0 || magnitudeB === 0) {
throw new Error('both vectors must have a length greater than 0');
}
return dotProduct / (magnitudeA * magnitudeB);
};
+1
View File
@@ -20,5 +20,6 @@ declare module 'has-ansi';
declare module 'terminal-kit';
declare module 'monaco-vim';
declare module '@orcid/bibtex-parse-js';
declare module 'sortablejs';
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;