chore: images create
This commit is contained in:
+1
-1
@@ -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
@@ -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"
|
||||
|
||||
Generated
+13
-20
@@ -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 |
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
|
||||
@@ -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: (
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Vendored
+2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -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': '输入查询'
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 |
@@ -20,6 +20,7 @@ type SchemaType =
|
||||
| 'Textarea'
|
||||
| 'Select'
|
||||
| 'Slider'
|
||||
| 'TextArea'
|
||||
| 'Checkbox';
|
||||
|
||||
export interface ParamsSchema {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
Vendored
+1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user