mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-13 08:49:02 +08:00
feat: create a chat assistant and extract SimilaritySlider (#67)
* feat: extract SimilaritySlider * feat: create a chat assistant
This commit is contained in:
parent
a8294f2168
commit
8c4ec9955e
5
web/src/assets/svg/chat-app-cube.svg
Normal file
5
web/src/assets/svg/chat-app-cube.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M6.1875 12.1045L7.04673 12.5819C7.21217 12.6738 7.29489 12.7197 7.38249 12.7377C7.46002 12.7537 7.53998 12.7537 7.61751 12.7377C7.70511 12.7197 7.78783 12.6738 7.95327 12.5819L8.8125 12.1045M3.5625 10.6462L2.73007 10.1837C2.55535 10.0866 2.46798 10.0381 2.40437 9.96907C2.34809 9.908 2.3055 9.83562 2.27945 9.75677C2.25 9.66764 2.25 9.5677 2.25 9.36782V8.45867M2.25 5.542V4.63284C2.25 4.43297 2.25 4.33303 2.27945 4.2439C2.3055 4.16505 2.34809 4.09267 2.40437 4.0316C2.46798 3.96257 2.55535 3.91403 2.73007 3.81696L3.5625 3.3545M6.1875 1.89617L7.04673 1.41882C7.21217 1.32691 7.29489 1.28095 7.38249 1.26294C7.46002 1.24699 7.53998 1.24699 7.61751 1.26294C7.70511 1.28095 7.78783 1.32691 7.95327 1.41882L8.8125 1.89617M11.4375 3.3545L12.2699 3.81696C12.4447 3.91403 12.532 3.96257 12.5956 4.0316C12.6519 4.09266 12.6945 4.16505 12.7206 4.2439C12.75 4.33303 12.75 4.43297 12.75 4.63284V5.542M12.75 8.45867V9.36782C12.75 9.5677 12.75 9.66764 12.7206 9.75677C12.6945 9.83562 12.6519 9.908 12.5956 9.96907C12.532 10.0381 12.4447 10.0866 12.2699 10.1837L11.4375 10.6462M6.1875 6.27117L7.5 7.00033M7.5 7.00033L8.8125 6.27117M7.5 7.00033V8.45867M2.25 4.08367L3.5625 4.81283M11.4375 4.81283L12.75 4.08367M7.5 11.3753V12.8337"
|
||||||
|
stroke="#A5A3A9" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
29
web/src/components/similarity-slider/index.tsx
Normal file
29
web/src/components/similarity-slider/index.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Form, Slider } from 'antd';
|
||||||
|
|
||||||
|
type FieldType = {
|
||||||
|
similarity_threshold?: number;
|
||||||
|
vector_similarity_weight?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SimilaritySlider = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item<FieldType>
|
||||||
|
label="Similarity threshold"
|
||||||
|
name={'similarity_threshold'}
|
||||||
|
initialValue={0}
|
||||||
|
>
|
||||||
|
<Slider max={1} step={0.01} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item<FieldType>
|
||||||
|
label="Vector similarity weight"
|
||||||
|
name={'vector_similarity_weight'}
|
||||||
|
initialValue={0}
|
||||||
|
>
|
||||||
|
<Slider max={1} step={0.01} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SimilaritySlider;
|
@ -11,3 +11,40 @@ export enum RunningStatus {
|
|||||||
DONE = '3', // need to refresh
|
DONE = '3', // need to refresh
|
||||||
FAIL = '4', // need to refresh
|
FAIL = '4', // need to refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ModelVariableType {
|
||||||
|
Improvise = 'Improvise',
|
||||||
|
Precise = 'Precise',
|
||||||
|
Balance = 'Balance',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const settledModelVariableMap = {
|
||||||
|
[ModelVariableType.Improvise]: {
|
||||||
|
temperature: 0.9,
|
||||||
|
top_p: 0.9,
|
||||||
|
frequency_penalty: 0.2,
|
||||||
|
presence_penalty: 0.4,
|
||||||
|
max_tokens: 512,
|
||||||
|
},
|
||||||
|
[ModelVariableType.Precise]: {
|
||||||
|
temperature: 0.1,
|
||||||
|
top_p: 0.3,
|
||||||
|
frequency_penalty: 0.7,
|
||||||
|
presence_penalty: 0.4,
|
||||||
|
max_tokens: 215,
|
||||||
|
},
|
||||||
|
[ModelVariableType.Balance]: {
|
||||||
|
temperature: 0.5,
|
||||||
|
top_p: 0.5,
|
||||||
|
frequency_penalty: 0.7,
|
||||||
|
presence_penalty: 0.4,
|
||||||
|
max_tokens: 215,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum LlmModelType {
|
||||||
|
Embedding = 'embedding',
|
||||||
|
Chat = 'chat',
|
||||||
|
Image2text = 'image2text',
|
||||||
|
Speech2text = 'speech2text',
|
||||||
|
}
|
||||||
|
@ -124,3 +124,22 @@ export const useFetchKnowledgeBaseConfiguration = () => {
|
|||||||
fetchKnowledgeBaseConfiguration();
|
fetchKnowledgeBaseConfiguration();
|
||||||
}, [fetchKnowledgeBaseConfiguration]);
|
}, [fetchKnowledgeBaseConfiguration]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useFetchKnowledgeList = (): IKnowledge[] => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
|
||||||
|
const { data = [] } = knowledgeModel;
|
||||||
|
|
||||||
|
const fetchList = useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'knowledgeModel/getList',
|
||||||
|
});
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchList();
|
||||||
|
}, [fetchList]);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
39
web/src/hooks/llmHooks.ts
Normal file
39
web/src/hooks/llmHooks.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { LlmModelType } from '@/constants/knowledge';
|
||||||
|
import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
|
||||||
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
|
export const useFetchLlmList = (modelType: LlmModelType) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const fetchLlmList = useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'settingModel/llm_list',
|
||||||
|
payload: { model_type: modelType },
|
||||||
|
});
|
||||||
|
}, [dispatch, modelType]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchLlmList();
|
||||||
|
}, [fetchLlmList]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectLlmOptions = () => {
|
||||||
|
const llmInfo: IThirdOAIModelCollection = useSelector(
|
||||||
|
(state: any) => state.settingModel.llmInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
const embeddingModelOptions = useMemo(() => {
|
||||||
|
return Object.entries(llmInfo).map(([key, value]) => {
|
||||||
|
return {
|
||||||
|
label: key,
|
||||||
|
options: value.map((x) => ({
|
||||||
|
label: x.llm_name,
|
||||||
|
value: x.llm_name,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [llmInfo]);
|
||||||
|
|
||||||
|
return embeddingModelOptions;
|
||||||
|
};
|
47
web/src/interfaces/database/chat.ts
Normal file
47
web/src/interfaces/database/chat.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export interface PromptConfig {
|
||||||
|
empty_response: string;
|
||||||
|
parameters: Parameter[];
|
||||||
|
prologue: string;
|
||||||
|
system: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Parameter {
|
||||||
|
key: string;
|
||||||
|
optional: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LlmSetting {
|
||||||
|
Creative: Variable;
|
||||||
|
Custom: Variable;
|
||||||
|
Evenly: Variable;
|
||||||
|
Precise: Variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Variable {
|
||||||
|
frequency_penalty: number;
|
||||||
|
max_tokens: number;
|
||||||
|
presence_penalty: number;
|
||||||
|
temperature: number;
|
||||||
|
top_p: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDialog {
|
||||||
|
create_date: string;
|
||||||
|
create_time: number;
|
||||||
|
description: string;
|
||||||
|
icon: string;
|
||||||
|
id: string;
|
||||||
|
kb_ids: string[];
|
||||||
|
kb_names: string[];
|
||||||
|
language: string;
|
||||||
|
llm_id: string;
|
||||||
|
llm_setting: LlmSetting;
|
||||||
|
llm_setting_type: string;
|
||||||
|
name: string;
|
||||||
|
prompt_config: PromptConfig;
|
||||||
|
prompt_type: string;
|
||||||
|
status: string;
|
||||||
|
tenant_id: string;
|
||||||
|
update_date: string;
|
||||||
|
update_time: number;
|
||||||
|
}
|
@ -53,7 +53,7 @@ const RenameModal = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldValue('name', initialName);
|
form.setFieldValue('name', initialName);
|
||||||
}, [initialName, documentId]);
|
}, [initialName, documentId, form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -17,13 +17,14 @@ import {
|
|||||||
UploadFile,
|
UploadFile,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
|
import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||||
import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { LlmModelType } from '../../constant';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
@ -35,9 +36,6 @@ const Configuration = () => {
|
|||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
const knowledgeBaseId = useKnowledgeBaseId();
|
||||||
const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
|
const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
|
||||||
|
|
||||||
const llmInfo: IThirdOAIModelCollection = useSelector(
|
|
||||||
(state: any) => state.settingModel.llmInfo,
|
|
||||||
);
|
|
||||||
const knowledgeDetails: IKnowledge = useSelector(
|
const knowledgeDetails: IKnowledge = useSelector(
|
||||||
(state: any) => state.kSModel.knowledgeDetails,
|
(state: any) => state.kSModel.knowledgeDetails,
|
||||||
);
|
);
|
||||||
@ -51,17 +49,7 @@ const Configuration = () => {
|
|||||||
|
|
||||||
const parserList = useSelectParserList();
|
const parserList = useSelectParserList();
|
||||||
|
|
||||||
const embeddingModelOptions = useMemo(() => {
|
const embeddingModelOptions = useSelectLlmOptions();
|
||||||
return Object.entries(llmInfo).map(([key, value]) => {
|
|
||||||
return {
|
|
||||||
label: key,
|
|
||||||
options: value.map((x) => ({
|
|
||||||
label: x.llm_name,
|
|
||||||
value: x.llm_name,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [llmInfo]);
|
|
||||||
|
|
||||||
const onFinish = async (values: any) => {
|
const onFinish = async (values: any) => {
|
||||||
console.info(values);
|
console.info(values);
|
||||||
@ -86,13 +74,6 @@ const Configuration = () => {
|
|||||||
console.log('Failed:', errorInfo);
|
console.log('Failed:', errorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchLlmList = useCallback(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/llm_list',
|
|
||||||
payload: { model_type: 'embedding' },
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const avatar = knowledgeDetails.avatar;
|
const avatar = knowledgeDetails.avatar;
|
||||||
let fileList: UploadFile[] = [];
|
let fileList: UploadFile[] = [];
|
||||||
@ -115,9 +96,7 @@ const Configuration = () => {
|
|||||||
useFetchParserList();
|
useFetchParserList();
|
||||||
useFetchKnowledgeBaseConfiguration();
|
useFetchKnowledgeBaseConfiguration();
|
||||||
|
|
||||||
useEffect(() => {
|
useFetchLlmList(LlmModelType.Embedding);
|
||||||
fetchLlmList();
|
|
||||||
}, [fetchLlmList]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.configurationWrapper}>
|
<div className={styles.configurationWrapper}>
|
||||||
|
@ -16,14 +16,10 @@ const KnowledgeTesting = () => {
|
|||||||
const handleTesting = async () => {
|
const handleTesting = async () => {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
console.info(values);
|
console.info(values);
|
||||||
const similarity_threshold = values.similarity_threshold / 100;
|
|
||||||
const vector_similarity_weight = values.vector_similarity_weight / 100;
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'testingModel/testDocumentChunk',
|
type: 'testingModel/testDocumentChunk',
|
||||||
payload: {
|
payload: {
|
||||||
...values,
|
...values,
|
||||||
similarity_threshold,
|
|
||||||
vector_similarity_weight,
|
|
||||||
kb_id: knowledgeBaseId,
|
kb_id: knowledgeBaseId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import SimilaritySlider from '@/components/similarity-slider';
|
||||||
|
import { DeleteOutlined, HistoryOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
@ -6,22 +8,15 @@ import {
|
|||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
Slider,
|
Slider,
|
||||||
SliderSingleProps,
|
|
||||||
Space,
|
Space,
|
||||||
Tag,
|
Tag,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
|
|
||||||
import { DeleteOutlined, HistoryOutlined } from '@ant-design/icons';
|
|
||||||
import { FormInstance } from 'antd/lib';
|
import { FormInstance } from 'antd/lib';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const list = [1, 2, 3];
|
const list = [1, 2, 3];
|
||||||
|
|
||||||
const marks: SliderSingleProps['marks'] = {
|
|
||||||
0: '0',
|
|
||||||
100: '1',
|
|
||||||
};
|
|
||||||
|
|
||||||
type FieldType = {
|
type FieldType = {
|
||||||
similarity_threshold?: number;
|
similarity_threshold?: number;
|
||||||
vector_similarity_weight?: number;
|
vector_similarity_weight?: number;
|
||||||
@ -29,12 +24,6 @@ type FieldType = {
|
|||||||
question: string;
|
question: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatter = (value: number | undefined) => {
|
|
||||||
return typeof value === 'number' ? value / 100 : 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tooltip = { formatter };
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
form: FormInstance;
|
form: FormInstance;
|
||||||
handleTesting: () => Promise<any>;
|
handleTesting: () => Promise<any>;
|
||||||
@ -59,23 +48,12 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
form={form}
|
form={form}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
similarity_threshold: 20,
|
similarity_threshold: 0.2,
|
||||||
vector_similarity_weight: 30,
|
vector_similarity_weight: 0.3,
|
||||||
top_k: 1024,
|
top_k: 1024,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item<FieldType>
|
<SimilaritySlider></SimilaritySlider>
|
||||||
label="Similarity threshold"
|
|
||||||
name={'similarity_threshold'}
|
|
||||||
>
|
|
||||||
<Slider marks={marks} defaultValue={0} tooltip={tooltip} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="Vector similarity weight"
|
|
||||||
name={'vector_similarity_weight'}
|
|
||||||
>
|
|
||||||
<Slider marks={marks} defaultValue={0} tooltip={tooltip} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType> label="Top k" name={'top_k'}>
|
<Form.Item<FieldType> label="Top k" name={'top_k'}>
|
||||||
<Slider marks={{ 0: 0, 2048: 2048 }} defaultValue={0} max={2048} />
|
<Slider marks={{ 0: 0, 2048: 2048 }} defaultValue={0} max={2048} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
import { Form, Input } from 'antd';
|
import { Form, Input, Select } from 'antd';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ISegmentedContentProps } from './interface';
|
import { ISegmentedContentProps } from './interface';
|
||||||
|
|
||||||
|
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||||
|
const knowledgeList = useFetchKnowledgeList();
|
||||||
|
const knowledgeOptions = knowledgeList.map((x) => ({
|
||||||
|
label: x.name,
|
||||||
|
value: x.id,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={classNames({
|
className={classNames({
|
||||||
@ -19,15 +28,43 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="e.g. Resume Jarvis" />
|
<Input placeholder="e.g. Resume Jarvis" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name={'avatar'} label="Assistant avatar">
|
<Form.Item name={'icon'} label="Assistant avatar">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name={'keywords'} label="Keywords">
|
<Form.Item name={'language'} label="Language" initialValue={'Chinese'}>
|
||||||
<Input.TextArea autoSize={{ minRows: 3 }} />
|
<Select
|
||||||
|
options={[
|
||||||
|
{ value: 'Chinese', label: 'Chinese' },
|
||||||
|
{ value: 'English', label: 'English' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name={'opener'} label="Set an opener">
|
<Form.Item
|
||||||
|
name={['prompt_config', 'empty_response']}
|
||||||
|
label="Empty response"
|
||||||
|
>
|
||||||
|
<Input placeholder="" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name={['prompt_config', 'prologue']} label="Set an opener">
|
||||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
<Input.TextArea autoSize={{ minRows: 5 }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Select one context"
|
||||||
|
name="kb_ids"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please select!',
|
||||||
|
type: 'array',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions}
|
||||||
|
placeholder="Please select"
|
||||||
|
></Select>
|
||||||
|
</Form.Item>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
7
web/src/pages/chat/chat-configuration-modal/constants.ts
Normal file
7
web/src/pages/chat/chat-configuration-modal/constants.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export const variableEnabledFieldMap = {
|
||||||
|
temperatureEnabled: 'temperature',
|
||||||
|
topPEnabled: 'top_p',
|
||||||
|
presencePenaltyEnabled: 'presence_penalty',
|
||||||
|
frequencyPenaltyEnabled: 'frequency_penalty',
|
||||||
|
maxTokensEnabled: 'max_tokens',
|
||||||
|
};
|
@ -41,3 +41,10 @@
|
|||||||
width: 0;
|
width: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sliderInputNumber {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
.variableSlider {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@ -2,17 +2,20 @@ import { ReactComponent as ChatConfigurationAtom } from '@/assets/svg/chat-confi
|
|||||||
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
||||||
import { Divider, Flex, Form, Modal, Segmented } from 'antd';
|
import { Divider, Flex, Form, Modal, Segmented } from 'antd';
|
||||||
import { SegmentedValue } from 'antd/es/segmented';
|
import { SegmentedValue } from 'antd/es/segmented';
|
||||||
import { useState } from 'react';
|
import omit from 'lodash/omit';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
import AssistantSetting from './assistant-setting';
|
import AssistantSetting from './assistant-setting';
|
||||||
import ModelSetting from './model-setting';
|
import ModelSetting from './model-setting';
|
||||||
import PromptEngine from './prompt-engine';
|
import PromptEngine from './prompt-engine';
|
||||||
|
|
||||||
|
import { useSetDialog } from '../hooks';
|
||||||
|
import { variableEnabledFieldMap } from './constants';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
enum ConfigurationSegmented {
|
enum ConfigurationSegmented {
|
||||||
AssistantSetting = 'Assistant Setting',
|
AssistantSetting = 'Assistant Setting',
|
||||||
ModelSetting = 'Model Setting',
|
|
||||||
PromptEngine = 'Prompt Engine',
|
PromptEngine = 'Prompt Engine',
|
||||||
|
ModelSetting = 'Model Setting',
|
||||||
}
|
}
|
||||||
|
|
||||||
const segmentedMap = {
|
const segmentedMap = {
|
||||||
@ -45,10 +48,24 @@ const ChatConfigurationModal = ({
|
|||||||
const [value, setValue] = useState<ConfigurationSegmented>(
|
const [value, setValue] = useState<ConfigurationSegmented>(
|
||||||
ConfigurationSegmented.AssistantSetting,
|
ConfigurationSegmented.AssistantSetting,
|
||||||
);
|
);
|
||||||
|
const promptEngineRef = useRef(null);
|
||||||
|
|
||||||
|
const setDialog = useSetDialog();
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const x = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
console.info(x);
|
const nextValues: any = omit(values, Object.keys(variableEnabledFieldMap));
|
||||||
|
const finalValues = {
|
||||||
|
...nextValues,
|
||||||
|
prompt_config: {
|
||||||
|
...nextValues.prompt_config,
|
||||||
|
parameters: promptEngineRef.current,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.info(promptEngineRef.current);
|
||||||
|
console.info(nextValues);
|
||||||
|
console.info(finalValues);
|
||||||
|
setDialog(finalValues);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@ -97,7 +114,14 @@ const ChatConfigurationModal = ({
|
|||||||
colon={false}
|
colon={false}
|
||||||
>
|
>
|
||||||
{Object.entries(segmentedMap).map(([key, Element]) => (
|
{Object.entries(segmentedMap).map(([key, Element]) => (
|
||||||
<Element key={key} show={key === value}></Element>
|
<Element
|
||||||
|
key={key}
|
||||||
|
show={key === value}
|
||||||
|
form={form}
|
||||||
|
{...(key === ConfigurationSegmented.PromptEngine
|
||||||
|
? { ref: promptEngineRef }
|
||||||
|
: {})}
|
||||||
|
></Element>
|
||||||
))}
|
))}
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
import { FormInstance } from 'antd';
|
||||||
|
|
||||||
export interface ISegmentedContentProps {
|
export interface ISegmentedContentProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
form: FormInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVariable {
|
||||||
|
temperature: number;
|
||||||
|
top_p: number;
|
||||||
|
frequency_penalty: number;
|
||||||
|
presence_penalty: number;
|
||||||
|
max_tokens: number;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,48 @@
|
|||||||
import { Divider, Flex, Form, InputNumber, Select, Slider } from 'antd';
|
import {
|
||||||
|
LlmModelType,
|
||||||
|
ModelVariableType,
|
||||||
|
settledModelVariableMap,
|
||||||
|
} from '@/constants/knowledge';
|
||||||
|
import { Divider, Flex, Form, InputNumber, Select, Slider, Switch } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { ISegmentedContentProps } from './interface';
|
import { ISegmentedContentProps } from './interface';
|
||||||
|
|
||||||
|
import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
|
||||||
|
import { variableEnabledFieldMap } from './constants';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Option } = Select;
|
const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||||
|
const parameterOptions = Object.values(ModelVariableType).map((x) => ({
|
||||||
|
label: x,
|
||||||
|
value: x,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const parameters: ModelVariableType = Form.useWatch('parameters', form);
|
||||||
|
|
||||||
|
const modelOptions = useSelectLlmOptions();
|
||||||
|
|
||||||
|
const handleParametersChange = (value: ModelVariableType) => {
|
||||||
|
console.info(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const variable = settledModelVariableMap[parameters];
|
||||||
|
form.setFieldsValue({ llm_setting: variable });
|
||||||
|
}, [parameters, form]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const values = Object.keys(variableEnabledFieldMap).reduce<
|
||||||
|
Record<string, boolean>
|
||||||
|
>((pre, field) => {
|
||||||
|
pre[field] = true;
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
form.setFieldsValue(values);
|
||||||
|
}, [form]);
|
||||||
|
|
||||||
|
useFetchLlmList(LlmModelType.Chat);
|
||||||
|
|
||||||
const ModelSetting = ({ show }: ISegmentedContentProps) => {
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={classNames({
|
className={classNames({
|
||||||
@ -15,135 +51,170 @@ const ModelSetting = ({ show }: ISegmentedContentProps) => {
|
|||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Model"
|
label="Model"
|
||||||
name="model"
|
name="llm_id"
|
||||||
// rules={[{ required: true, message: 'Please input!' }]}
|
rules={[{ required: true, message: 'Please select!' }]}
|
||||||
>
|
>
|
||||||
<Select />
|
<Select options={modelOptions} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Parameters"
|
label="Parameters"
|
||||||
name="parameters"
|
name="parameters"
|
||||||
|
initialValue={ModelVariableType.Precise}
|
||||||
// rules={[{ required: true, message: 'Please input!' }]}
|
// rules={[{ required: true, message: 'Please input!' }]}
|
||||||
>
|
>
|
||||||
<Select />
|
<Select<ModelVariableType>
|
||||||
|
options={parameterOptions}
|
||||||
|
onChange={handleParametersChange}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Temperature">
|
<Form.Item label="Temperature" tooltip={'xx'}>
|
||||||
<Flex gap={20}>
|
<Flex gap={20} align="center">
|
||||||
|
<Form.Item
|
||||||
|
name={'temperatureEnabled'}
|
||||||
|
valuePropName="checked"
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Switch size="small" />
|
||||||
|
</Form.Item>
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'province']}
|
name={['llm_setting', 'temperature']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Province is required' }]}
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
>
|
>
|
||||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'street']}
|
name={['llm_setting', 'temperature']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{
|
className={styles.sliderInputNumber}
|
||||||
width: 50,
|
max={1}
|
||||||
}}
|
min={0}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Top P">
|
<Form.Item label="Top P" tooltip={'xx'}>
|
||||||
<Flex gap={20}>
|
<Flex gap={20} align="center">
|
||||||
|
<Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
|
||||||
|
<Switch size="small" />
|
||||||
|
</Form.Item>
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'province']}
|
name={['llm_setting', 'top_p']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Province is required' }]}
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
>
|
>
|
||||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'street']}
|
name={['llm_setting', 'top_p']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{
|
className={styles.sliderInputNumber}
|
||||||
width: 50,
|
max={1}
|
||||||
}}
|
min={0}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Presence Penalty">
|
<Form.Item label="Presence Penalty" tooltip={'xx'}>
|
||||||
<Flex gap={20}>
|
<Flex gap={20} align="center">
|
||||||
|
<Form.Item
|
||||||
|
name={'presencePenaltyEnabled'}
|
||||||
|
valuePropName="checked"
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Switch size="small" />
|
||||||
|
</Form.Item>
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'province']}
|
name={['llm_setting', 'presence_penalty']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Province is required' }]}
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
>
|
>
|
||||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'street']}
|
name={['llm_setting', 'presence_penalty']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{
|
className={styles.sliderInputNumber}
|
||||||
width: 50,
|
max={1}
|
||||||
}}
|
min={0}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Frequency Penalty">
|
<Form.Item label="Frequency Penalty" tooltip={'xx'}>
|
||||||
<Flex gap={20}>
|
<Flex gap={20} align="center">
|
||||||
|
<Form.Item
|
||||||
|
name={'frequencyPenaltyEnabled'}
|
||||||
|
valuePropName="checked"
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Switch size="small" />
|
||||||
|
</Form.Item>
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'province']}
|
name={['llm_setting', 'frequency_penalty']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Province is required' }]}
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
>
|
>
|
||||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'street']}
|
name={['llm_setting', 'frequency_penalty']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{
|
className={styles.sliderInputNumber}
|
||||||
width: 50,
|
max={1}
|
||||||
}}
|
min={0}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Max Tokens">
|
<Form.Item label="Max Tokens" tooltip={'xx'}>
|
||||||
<Flex gap={20}>
|
<Flex gap={20} align="center">
|
||||||
|
<Form.Item name={'maxTokensEnabled'} valuePropName="checked" noStyle>
|
||||||
|
<Switch size="small" />
|
||||||
|
</Form.Item>
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'province']}
|
name={['llm_setting', 'max_tokens']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Province is required' }]}
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
>
|
>
|
||||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
<Slider className={styles.variableSlider} max={2048} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['address', 'street']}
|
name={['llm_setting', 'max_tokens']}
|
||||||
noStyle
|
noStyle
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
style={{
|
className={styles.sliderInputNumber}
|
||||||
width: 50,
|
max={2048}
|
||||||
}}
|
min={0}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import SimilaritySlider from '@/components/similarity-slider';
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -6,13 +7,19 @@ import {
|
|||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
Row,
|
Row,
|
||||||
Select,
|
Slider,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
TableProps,
|
TableProps,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useState } from 'react';
|
import {
|
||||||
|
ForwardedRef,
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { EditableCell, EditableRow } from './editable-cell';
|
import { EditableCell, EditableRow } from './editable-cell';
|
||||||
import { ISegmentedContentProps } from './interface';
|
import { ISegmentedContentProps } from './interface';
|
||||||
@ -21,12 +28,20 @@ import styles from './index.less';
|
|||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
key: string;
|
key: string;
|
||||||
|
variable: string;
|
||||||
optional: boolean;
|
optional: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Option } = Select;
|
type FieldType = {
|
||||||
|
similarity_threshold?: number;
|
||||||
|
vector_similarity_weight?: number;
|
||||||
|
top_n?: number;
|
||||||
|
};
|
||||||
|
|
||||||
const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
const PromptEngine = (
|
||||||
|
{ show, form }: ISegmentedContentProps,
|
||||||
|
ref: ForwardedRef<Array<Omit<DataType, 'variable'>>>,
|
||||||
|
) => {
|
||||||
const [dataSource, setDataSource] = useState<DataType[]>([]);
|
const [dataSource, setDataSource] = useState<DataType[]>([]);
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
@ -52,6 +67,44 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
|||||||
setDataSource(newData);
|
setDataSource(newData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
setDataSource((state) => [
|
||||||
|
...state,
|
||||||
|
{
|
||||||
|
key: uuid(),
|
||||||
|
variable: '',
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOptionalChange = (row: DataType) => (checked: boolean) => {
|
||||||
|
const newData = [...dataSource];
|
||||||
|
const index = newData.findIndex((item) => row.key === item.key);
|
||||||
|
const item = newData[index];
|
||||||
|
newData.splice(index, 1, {
|
||||||
|
...item,
|
||||||
|
optional: checked,
|
||||||
|
});
|
||||||
|
setDataSource(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => {
|
||||||
|
return dataSource
|
||||||
|
.filter((x) => x.variable.trim() !== '')
|
||||||
|
.map((x) => ({ key: x.variable, optional: x.optional }));
|
||||||
|
},
|
||||||
|
[dataSource],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setFieldValue(['prompt_config', 'parameters'], dataSource);
|
||||||
|
const x = form.getFieldValue(['prompt_config', 'parameters']);
|
||||||
|
console.info(x);
|
||||||
|
}, [dataSource, form]);
|
||||||
|
|
||||||
const columns: TableProps<DataType>['columns'] = [
|
const columns: TableProps<DataType>['columns'] = [
|
||||||
{
|
{
|
||||||
title: 'key',
|
title: 'key',
|
||||||
@ -71,8 +124,14 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
|||||||
key: 'optional',
|
key: 'optional',
|
||||||
width: 40,
|
width: 40,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render() {
|
render(text, record) {
|
||||||
return <Switch size="small" />;
|
return (
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={text}
|
||||||
|
onChange={handleOptionalChange(record)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -87,17 +146,6 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleAdd = () => {
|
|
||||||
setDataSource((state) => [
|
|
||||||
...state,
|
|
||||||
{
|
|
||||||
key: uuid(),
|
|
||||||
variable: '',
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={classNames({
|
className={classNames({
|
||||||
@ -106,12 +154,20 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
|||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Orchestrate"
|
label="Orchestrate"
|
||||||
name="orchestrate"
|
|
||||||
rules={[{ required: true, message: 'Please input!' }]}
|
rules={[{ required: true, message: 'Please input!' }]}
|
||||||
|
name={['prompt_config', 'system']}
|
||||||
|
initialValue={`你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。
|
||||||
|
以下是知识库:
|
||||||
|
{knowledge}
|
||||||
|
以上是知识库。`}
|
||||||
>
|
>
|
||||||
<Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
|
<Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
|
<SimilaritySlider></SimilaritySlider>
|
||||||
|
<Form.Item<FieldType> label="Top n" name={'top_n'} initialValue={0}>
|
||||||
|
<Slider max={30} />
|
||||||
|
</Form.Item>
|
||||||
<section className={classNames(styles.variableContainer)}>
|
<section className={classNames(styles.variableContainer)}>
|
||||||
<Row align={'middle'} justify="end">
|
<Row align={'middle'} justify="end">
|
||||||
<Col span={6} className={styles.variableAlign}>
|
<Col span={6} className={styles.variableAlign}>
|
||||||
@ -139,25 +195,8 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
|||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
<Form.Item
|
|
||||||
label="Select one context"
|
|
||||||
name="context"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: 'Please select your favourite colors!',
|
|
||||||
type: 'array',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="multiple" placeholder="Please select favourite colors">
|
|
||||||
<Option value="red">Red</Option>
|
|
||||||
<Option value="green">Green</Option>
|
|
||||||
<Option value="blue">Blue</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PromptEngine;
|
export default forwardRef(PromptEngine);
|
||||||
|
29
web/src/pages/chat/hooks.ts
Normal file
29
web/src/pages/chat/hooks.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { IDialog } from '@/interfaces/database/chat';
|
||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
|
export const useFetchDialogList = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const dialogList: IDialog[] = useSelector(
|
||||||
|
(state: any) => state.chatModel.dialogList,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: 'chatModel/listDialog' });
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return dialogList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetDialog = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const setDialog = useCallback(
|
||||||
|
(payload: IDialog) => {
|
||||||
|
dispatch({ type: 'chatModel/setDialog', payload });
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return setDialog;
|
||||||
|
};
|
@ -4,6 +4,17 @@
|
|||||||
.chatAppWrapper {
|
.chatAppWrapper {
|
||||||
width: 288px;
|
width: 288px;
|
||||||
padding: 26px;
|
padding: 26px;
|
||||||
|
|
||||||
|
.chatAppCard {
|
||||||
|
:global(.ant-card-body) {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.cubeIcon {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.chatTitleWrapper {
|
.chatTitleWrapper {
|
||||||
width: 220px;
|
width: 220px;
|
||||||
|
@ -1,14 +1,73 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Divider, Flex, Space, Tag } from 'antd';
|
import {
|
||||||
import { useSelector } from 'umi';
|
Button,
|
||||||
|
Card,
|
||||||
|
Divider,
|
||||||
|
Dropdown,
|
||||||
|
Flex,
|
||||||
|
MenuProps,
|
||||||
|
Space,
|
||||||
|
Tag,
|
||||||
|
} from 'antd';
|
||||||
import ChatContainer from './chat-container';
|
import ChatContainer from './chat-container';
|
||||||
|
|
||||||
|
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
||||||
import ModalManager from '@/components/modal-manager';
|
import ModalManager from '@/components/modal-manager';
|
||||||
|
import classNames from 'classnames';
|
||||||
import ChatConfigurationModal from './chat-configuration-modal';
|
import ChatConfigurationModal from './chat-configuration-modal';
|
||||||
|
import { useFetchDialogList } from './hooks';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const Chat = () => {
|
const Chat = () => {
|
||||||
const { name } = useSelector((state: any) => state.chatModel);
|
const dialogList = useFetchDialogList();
|
||||||
|
const [activated, setActivated] = useState<string>('');
|
||||||
|
|
||||||
|
const handleAppCardEnter = (id: string) => () => {
|
||||||
|
setActivated(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAppCardLeave = () => {
|
||||||
|
setActivated('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: (
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://www.antgroup.com"
|
||||||
|
>
|
||||||
|
1st menu item
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const appItems: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: (
|
||||||
|
<Space>
|
||||||
|
<EditOutlined />
|
||||||
|
Edit
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: (
|
||||||
|
<Space>
|
||||||
|
<DeleteOutlined />
|
||||||
|
Delete chat
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex className={styles.chatWrapper}>
|
<Flex className={styles.chatWrapper}>
|
||||||
@ -32,9 +91,33 @@ const Chat = () => {
|
|||||||
</ModalManager>
|
</ModalManager>
|
||||||
|
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<Card>
|
<Space direction={'vertical'} size={'middle'}>
|
||||||
<p>Card content</p>
|
{dialogList.map((x) => (
|
||||||
</Card>
|
<Card
|
||||||
|
key={x.id}
|
||||||
|
className={classNames(styles.chatAppCard)}
|
||||||
|
onMouseEnter={handleAppCardEnter(x.id)}
|
||||||
|
onMouseLeave={handleAppCardLeave}
|
||||||
|
>
|
||||||
|
<Flex justify="space-between" align="center">
|
||||||
|
<Space>
|
||||||
|
{x.icon}
|
||||||
|
<section>
|
||||||
|
<b>{x.name}</b>
|
||||||
|
<div>{x.description}</div>
|
||||||
|
</section>
|
||||||
|
</Space>
|
||||||
|
{activated === x.id && (
|
||||||
|
<section>
|
||||||
|
<Dropdown menu={{ items: appItems }}>
|
||||||
|
<ChatAppCube className={styles.cubeIcon}></ChatAppCube>
|
||||||
|
</Dropdown>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Divider type={'vertical'} className={styles.divider}></Divider>
|
<Divider type={'vertical'} className={styles.divider}></Divider>
|
||||||
@ -49,7 +132,9 @@ const Chat = () => {
|
|||||||
<b>Chat</b>
|
<b>Chat</b>
|
||||||
<Tag>25</Tag>
|
<Tag>25</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
<FormOutlined />
|
<Dropdown menu={{ items }}>
|
||||||
|
<FormOutlined />
|
||||||
|
</Dropdown>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<section className={styles.chatTitleContent}>today</section>
|
<section className={styles.chatTitleContent}>today</section>
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
|
import { IDialog } from '@/interfaces/database/chat';
|
||||||
|
import chatService from '@/services/chatService';
|
||||||
|
import { message } from 'antd';
|
||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
|
|
||||||
export interface ChatModelState {
|
export interface ChatModelState {
|
||||||
name: string;
|
name: string;
|
||||||
|
dialogList: IDialog[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: DvaModel<ChatModelState> = {
|
const model: DvaModel<ChatModelState> = {
|
||||||
namespace: 'chatModel',
|
namespace: 'chatModel',
|
||||||
state: {
|
state: {
|
||||||
name: 'kate',
|
name: 'kate',
|
||||||
|
dialogList: [],
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
save(state, action) {
|
save(state, action) {
|
||||||
@ -16,16 +21,41 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
...action.payload,
|
...action.payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
setDialogList(state, { payload }) {
|
||||||
subscriptions: {
|
return {
|
||||||
setup({ dispatch, history }) {
|
...state,
|
||||||
return history.listen((query) => {
|
dialogList: payload,
|
||||||
console.log(query);
|
};
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
*query({ payload }, { call, put }) {},
|
*getDialog({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.getDialog, payload);
|
||||||
|
},
|
||||||
|
*setDialog({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.setDialog, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({ type: 'listDialog' });
|
||||||
|
message.success('Created successfully !');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*listDialog({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.listDialog, payload);
|
||||||
|
yield put({ type: 'setDialogList', payload: data.data });
|
||||||
|
},
|
||||||
|
*listConversation({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.listConversation, payload);
|
||||||
|
},
|
||||||
|
*getConversation({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.getConversation, payload);
|
||||||
|
},
|
||||||
|
*setConversation({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.setConversation, payload);
|
||||||
|
},
|
||||||
|
*completeConversation({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.completeConversation, payload);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,33 +2,14 @@ import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
|
|||||||
import ModalManager from '@/components/modal-manager';
|
import ModalManager from '@/components/modal-manager';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Space } from 'antd';
|
import { Button, Flex, Space } from 'antd';
|
||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
|
||||||
import KnowledgeCard from './knowledge-card';
|
import KnowledgeCard from './knowledge-card';
|
||||||
import KnowledgeCreatingModal from './knowledge-creating-modal';
|
import KnowledgeCreatingModal from './knowledge-creating-modal';
|
||||||
|
|
||||||
|
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const Knowledge = () => {
|
const Knowledge = () => {
|
||||||
const dispatch = useDispatch();
|
const data = useFetchKnowledgeList();
|
||||||
const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { data = [] } = knowledgeModel;
|
|
||||||
|
|
||||||
const fetchList = useCallback(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'knowledgeModel/getList',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// const handleAddKnowledge = () => {
|
|
||||||
// navigate(`/knowledge/${KnowledgeRouteKey.Configuration}`);
|
|
||||||
// };
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchList();
|
|
||||||
}, [fetchList]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.knowledge}>
|
<div className={styles.knowledge}>
|
||||||
|
@ -37,7 +37,7 @@ const SettingList = () => {
|
|||||||
type: 'settingModel/my_llm',
|
type: 'settingModel/my_llm',
|
||||||
payload: {},
|
payload: {},
|
||||||
});
|
});
|
||||||
}, []);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
48
web/src/services/chatService.ts
Normal file
48
web/src/services/chatService.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import api from '@/utils/api';
|
||||||
|
import registerServer from '@/utils/registerServer';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDialog,
|
||||||
|
setDialog,
|
||||||
|
listDialog,
|
||||||
|
getConversation,
|
||||||
|
setConversation,
|
||||||
|
completeConversation,
|
||||||
|
listConversation,
|
||||||
|
} = api;
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
getDialog: {
|
||||||
|
url: getDialog,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
setDialog: {
|
||||||
|
url: setDialog,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
listDialog: {
|
||||||
|
url: listDialog,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
listConversation: {
|
||||||
|
url: listConversation,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
getConversation: {
|
||||||
|
url: getConversation,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
setConversation: {
|
||||||
|
url: setConversation,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
completeConversation: {
|
||||||
|
url: completeConversation,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const chatService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
|
||||||
|
export default chatService;
|
@ -42,4 +42,14 @@ export default {
|
|||||||
document_create: `${api_host}/document/create`,
|
document_create: `${api_host}/document/create`,
|
||||||
document_run: `${api_host}/document/run`,
|
document_run: `${api_host}/document/run`,
|
||||||
document_change_parser: `${api_host}/document/change_parser`,
|
document_change_parser: `${api_host}/document/change_parser`,
|
||||||
|
|
||||||
|
setDialog: `${api_host}/dialog/set`,
|
||||||
|
getDialog: `${api_host}/dialog/get`,
|
||||||
|
listDialog: `${api_host}/dialog/list`,
|
||||||
|
|
||||||
|
setConversation: `${api_host}/conversation/set`,
|
||||||
|
getConversation: `${api_host}/conversation/get`,
|
||||||
|
listConversation: `${api_host}/conversation/list`,
|
||||||
|
removeConversation: `${api_host}/conversation/rm`,
|
||||||
|
completeConversation: `${api_host}/conversation/completion`,
|
||||||
};
|
};
|
||||||
|
@ -5,22 +5,23 @@
|
|||||||
*/
|
*/
|
||||||
// import numeral from 'numeral';
|
// import numeral from 'numeral';
|
||||||
|
|
||||||
import JSEncrypt from 'jsencrypt';
|
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
|
import JSEncrypt from 'jsencrypt';
|
||||||
|
|
||||||
export const getWidth = () => {
|
export const getWidth = () => {
|
||||||
return { width: window.innerWidth };
|
return { width: window.innerWidth };
|
||||||
};
|
};
|
||||||
export const rsaPsw = (password: string) => {
|
export const rsaPsw = (password: string) => {
|
||||||
const pub = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB-----END PUBLIC KEY-----"
|
const pub =
|
||||||
const encryptor = new JSEncrypt()
|
'-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB-----END PUBLIC KEY-----';
|
||||||
|
const encryptor = new JSEncrypt();
|
||||||
|
|
||||||
encryptor.setPublicKey(pub)
|
encryptor.setPublicKey(pub);
|
||||||
|
|
||||||
return encryptor.encrypt(Base64.encode(password))
|
return encryptor.encrypt(Base64.encode(password));
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getWidth,
|
getWidth,
|
||||||
rsaPsw
|
rsaPsw,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user