feat: 增加不同模型

This commit is contained in:
arvinxx
2023-07-16 20:52:35 +08:00
parent a82f35d338
commit d95027d42b
12 changed files with 294 additions and 89 deletions

View File

@@ -3,6 +3,7 @@
"agentAvatar": "头像",
"agentDescription": "描述",
"agentDescriptionPlaceholder": "请输入描述",
"agentModel": "模型",
"agentName": "名称",
"agentNamePlaceholder": "请输入名称",
"agentProfile": "助手信息",
@@ -18,7 +19,12 @@
"defaultAgent": "默认助手",
"edit": "编辑",
"editAgentProfile": "编辑助手信息",
"export": "导出",
"gpt-3.5-turbo": "GPT 3.5",
"gpt-3.5-turbo-16k": "GPT 3.5 (16K)",
"gpt-4": "GPT 4",
"gpt-4-32k": "GPT 4 (32K)",
"modelConfig": "模型配置",
"modelTemperature": "发散度",
"newAgent": "新建助手",
"noDescription": "暂无描述",
@@ -29,5 +35,6 @@
"sessionSetting": "会话设置",
"setting": "设置",
"share": "分享",
"updateAgent": "更新助理信息"
"updateAgent": "更新助理信息",
"updatePrompt": "更新提示词"
}

View File

@@ -86,7 +86,6 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
{
streaming: true,
...params,
callbacks: [
{
handleLLMNewToken(token) {

View File

@@ -13,7 +13,7 @@ const SessionList = memo(() => {
const { styles, cx } = useStyles();
const list = useSessionStore((s) => sessionSelectors.chatList(s), isEqual);
const [activeId, loading] = useSessionStore(
(s) => [s.activeId, s.loading.summarizingTitle],
(s) => [s.activeId, s.autocompleteLoading.title],
shallow,
);

View File

@@ -0,0 +1,106 @@
import { TextArea } from '@lobehub/ui';
import { Button, Collapse, InputNumber, Segmented, Slider } from 'antd';
import isEqual from 'fast-deep-equal';
import { useTranslation } from 'next-i18next';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
import { agentSelectors, useSessionStore } from '@/store/session';
import { LanguageModel } from '@/types/llm';
import { FormItem } from './FormItem';
import { useStyles } from './style';
const AgentConfig = () => {
const { t } = useTranslation('common');
const { styles, theme } = useStyles();
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
return (
<>
<Flexbox
align={'center'}
distribution={'space-between'}
horizontal
paddingBlock={12}
style={{
borderBottom: `1px solid ${theme.colorBorder}`,
}}
>
<Flexbox className={styles.profile}> {t('modelConfig')}</Flexbox>
</Flexbox>
<Flexbox gap={24}>
<FormItem label={t('agentModel')}>
<Segmented
block
onChange={(value) => {
updateAgentConfig({ model: value as LanguageModel });
}}
options={Object.values(LanguageModel).map((value) => ({
label: t(value),
value,
}))}
size={'large'}
/>
</FormItem>
<FormItem label={t('agentPrompt')}>
<Flexbox gap={16}>
<TextArea
placeholder={t('agentPromptPlaceholder')}
style={{ minHeight: 160 }}
type={'block'}
value={config.systemRole}
/>
<Flexbox direction={'horizontal-reverse'}>
<Button type={'primary'}>{t('updatePrompt')}</Button>
</Flexbox>
</Flexbox>
</FormItem>
<Collapse
activeKey={['advanceSettings']}
bordered={false}
className={styles.title}
expandIconPosition={'end'}
items={[
{
children: (
<Flexbox paddingBlock={16}>
<FormItem label={t('modelTemperature')}>
<Flexbox gap={16} horizontal>
<Slider
max={1}
min={0}
onChange={(value) => {
updateAgentConfig({ params: { temperature: value } });
}}
step={0.1}
style={{ flex: 1 }}
value={Number(config.params.temperature)}
/>
<InputNumber
max={1}
min={0}
onChange={(value) => {
if (value) updateAgentConfig({ params: { temperature: value } });
}}
value={config.params.temperature}
/>
</Flexbox>
</FormItem>
</Flexbox>
),
key: 'advanceSettings',
label: t('advanceSettings'),
},
]}
/>
</Flexbox>
</>
);
};
export default AgentConfig;

View File

@@ -0,0 +1,84 @@
import { ActionIcon, Avatar, Input } from '@lobehub/ui';
import { Button } from 'antd';
import isEqual from 'fast-deep-equal';
import { LucideSparkles } from 'lucide-react';
import { useTranslation } from 'next-i18next';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
import { agentSelectors, useSessionStore } from '@/store/session';
import { FormItem } from './FormItem';
import { useStyles } from './style';
const AgentMeta = () => {
const { t } = useTranslation('common');
const { styles, theme } = useStyles();
const metaData = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
const [autocompleteMeta, loading] = useSessionStore(
(s) => [s.autocompleteMeta, s.autocompleteLoading],
shallow,
);
const basic = [
{ key: 'title', label: t('agentName'), placeholder: t('agentNamePlaceholder') },
{
key: 'description',
label: t('agentDescription'),
placeholder: t('agentDescriptionPlaceholder'),
},
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
];
return (
<>
<Flexbox
align={'center'}
distribution={'space-between'}
horizontal
paddingBlock={12}
style={{
borderBottom: `1px solid ${theme.colorBorder}`,
}}
>
<Flexbox className={styles.profile}> {t('profile')}</Flexbox>
<Button size={'large'}>{t('autoGenerate')}</Button>
</Flexbox>
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
<Flexbox flex={1} gap={24}>
{basic.map((item) => (
<FormItem key={item.key} label={item.label}>
<Input
placeholder={item.placeholder}
suffix={
<ActionIcon
icon={LucideSparkles}
loading={loading[item.key as keyof typeof loading]}
onClick={() => {
autocompleteMeta(item.key as keyof typeof metaData);
}}
size={'small'}
style={{
color: theme.purple,
}}
title={t('autoGenerate')}
/>
}
type={'block'}
value={metaData[item.key as keyof typeof metaData]}
/>
</FormItem>
))}
</Flexbox>
<FormItem label={t('agentAvatar')}>
<Avatar avatar={metaData.avatar} size={200} />
</FormItem>
</Flexbox>
</>
);
};
export default AgentMeta;

View File

@@ -13,7 +13,8 @@ interface FormItemProps {
children: ReactNode;
label: string;
}
const FormItem = memo<FormItemProps>(({ label, children }) => {
export const FormItem = memo<FormItemProps>(({ label, children }) => {
const { styles } = useStyles();
return (
@@ -23,5 +24,3 @@ const FormItem = memo<FormItemProps>(({ label, children }) => {
</Flexbox>
);
});
export default FormItem;

View File

@@ -1,5 +1,5 @@
import { Avatar, ChatHeader, Input, TextArea } from '@lobehub/ui';
import { Button, Slider } from 'antd';
import { ChatHeader } from '@lobehub/ui';
import { Button } from 'antd';
import { createStyles } from 'antd-style';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
@@ -8,7 +8,8 @@ import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import ChatLayout from '../../layout';
import FormItem from './Form';
import AgentConfig from './AgentConfig';
import AgentMeta from './AgentMeta';
const useStyles = createStyles(({ css, token }) => ({
footer: css`
@@ -23,10 +24,6 @@ const useStyles = createStyles(({ css, token }) => ({
background: ${token.colorBgContainer};
border-bottom: 1px solid ${token.colorSplit};
`,
profile: css`
font-size: 20px;
color: ${token.colorTextTertiary};
`,
title: css`
font-size: 16px;
font-weight: 500;
@@ -36,13 +33,8 @@ const useStyles = createStyles(({ css, token }) => ({
const EditPage = memo(() => {
const { t } = useTranslation('common');
const { styles, theme } = useStyles();
const { styles } = useStyles();
const basic = [
{ label: t('agentName'), placeholder: t('agentNamePlaceholder') },
{ label: t('agentDescription'), placeholder: t('agentDescriptionPlaceholder') },
{ label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
];
return (
<ChatLayout>
<Flexbox height={'100vh'} style={{ position: 'relative' }} width={'100%'}>
@@ -50,59 +42,18 @@ const EditPage = memo(() => {
<ChatHeader
left={<div className={styles.title}>{t('editAgentProfile')}</div>}
onBackClick={() => Router.back()}
right={
<>
<Button>{t('share')}</Button>
<Button type={'primary'}>{t('export')}</Button>
</>
}
showBackButton
/>
{/*form*/}
<Flexbox className={styles.form} flex={1} gap={10} padding={24}>
<FormItem label={t('agentPrompt')}>
<TextArea
placeholder={t('agentPromptPlaceholder')}
// style={{ minHeight: 64 }}
type={'block'}
/>
</FormItem>
<Flexbox
align={'center'}
distribution={'space-between'}
horizontal
paddingBlock={12}
style={{
borderBottom: `1px solid ${theme.colorBorder}`,
}}
>
<Flexbox className={styles.profile}> {t('profile')}</Flexbox>
<Button size={'large'}>{t('autoGenerate')}</Button>
</Flexbox>
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
<Flexbox flex={1} gap={24}>
{basic.map((item) => (
<FormItem key={item.label} label={item.label}>
<Input placeholder={item.placeholder} type={'block'} />
</FormItem>
))}
<Flexbox className={styles.profile}> {t('advanceSettings')}</Flexbox>
<FormItem label={t('modelTemperature')}>
<Slider />
</FormItem>
</Flexbox>
<FormItem label={t('agentAvatar')}>
<Avatar size={200} />
</FormItem>
</Flexbox>
</Flexbox>
{/*bottom*/}
<Flexbox
className={styles.footer}
direction={'horizontal-reverse'}
gap={8}
padding={'16px 24px'}
>
<Button type={'primary'}>{t('updateAgent')}</Button>
<Button>{t('reset')}</Button>
<AgentMeta />
<AgentConfig />
</Flexbox>
</Flexbox>
</ChatLayout>

View File

@@ -0,0 +1,33 @@
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token }) => ({
footer: css`
position: sticky;
bottom: 0;
border-top: 1px solid ${token.colorBorder};
`,
form: css`
overflow-y: auto;
`,
header: css`
background: ${token.colorBgContainer};
border-bottom: 1px solid ${token.colorSplit};
`,
profile: css`
font-size: 20px;
font-weight: bold;
color: ${token.colorText};
`,
title: css`
font-size: 16px;
font-weight: 500;
.ant-collapse-header {
padding: 0 !important;
}
.ant-collapse-content-box {
padding: 0 !important;
}
`,
}));

View File

@@ -24,13 +24,14 @@ export interface AgentAction {
* @returns 一个 Promise用于异步操作完成后的处理
*/
autocompleteAgentDescription: (id: string) => Promise<void>;
/**
* 自动完成代理标题
* @param id - 代理的 ID
* @returns 一个 Promise用于异步操作完成后的处理
*/
autocompleteAgentTitle: (id: string) => Promise<void>;
autocompleteMeta: (key: keyof MetaData) => void;
/**
* 自动完成会话代理元数据
* @param id - 代理的 ID
@@ -77,7 +78,7 @@ export const createAgentSlice: StateCreator<
const emoji = await fetchPresetTaskResult({
onLoadingChange: (loading) => {
get().updateLoadingState('pickingEmojiAvatar', loading);
get().updateLoadingState('avatar', loading);
},
params: promptPickEmoji(systemRole),
});
@@ -111,7 +112,7 @@ export const createAgentSlice: StateCreator<
});
},
onLoadingChange: (loading) => {
updateLoadingState('summarizingDescription', loading);
updateLoadingState('description', loading);
},
onMessageHandle: internalUpdateAgentMeta(id)('description'),
params: promptSummaryDescription(chats),
@@ -137,13 +138,34 @@ export const createAgentSlice: StateCreator<
dispatchSession({ id, key: 'title', type: 'updateSessionMeta', value: previousTitle });
},
onLoadingChange: (loading) => {
updateLoadingState('summarizingTitle', loading);
updateLoadingState('title', loading);
},
onMessageHandle: internalUpdateAgentMeta(id)('title'),
params: promptSummaryTitle(chats),
});
},
autocompleteMeta: (key) => {
const { activeId, autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
if (!activeId) return;
switch (key) {
case 'avatar': {
autoPickEmoji(activeId);
return;
}
case 'description': {
autocompleteAgentDescription(activeId);
return;
}
case 'title': {
autocompleteAgentTitle(activeId);
}
}
},
autocompleteSessionAgentMeta: (id) => {
const session = sessionSelectors.getSessionById(id)(get());
@@ -160,7 +182,6 @@ export const createAgentSlice: StateCreator<
get().autoPickEmoji(id);
}
},
internalUpdateAgentMeta: (id: string) => (key: keyof MetaData) => {
let value = '';
return (text: string) => {
@@ -183,6 +204,6 @@ export const createAgentSlice: StateCreator<
get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
},
updateLoadingState: (key, value) => {
set({ loading: { ...get().loading, [key]: value } });
set({ autocompleteLoading: { ...get().autocompleteLoading, [key]: value } });
},
});

View File

@@ -1,13 +1,11 @@
import { LanguageModel } from '@/types/llm';
import { MetaData } from '@/types/meta';
import { LobeAgentConfig } from '@/types/session';
export interface SessionLoadingState {
pickingEmojiAvatar: boolean;
summarizingDescription: boolean;
summarizingTitle: boolean;
}
export type SessionLoadingState = Record<Partial<keyof MetaData>, boolean>;
export interface AgentConfigState {
loading: SessionLoadingState;
autocompleteLoading: SessionLoadingState;
showAgentSettings: boolean;
}
@@ -24,10 +22,12 @@ export const DEFAULT_TITLE = '默认对话';
export const initialAgentConfigState: AgentConfigState = {
// // loading 中间态
loading: {
pickingEmojiAvatar: false,
summarizingDescription: false,
summarizingTitle: false,
autocompleteLoading: {
avatar: false,
backgroundColor: false,
description: false,
tag: false,
title: false,
},
showAgentSettings: false,

View File

@@ -1,16 +1,19 @@
import { SessionStore } from '@/store/session';
import { LanguageModel } from '@/types/llm';
import { MetaData } from '@/types/meta';
import { LobeAgentConfig } from '@/types/session';
import { sessionSelectors } from '../session';
import { DEFAULT_AVATAR, initialLobeAgentConfig } from './initialState';
const currentAgentTitle = (s: SessionStore) => {
const currentAgentMeta = (s: SessionStore): MetaData => {
const session = sessionSelectors.currentSession(s);
return session?.meta.title;
return session?.meta || {};
};
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title;
const currentAgentAvatar = (s: SessionStore) => {
const session = sessionSelectors.currentSession(s);
@@ -39,6 +42,7 @@ export const agentSelectors = {
currentAgentAvatar,
currentAgentConfig,
currentAgentConfigSafe,
currentAgentMeta,
currentAgentModel,
currentAgentTitle,
};

View File

@@ -1,7 +1,7 @@
import { StateCreator } from 'zustand/vanilla';
import { fetchChatModel } from '@/services/chatModel';
import { SessionStore, chatSelectors, sessionSelectors } from '@/store/session';
import { SessionStore, agentSelectors, chatSelectors, sessionSelectors } from '@/store/session';
import { ChatMessage } from '@/types/chatMessage';
import { FetchSSEOptions, fetchSSE } from '@/utils/fetch';
import { nanoid } from '@/utils/uuid';
@@ -83,8 +83,9 @@ export const createChatSlice: StateCreator<
generateMessage: async (messages, options) => {
set({ chatLoading: true });
const config = agentSelectors.currentAgentConfigSafe(get());
const fetcher = () => fetchChatModel({ messages });
const fetcher = () => fetchChatModel({ messages, model: config.model, ...config.params });
await fetchSSE(fetcher, options);