|
|
@@ -1,9 +1,11 @@
|
|
|
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
|
-import { UserContext } from '../context/User';
|
|
|
-import { API, getUserIdFromLocalStorage, showError } from '../helpers';
|
|
|
-import { Card, Chat, Input, Layout, Select, Slider, TextArea, Typography } from '@douyinfe/semi-ui';
|
|
|
+import { UserContext } from '../../context/User/index.js';
|
|
|
+import { API, getUserIdFromLocalStorage, showError } from '../../helpers/index.js';
|
|
|
+import { Card, Chat, Input, Layout, Select, Slider, TextArea, Typography, Button } from '@douyinfe/semi-ui';
|
|
|
import { SSE } from 'sse';
|
|
|
+import { IconSetting } from '@douyinfe/semi-icons';
|
|
|
+import { StyleContext } from '../../context/Style/index.js';
|
|
|
|
|
|
const defaultMessage = [
|
|
|
{
|
|
|
@@ -54,6 +56,8 @@ const Playground = () => {
|
|
|
const [message, setMessage] = useState(defaultMessage);
|
|
|
const [models, setModels] = useState([]);
|
|
|
const [groups, setGroups] = useState([]);
|
|
|
+ const [showSettings, setShowSettings] = useState(true);
|
|
|
+ const [styleState, styleDispatch] = useContext(StyleContext);
|
|
|
|
|
|
const handleInputChange = (name, value) => {
|
|
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
|
|
@@ -257,95 +261,141 @@ const Playground = () => {
|
|
|
})
|
|
|
}, []);
|
|
|
|
|
|
+ const SettingsToggle = () => {
|
|
|
+ if (!styleState.isMobile) return null;
|
|
|
+ return (
|
|
|
+ <Button
|
|
|
+ icon={<IconSetting />}
|
|
|
+ style={{
|
|
|
+ position: 'absolute',
|
|
|
+ left: showSettings ? -10 : -20,
|
|
|
+ top: '50%',
|
|
|
+ transform: 'translateY(-50%)',
|
|
|
+ zIndex: 1000,
|
|
|
+ width: 40,
|
|
|
+ height: 40,
|
|
|
+ borderRadius: '0 20px 20px 0',
|
|
|
+ padding: 0,
|
|
|
+ boxShadow: '2px 0 8px rgba(0, 0, 0, 0.15)',
|
|
|
+ }}
|
|
|
+ onClick={() => setShowSettings(!showSettings)}
|
|
|
+ theme="solid"
|
|
|
+ type="primary"
|
|
|
+ />
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ function CustomInputRender(props) {
|
|
|
+ const { detailProps } = props;
|
|
|
+ const { clearContextNode, uploadNode, inputNode, sendNode, onClick } = detailProps;
|
|
|
+
|
|
|
+ return <div style={{margin: '8px 16px', display: 'flex', flexDirection:'row',
|
|
|
+ alignItems: 'flex-end', borderRadius: 16,padding: 10, border: '1px solid var(--semi-color-border)'}}
|
|
|
+ onClick={onClick}
|
|
|
+ >
|
|
|
+ {/*{uploadNode}*/}
|
|
|
+ {inputNode}
|
|
|
+ {sendNode}
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+
|
|
|
+ const renderInputArea = useCallback((props) => {
|
|
|
+ return (<CustomInputRender {...props} />)
|
|
|
+ }, []);
|
|
|
+
|
|
|
return (
|
|
|
<Layout style={{height: '100%'}}>
|
|
|
- <Layout.Sider>
|
|
|
- <Card style={commonOuterStyle}>
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text strong>分组:</Typography.Text>
|
|
|
- </div>
|
|
|
- <Select
|
|
|
- placeholder={'请选择分组'}
|
|
|
- name='group'
|
|
|
- required
|
|
|
- selection
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('group', value);
|
|
|
- }}
|
|
|
- value={inputs.group}
|
|
|
- autoComplete='new-password'
|
|
|
- optionList={groups}
|
|
|
- />
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text strong>模型:</Typography.Text>
|
|
|
- </div>
|
|
|
- <Select
|
|
|
- placeholder={'请选择模型'}
|
|
|
- name='model'
|
|
|
- required
|
|
|
- selection
|
|
|
- searchPosition='dropdown'
|
|
|
- filter
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('model', value);
|
|
|
- }}
|
|
|
- value={inputs.model}
|
|
|
- autoComplete='new-password'
|
|
|
- optionList={models}
|
|
|
- />
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text strong>Temperature:</Typography.Text>
|
|
|
- </div>
|
|
|
- <Slider
|
|
|
- step={0.1}
|
|
|
- min={0.1}
|
|
|
- max={1}
|
|
|
- value={inputs.temperature}
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('temperature', value);
|
|
|
- }}
|
|
|
- />
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text strong>MaxTokens:</Typography.Text>
|
|
|
- </div>
|
|
|
- <Input
|
|
|
- placeholder='MaxTokens'
|
|
|
- name='max_tokens'
|
|
|
- required
|
|
|
- autoComplete='new-password'
|
|
|
- defaultValue={0}
|
|
|
- value={inputs.max_tokens}
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('max_tokens', value);
|
|
|
- }}
|
|
|
- />
|
|
|
+ {(showSettings || !styleState.isMobile) && (
|
|
|
+ <Layout.Sider style={{ display: styleState.isMobile ? 'block' : 'initial' }}>
|
|
|
+ <Card style={commonOuterStyle}>
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Typography.Text strong>分组:</Typography.Text>
|
|
|
+ </div>
|
|
|
+ <Select
|
|
|
+ placeholder={'请选择分组'}
|
|
|
+ name='group'
|
|
|
+ required
|
|
|
+ selection
|
|
|
+ onChange={(value) => {
|
|
|
+ handleInputChange('group', value);
|
|
|
+ }}
|
|
|
+ value={inputs.group}
|
|
|
+ autoComplete='new-password'
|
|
|
+ optionList={groups}
|
|
|
+ />
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Typography.Text strong>模型:</Typography.Text>
|
|
|
+ </div>
|
|
|
+ <Select
|
|
|
+ placeholder={'请选择模型'}
|
|
|
+ name='model'
|
|
|
+ required
|
|
|
+ selection
|
|
|
+ searchPosition='dropdown'
|
|
|
+ filter
|
|
|
+ onChange={(value) => {
|
|
|
+ handleInputChange('model', value);
|
|
|
+ }}
|
|
|
+ value={inputs.model}
|
|
|
+ autoComplete='new-password'
|
|
|
+ optionList={models}
|
|
|
+ />
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Typography.Text strong>Temperature:</Typography.Text>
|
|
|
+ </div>
|
|
|
+ <Slider
|
|
|
+ step={0.1}
|
|
|
+ min={0.1}
|
|
|
+ max={1}
|
|
|
+ value={inputs.temperature}
|
|
|
+ onChange={(value) => {
|
|
|
+ handleInputChange('temperature', value);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Typography.Text strong>MaxTokens:</Typography.Text>
|
|
|
+ </div>
|
|
|
+ <Input
|
|
|
+ placeholder='MaxTokens'
|
|
|
+ name='max_tokens'
|
|
|
+ required
|
|
|
+ autoComplete='new-password'
|
|
|
+ defaultValue={0}
|
|
|
+ value={inputs.max_tokens}
|
|
|
+ onChange={(value) => {
|
|
|
+ handleInputChange('max_tokens', value);
|
|
|
+ }}
|
|
|
+ />
|
|
|
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text strong>System:</Typography.Text>
|
|
|
- </div>
|
|
|
- <TextArea
|
|
|
- placeholder='System Prompt'
|
|
|
- name='system'
|
|
|
- required
|
|
|
- autoComplete='new-password'
|
|
|
- autosize
|
|
|
- defaultValue={systemPrompt}
|
|
|
- // value={systemPrompt}
|
|
|
- onChange={(value) => {
|
|
|
- setSystemPrompt(value);
|
|
|
- }}
|
|
|
- />
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Typography.Text strong>System:</Typography.Text>
|
|
|
+ </div>
|
|
|
+ <TextArea
|
|
|
+ placeholder='System Prompt'
|
|
|
+ name='system'
|
|
|
+ required
|
|
|
+ autoComplete='new-password'
|
|
|
+ autosize
|
|
|
+ defaultValue={systemPrompt}
|
|
|
+ // value={systemPrompt}
|
|
|
+ onChange={(value) => {
|
|
|
+ setSystemPrompt(value);
|
|
|
+ }}
|
|
|
+ />
|
|
|
|
|
|
- </Card>
|
|
|
- </Layout.Sider>
|
|
|
+ </Card>
|
|
|
+ </Layout.Sider>
|
|
|
+ )}
|
|
|
<Layout.Content>
|
|
|
- <div style={{height: '100%'}}>
|
|
|
+ <div style={{height: '100%', position: 'relative'}}>
|
|
|
+ <SettingsToggle />
|
|
|
<Chat
|
|
|
chatBoxRenderConfig={{
|
|
|
renderChatBoxAction: () => {
|
|
|
return <div></div>
|
|
|
}
|
|
|
}}
|
|
|
+ renderInputArea={renderInputArea}
|
|
|
roleConfig={roleInfo}
|
|
|
style={commonOuterStyle}
|
|
|
chats={message}
|