@lobehub/chat 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changelogrc.js +1 -0
- package/.commitlintrc.js +1 -0
- package/.editorconfig +16 -0
- package/.eslintignore +32 -0
- package/.eslintrc.js +6 -0
- package/.github/ISSUE_TEMPLATE/1_bug_report.yml +45 -0
- package/.github/ISSUE_TEMPLATE/2_feature_request.yml +21 -0
- package/.github/ISSUE_TEMPLATE/3_question.yml +15 -0
- package/.github/ISSUE_TEMPLATE/4_other.md +7 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- package/.github/dependabot.yml +17 -0
- package/.github/workflows/auto-merge.yml +32 -0
- package/.github/workflows/contributor-help.yml +29 -0
- package/.github/workflows/issue-check-inactive.yml +22 -0
- package/.github/workflows/issue-close-require.yml +46 -0
- package/.github/workflows/issue-remove-inactive.yml +25 -0
- package/.github/workflows/release.yml +34 -0
- package/.github/workflows/test.yml +30 -0
- package/.gitpod.yml +3 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +5 -0
- package/.i18nrc.js +13 -0
- package/.prettierignore +63 -0
- package/.prettierrc.js +1 -0
- package/.releaserc.js +1 -0
- package/.remarkrc.js +1 -0
- package/.stylelintrc.js +8 -0
- package/CHANGELOG.md +80 -0
- package/README.md +147 -0
- package/locales/en_US/common.json +40 -0
- package/locales/en_US/setting.json +97 -0
- package/locales/zh_CN/common.json +40 -0
- package/locales/zh_CN/setting.json +98 -0
- package/next.config.mjs +32 -0
- package/package.json +138 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/scripts/genDefaultLocale.mjs +12 -0
- package/scripts/toc.mjs +40 -0
- package/src/const/fetch.ts +1 -0
- package/src/const/modelTokens.ts +8 -0
- package/src/features/FolderPanel/index.tsx +55 -0
- package/src/helpers/prompt.test.ts +36 -0
- package/src/helpers/prompt.ts +36 -0
- package/src/helpers/url.ts +17 -0
- package/src/layout/index.tsx +42 -0
- package/src/layout/style.ts +18 -0
- package/src/locales/create.ts +48 -0
- package/src/locales/default/common.ts +41 -0
- package/src/locales/default/setting.ts +97 -0
- package/src/locales/index.ts +5 -0
- package/src/locales/resources/en_US.ts +9 -0
- package/src/locales/resources/index.ts +7 -0
- package/src/locales/resources/zh_CN.ts +9 -0
- package/src/migrations/FromV0ToV1.ts +12 -0
- package/src/migrations/index.ts +13 -0
- package/src/pages/Sidebar.tsx +36 -0
- package/src/pages/_app.page.tsx +13 -0
- package/src/pages/_document.page.tsx +70 -0
- package/src/pages/api/LangChainStream.ts +95 -0
- package/src/pages/api/chain.api.ts +17 -0
- package/src/pages/api/openai.api.ts +31 -0
- package/src/pages/chat/SessionList/Header.tsx +56 -0
- package/src/pages/chat/SessionList/List/SessionItem.tsx +90 -0
- package/src/pages/chat/SessionList/List/index.tsx +31 -0
- package/src/pages/chat/SessionList/List/style.ts +77 -0
- package/src/pages/chat/SessionList/index.tsx +18 -0
- package/src/pages/chat/[id]/Config/ConfigCell.tsx +68 -0
- package/src/pages/chat/[id]/Config/ReadMode.tsx +63 -0
- package/src/pages/chat/[id]/Config/index.tsx +79 -0
- package/src/pages/chat/[id]/Conversation/ChatList.tsx +36 -0
- package/src/pages/chat/[id]/Conversation/Input.tsx +61 -0
- package/src/pages/chat/[id]/Conversation/index.tsx +32 -0
- package/src/pages/chat/[id]/Header.tsx +86 -0
- package/src/pages/chat/[id]/edit/AgentConfig.tsx +95 -0
- package/src/pages/chat/[id]/edit/AgentMeta.tsx +117 -0
- package/src/pages/chat/[id]/edit/FormItem.tsx +26 -0
- package/src/pages/chat/[id]/edit/Prompt.tsx +68 -0
- package/src/pages/chat/[id]/edit/index.page.tsx +62 -0
- package/src/pages/chat/[id]/edit/style.ts +42 -0
- package/src/pages/chat/[id]/index.page.tsx +40 -0
- package/src/pages/chat/index.page.tsx +1 -0
- package/src/pages/chat/layout.tsx +51 -0
- package/src/pages/index.page.tsx +1 -0
- package/src/pages/setting/Header.tsx +27 -0
- package/src/pages/setting/SettingForm.tsx +42 -0
- package/src/pages/setting/index.page.tsx +41 -0
- package/src/prompts/agent.ts +65 -0
- package/src/services/chatModel.ts +34 -0
- package/src/services/langChain.ts +18 -0
- package/src/services/url.ts +8 -0
- package/src/store/middleware/createHashStorage.ts +49 -0
- package/src/store/session/index.ts +33 -0
- package/src/store/session/initialState.ts +11 -0
- package/src/store/session/selectors.ts +3 -0
- package/src/store/session/slices/agentConfig/action.ts +226 -0
- package/src/store/session/slices/agentConfig/index.ts +3 -0
- package/src/store/session/slices/agentConfig/initialState.ts +34 -0
- package/src/store/session/slices/agentConfig/selectors.ts +54 -0
- package/src/store/session/slices/chat/action.ts +210 -0
- package/src/store/session/slices/chat/index.ts +3 -0
- package/src/store/session/slices/chat/initialState.ts +12 -0
- package/src/store/session/slices/chat/messageReducer.test.ts +70 -0
- package/src/store/session/slices/chat/messageReducer.ts +84 -0
- package/src/store/session/slices/chat/selectors.ts +83 -0
- package/src/store/session/slices/session/action.ts +118 -0
- package/src/store/session/slices/session/index.ts +3 -0
- package/src/store/session/slices/session/initialState.ts +31 -0
- package/src/store/session/slices/session/reducers/session.test.ts +456 -0
- package/src/store/session/slices/session/reducers/session.ts +113 -0
- package/src/store/session/slices/session/selectors/chat.ts +4 -0
- package/src/store/session/slices/session/selectors/index.ts +20 -0
- package/src/store/session/slices/session/selectors/list.ts +65 -0
- package/src/store/session/store.ts +17 -0
- package/src/store/settings/action.ts +31 -0
- package/src/store/settings/index.ts +23 -0
- package/src/store/settings/initialState.ts +25 -0
- package/src/store/settings/selectors.ts +9 -0
- package/src/store/settings/store.ts +13 -0
- package/src/styles/antdOverride.ts +29 -0
- package/src/styles/global.ts +23 -0
- package/src/styles/index.ts +6 -0
- package/src/types/chatMessage.ts +46 -0
- package/src/types/exportConfig.ts +23 -0
- package/src/types/global.d.ts +14 -0
- package/src/types/i18next.d.ts +8 -0
- package/src/types/langchain.ts +34 -0
- package/src/types/llm.ts +49 -0
- package/src/types/locale.ts +7 -0
- package/src/types/meta.ts +26 -0
- package/src/types/openai.ts +62 -0
- package/src/types/session.ts +59 -0
- package/src/utils/VersionController.test.ts +90 -0
- package/src/utils/VersionController.ts +64 -0
- package/src/utils/compass.ts +94 -0
- package/src/utils/fetch.ts +132 -0
- package/src/utils/filter.test.ts +120 -0
- package/src/utils/filter.ts +29 -0
- package/src/utils/uploadFIle.ts +8 -0
- package/src/utils/uuid.ts +9 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { memo, useEffect } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { createI18nNext } from '@/locales/create';
|
|
7
|
+
import { Sessions } from '@/pages/chat/SessionList';
|
|
8
|
+
|
|
9
|
+
import Sidebar from '../Sidebar';
|
|
10
|
+
import Header from './Header';
|
|
11
|
+
import SettingForm from './SettingForm';
|
|
12
|
+
|
|
13
|
+
const initI18n = createI18nNext('setting');
|
|
14
|
+
|
|
15
|
+
const SettingLayout = memo(() => {
|
|
16
|
+
const { t } = useTranslation('setting');
|
|
17
|
+
const pageTitle = `${t('header')} - LobeChat`;
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
initI18n.finally();
|
|
21
|
+
}, []);
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<Head>
|
|
25
|
+
<title>{pageTitle}</title>
|
|
26
|
+
</Head>
|
|
27
|
+
<Flexbox horizontal width={'100%'}>
|
|
28
|
+
<Sidebar />
|
|
29
|
+
<Sessions />
|
|
30
|
+
<Flexbox flex={1}>
|
|
31
|
+
<Header />
|
|
32
|
+
<Flexbox align={'center'} padding={24}>
|
|
33
|
+
<SettingForm />
|
|
34
|
+
</Flexbox>
|
|
35
|
+
</Flexbox>
|
|
36
|
+
</Flexbox>
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default SettingLayout;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { OpenAIStreamPayload } from '@/types/openai';
|
|
2
|
+
|
|
3
|
+
// 自动起名
|
|
4
|
+
export const promptSummaryAgentName = (content: string): Partial<OpenAIStreamPayload> => ({
|
|
5
|
+
messages: [
|
|
6
|
+
{
|
|
7
|
+
content: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
|
|
8
|
+
输入: {文本作为JSON引用字符串}
|
|
9
|
+
输出: {角色名}
|
|
10
|
+
`,
|
|
11
|
+
role: 'system',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
|
|
15
|
+
role: 'user',
|
|
16
|
+
},
|
|
17
|
+
{ content: '前端 vitest 测试专家', role: 'assistant' },
|
|
18
|
+
{
|
|
19
|
+
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
|
20
|
+
role: 'user',
|
|
21
|
+
},
|
|
22
|
+
{ content: 'js 转 ts 专家', role: 'assistant' },
|
|
23
|
+
{
|
|
24
|
+
content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
|
|
25
|
+
输入:页面加载中
|
|
26
|
+
输出:页面似乎在思考,一会儿才能准备好}`,
|
|
27
|
+
role: 'user',
|
|
28
|
+
},
|
|
29
|
+
{ content: '文案比喻优化专家', role: 'assistant' },
|
|
30
|
+
{ content: `输入: {${content}}`, role: 'user' },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 自动挑选 emoji 和背景色
|
|
35
|
+
export const promptPickEmoji = (content: string): Partial<OpenAIStreamPayload> => ({
|
|
36
|
+
messages: [
|
|
37
|
+
{
|
|
38
|
+
content: `你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。
|
|
39
|
+
输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。
|
|
40
|
+
输出: 💅
|
|
41
|
+
|
|
42
|
+
输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。
|
|
43
|
+
输出: 🧪
|
|
44
|
+
`,
|
|
45
|
+
role: 'system',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
content: `输入:${content}`,
|
|
49
|
+
role: 'user',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const promptSummaryDescription = (content: string): Partial<OpenAIStreamPayload> => ({
|
|
55
|
+
messages: [
|
|
56
|
+
{
|
|
57
|
+
content: '你是一名擅长会话的助理,你需要将用户的提示词做一个3句话以内的总结。',
|
|
58
|
+
role: 'system',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
content: `输入:${content}`,
|
|
62
|
+
role: 'user',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { merge } from 'lodash-es';
|
|
2
|
+
|
|
3
|
+
import type { OpenAIStreamPayload } from '@/types/openai';
|
|
4
|
+
|
|
5
|
+
import { URLS } from './url';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 专门用于对话的 fetch
|
|
9
|
+
*/
|
|
10
|
+
export const fetchChatModel = (
|
|
11
|
+
params: Partial<OpenAIStreamPayload>,
|
|
12
|
+
signal?: AbortSignal | undefined,
|
|
13
|
+
) => {
|
|
14
|
+
const payload = merge(
|
|
15
|
+
{
|
|
16
|
+
frequency_penalty: 0,
|
|
17
|
+
model: 'gpt-3.5-turbo',
|
|
18
|
+
presence_penalty: 0,
|
|
19
|
+
stream: true,
|
|
20
|
+
temperature: 0.6,
|
|
21
|
+
top_p: 1,
|
|
22
|
+
},
|
|
23
|
+
params,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return fetch(URLS.openai, {
|
|
27
|
+
body: JSON.stringify(payload),
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
method: 'POST',
|
|
32
|
+
signal,
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { URLS } from '@/services/url';
|
|
2
|
+
import { LangChainParams } from '@/types/langchain';
|
|
3
|
+
import { fetchAIFactory } from '@/utils/fetch';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 专门用于 FlowChain 的 fetch
|
|
7
|
+
*/
|
|
8
|
+
export const fetchLangChain = fetchAIFactory(
|
|
9
|
+
(params: LangChainParams, signal?: AbortSignal | undefined) =>
|
|
10
|
+
fetch(URLS.chain, {
|
|
11
|
+
body: JSON.stringify(params),
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
},
|
|
15
|
+
method: 'POST',
|
|
16
|
+
signal,
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isEmpty } from 'lodash-es';
|
|
2
|
+
import { PersistStorage } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
import { Compressor } from '@/utils/compass';
|
|
5
|
+
|
|
6
|
+
export const createHashStorage = <T extends object>(): PersistStorage<T> => {
|
|
7
|
+
return {
|
|
8
|
+
getItem: async () => {
|
|
9
|
+
const searchParameters = new URLSearchParams(location.hash.slice(1));
|
|
10
|
+
const state: any = {};
|
|
11
|
+
const pool = [...searchParameters.entries()].map(async ([k, v]) => {
|
|
12
|
+
const string_ = await Compressor.decompressAsync(v);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
state[k] = JSON.parse(string_);
|
|
16
|
+
} catch {
|
|
17
|
+
state[k] = string_;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await Promise.all(pool);
|
|
22
|
+
|
|
23
|
+
return { state } as any;
|
|
24
|
+
},
|
|
25
|
+
removeItem: (key): void => {
|
|
26
|
+
const searchParameters = new URLSearchParams(location.hash.slice(1));
|
|
27
|
+
searchParameters.delete(key);
|
|
28
|
+
location.hash = searchParameters.toString();
|
|
29
|
+
},
|
|
30
|
+
setItem: async (_, newValue) => {
|
|
31
|
+
const searchParameters = new URLSearchParams(location.hash.slice(1));
|
|
32
|
+
|
|
33
|
+
const pool = Object.entries(newValue.state).map(async ([k, v]) => {
|
|
34
|
+
if (isEmpty(v)) {
|
|
35
|
+
searchParameters.delete(k);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (typeof v === 'string') {
|
|
39
|
+
searchParameters.set(k, await Compressor.compressAsync(v));
|
|
40
|
+
} else {
|
|
41
|
+
searchParameters.set(k, await Compressor.compressAsync(JSON.stringify(v)));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
await Promise.all(pool);
|
|
45
|
+
|
|
46
|
+
location.hash = searchParameters.toString();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { PersistOptions, devtools, persist } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
import { SessionStore, createStore } from './store';
|
|
5
|
+
|
|
6
|
+
type SessionPersist = Pick<SessionStore, 'sessions'>;
|
|
7
|
+
|
|
8
|
+
export const LOBE_CHAT = 'LOBE_CHAT';
|
|
9
|
+
|
|
10
|
+
const persistOptions: PersistOptions<SessionStore, SessionPersist> = {
|
|
11
|
+
name: LOBE_CHAT,
|
|
12
|
+
|
|
13
|
+
partialize: (s) => ({
|
|
14
|
+
sessions: s.sessions,
|
|
15
|
+
}),
|
|
16
|
+
|
|
17
|
+
// 手动控制 Hydration ,避免 ssr 报错
|
|
18
|
+
skipHydration: true,
|
|
19
|
+
version: 0,
|
|
20
|
+
// version: Migration.targetVersion,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const useSessionStore = create<SessionStore>()(
|
|
24
|
+
persist(
|
|
25
|
+
devtools(createStore, {
|
|
26
|
+
name: LOBE_CHAT,
|
|
27
|
+
}),
|
|
28
|
+
persistOptions,
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export * from './selectors';
|
|
33
|
+
export type { SessionStore } from './store';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AgentConfigState, initialAgentConfigState } from './slices/agentConfig';
|
|
2
|
+
import { ChatState, initialChatState } from './slices/chat';
|
|
3
|
+
import { SessionState, initialSessionState } from './slices/session';
|
|
4
|
+
|
|
5
|
+
export type SessionStoreState = SessionState & ChatState & AgentConfigState;
|
|
6
|
+
|
|
7
|
+
export const initialState: SessionStoreState = {
|
|
8
|
+
...initialSessionState,
|
|
9
|
+
...initialChatState,
|
|
10
|
+
...initialAgentConfigState,
|
|
11
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
|
|
3
|
+
import { promptPickEmoji, promptSummaryAgentName, promptSummaryDescription } from '@/prompts/agent';
|
|
4
|
+
import { SessionStore, sessionSelectors } from '@/store/session';
|
|
5
|
+
import { MetaData } from '@/types/meta';
|
|
6
|
+
import { LobeAgentConfig } from '@/types/session';
|
|
7
|
+
import { fetchPresetTaskResult } from '@/utils/fetch';
|
|
8
|
+
|
|
9
|
+
import { SessionLoadingState } from './initialState';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 代理行为接口
|
|
13
|
+
*/
|
|
14
|
+
export interface AgentAction {
|
|
15
|
+
/**
|
|
16
|
+
* 自动选择表情
|
|
17
|
+
* @param id - 表情的 ID
|
|
18
|
+
*/
|
|
19
|
+
autoPickEmoji: (id: string) => void;
|
|
20
|
+
/**
|
|
21
|
+
* 自动完成代理描述
|
|
22
|
+
* @param id - 代理的 ID
|
|
23
|
+
* @returns 一个 Promise,用于异步操作完成后的处理
|
|
24
|
+
*/
|
|
25
|
+
autocompleteAgentDescription: (id: string) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 自动完成代理标题
|
|
28
|
+
* @param id - 代理的 ID
|
|
29
|
+
* @returns 一个 Promise,用于异步操作完成后的处理
|
|
30
|
+
*/
|
|
31
|
+
autocompleteAgentTitle: (id: string) => Promise<void>;
|
|
32
|
+
|
|
33
|
+
autocompleteMeta: (key: keyof MetaData) => void;
|
|
34
|
+
/**
|
|
35
|
+
* 自动完成会话代理元数据
|
|
36
|
+
* @param id - 代理的 ID
|
|
37
|
+
*/
|
|
38
|
+
autocompleteSessionAgentMeta: (id: string, replace?: boolean) => void;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 内部更新代理元数据
|
|
42
|
+
* @param id - 代理的 ID
|
|
43
|
+
* @returns 任意类型的返回值
|
|
44
|
+
*/
|
|
45
|
+
internalUpdateAgentMeta: (id: string) => any;
|
|
46
|
+
/**
|
|
47
|
+
* 切换配置
|
|
48
|
+
* @param showPanel - 是否显示面板,默认为 true
|
|
49
|
+
*/
|
|
50
|
+
toggleConfig: (showPanel?: boolean) => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 更新代理配置
|
|
54
|
+
* @param config - 部分 LobeAgentConfig 的配置
|
|
55
|
+
*/
|
|
56
|
+
updateAgentConfig: (config: Partial<LobeAgentConfig>) => void;
|
|
57
|
+
updateAgentMeta: (meta: Partial<MetaData>) => void;
|
|
58
|
+
/**
|
|
59
|
+
* 更新加载状态
|
|
60
|
+
* @param key - SessionLoadingState 的键
|
|
61
|
+
* @param value - 加载状态的值
|
|
62
|
+
*/
|
|
63
|
+
updateLoadingState: (key: keyof SessionLoadingState, value: boolean) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const createAgentSlice: StateCreator<
|
|
67
|
+
SessionStore,
|
|
68
|
+
[['zustand/devtools', never]],
|
|
69
|
+
[],
|
|
70
|
+
AgentAction
|
|
71
|
+
> = (set, get) => ({
|
|
72
|
+
autoPickEmoji: async (id) => {
|
|
73
|
+
const { dispatchSession } = get();
|
|
74
|
+
const session = sessionSelectors.getSessionById(id)(get());
|
|
75
|
+
if (!session) return;
|
|
76
|
+
|
|
77
|
+
const systemRole = session.config.systemRole;
|
|
78
|
+
|
|
79
|
+
const emoji = await fetchPresetTaskResult({
|
|
80
|
+
onLoadingChange: (loading) => {
|
|
81
|
+
get().updateLoadingState('avatar', loading);
|
|
82
|
+
},
|
|
83
|
+
params: promptPickEmoji(systemRole),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (emoji) {
|
|
87
|
+
dispatchSession({ id, key: 'avatar', type: 'updateSessionMeta', value: emoji });
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
autocompleteAgentDescription: async (id) => {
|
|
92
|
+
const { dispatchSession, updateLoadingState, internalUpdateAgentMeta } = get();
|
|
93
|
+
const session = sessionSelectors.getSessionById(id)(get());
|
|
94
|
+
if (!session) return;
|
|
95
|
+
|
|
96
|
+
const systemRole = session.config.systemRole;
|
|
97
|
+
|
|
98
|
+
if (!systemRole) return;
|
|
99
|
+
|
|
100
|
+
const preValue = session.meta.description;
|
|
101
|
+
|
|
102
|
+
// 替换为 ...
|
|
103
|
+
dispatchSession({ id, key: 'description', type: 'updateSessionMeta', value: '...' });
|
|
104
|
+
|
|
105
|
+
fetchPresetTaskResult({
|
|
106
|
+
onError: () => {
|
|
107
|
+
dispatchSession({
|
|
108
|
+
id,
|
|
109
|
+
key: 'description',
|
|
110
|
+
type: 'updateSessionMeta',
|
|
111
|
+
value: preValue,
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
onLoadingChange: (loading) => {
|
|
115
|
+
updateLoadingState('description', loading);
|
|
116
|
+
},
|
|
117
|
+
onMessageHandle: internalUpdateAgentMeta(id)('description'),
|
|
118
|
+
params: promptSummaryDescription(systemRole),
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
autocompleteAgentTitle: async (id) => {
|
|
123
|
+
const { dispatchSession, updateLoadingState, internalUpdateAgentMeta } = get();
|
|
124
|
+
const session = sessionSelectors.getSessionById(id)(get());
|
|
125
|
+
if (!session) return;
|
|
126
|
+
|
|
127
|
+
const systemRole = session.config.systemRole;
|
|
128
|
+
|
|
129
|
+
if (!systemRole) return;
|
|
130
|
+
|
|
131
|
+
const previousTitle = session.meta.title;
|
|
132
|
+
|
|
133
|
+
// 替换为 ...
|
|
134
|
+
dispatchSession({ id, key: 'title', type: 'updateSessionMeta', value: '...' });
|
|
135
|
+
|
|
136
|
+
fetchPresetTaskResult({
|
|
137
|
+
onError: () => {
|
|
138
|
+
dispatchSession({ id, key: 'title', type: 'updateSessionMeta', value: previousTitle });
|
|
139
|
+
},
|
|
140
|
+
onLoadingChange: (loading) => {
|
|
141
|
+
updateLoadingState('title', loading);
|
|
142
|
+
},
|
|
143
|
+
onMessageHandle: internalUpdateAgentMeta(id)('title'),
|
|
144
|
+
params: promptSummaryAgentName(systemRole),
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
autocompleteMeta: (key) => {
|
|
149
|
+
const { activeId, autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
|
|
150
|
+
if (!activeId) return;
|
|
151
|
+
|
|
152
|
+
switch (key) {
|
|
153
|
+
case 'avatar': {
|
|
154
|
+
autoPickEmoji(activeId);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case 'description': {
|
|
159
|
+
autocompleteAgentDescription(activeId);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case 'title': {
|
|
164
|
+
autocompleteAgentTitle(activeId);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
autocompleteSessionAgentMeta: (id, replace) => {
|
|
170
|
+
const session = sessionSelectors.getSessionById(id)(get());
|
|
171
|
+
|
|
172
|
+
if (!session) return;
|
|
173
|
+
|
|
174
|
+
if (!session.meta.title || replace) {
|
|
175
|
+
get().autocompleteAgentTitle(id);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!session.meta.description || replace) {
|
|
179
|
+
get().autocompleteAgentDescription(id);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!session.meta.avatar || replace) {
|
|
183
|
+
get().autoPickEmoji(id);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
internalUpdateAgentMeta: (id: string) => (key: keyof MetaData) => {
|
|
187
|
+
let value = '';
|
|
188
|
+
return (text: string) => {
|
|
189
|
+
value += text;
|
|
190
|
+
get().dispatchSession({ id, key, type: 'updateSessionMeta', value });
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
toggleConfig: (newValue) => {
|
|
195
|
+
const showAgentSettings = typeof newValue === 'boolean' ? newValue : !get().showAgentSettings;
|
|
196
|
+
|
|
197
|
+
set({ showAgentSettings });
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
updateAgentConfig: (config) => {
|
|
201
|
+
const { activeId } = get();
|
|
202
|
+
const session = sessionSelectors.currentSession(get());
|
|
203
|
+
if (!activeId || !session) return;
|
|
204
|
+
|
|
205
|
+
get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
|
|
206
|
+
},
|
|
207
|
+
updateAgentMeta: (meta) => {
|
|
208
|
+
const { activeId } = get();
|
|
209
|
+
const session = sessionSelectors.currentSession(get());
|
|
210
|
+
if (!activeId || !session) return;
|
|
211
|
+
|
|
212
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
213
|
+
if (value !== undefined) {
|
|
214
|
+
get().dispatchSession({
|
|
215
|
+
id: activeId,
|
|
216
|
+
key: key as keyof MetaData,
|
|
217
|
+
type: 'updateSessionMeta',
|
|
218
|
+
value,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
updateLoadingState: (key, value) => {
|
|
224
|
+
set({ autocompleteLoading: { ...get().autocompleteLoading, [key]: value } });
|
|
225
|
+
},
|
|
226
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LanguageModel } from '@/types/llm';
|
|
2
|
+
import { MetaData } from '@/types/meta';
|
|
3
|
+
import { LobeAgentConfig } from '@/types/session';
|
|
4
|
+
|
|
5
|
+
export type SessionLoadingState = Record<Partial<keyof MetaData>, boolean>;
|
|
6
|
+
|
|
7
|
+
export interface AgentConfigState {
|
|
8
|
+
autocompleteLoading: SessionLoadingState;
|
|
9
|
+
|
|
10
|
+
showAgentSettings: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const initialLobeAgentConfig: LobeAgentConfig = {
|
|
14
|
+
model: LanguageModel.GPT3_5,
|
|
15
|
+
params: { temperature: 0.6 },
|
|
16
|
+
systemRole: '',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_AVATAR = 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_TITLE = '默认对话';
|
|
22
|
+
|
|
23
|
+
export const initialAgentConfigState: AgentConfigState = {
|
|
24
|
+
// // loading 中间态
|
|
25
|
+
autocompleteLoading: {
|
|
26
|
+
avatar: false,
|
|
27
|
+
backgroundColor: false,
|
|
28
|
+
description: false,
|
|
29
|
+
tag: false,
|
|
30
|
+
title: false,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
showAgentSettings: false,
|
|
34
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SessionStore } from '@/store/session';
|
|
2
|
+
import { LanguageModel } from '@/types/llm';
|
|
3
|
+
import { MetaData } from '@/types/meta';
|
|
4
|
+
import { LobeAgentConfig } from '@/types/session';
|
|
5
|
+
|
|
6
|
+
import { sessionSelectors } from '../session';
|
|
7
|
+
import { DEFAULT_AVATAR, initialLobeAgentConfig } from './initialState';
|
|
8
|
+
|
|
9
|
+
const currentAgentMeta = (s: SessionStore): MetaData => {
|
|
10
|
+
const session = sessionSelectors.currentSession(s);
|
|
11
|
+
|
|
12
|
+
return session?.meta || {};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title;
|
|
16
|
+
|
|
17
|
+
const currentAgentAvatar = (s: SessionStore) => {
|
|
18
|
+
const session = sessionSelectors.currentSession(s);
|
|
19
|
+
|
|
20
|
+
if (!session) return DEFAULT_AVATAR;
|
|
21
|
+
|
|
22
|
+
return session.meta.avatar || DEFAULT_AVATAR;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const currentAgentConfig = (s: SessionStore) => {
|
|
26
|
+
const session = sessionSelectors.currentSession(s);
|
|
27
|
+
|
|
28
|
+
return session?.config;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const currentAgentConfigSafe = (s: SessionStore): LobeAgentConfig => {
|
|
32
|
+
return currentAgentConfig(s) || initialLobeAgentConfig;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const currentAgentModel = (s: SessionStore): LanguageModel => {
|
|
36
|
+
const config = currentAgentConfig(s);
|
|
37
|
+
|
|
38
|
+
return config?.model || LanguageModel.GPT3_5;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const hasSystemRole = (s: SessionStore) => {
|
|
42
|
+
const config = currentAgentConfigSafe(s);
|
|
43
|
+
|
|
44
|
+
return !!config.systemRole;
|
|
45
|
+
};
|
|
46
|
+
export const agentSelectors = {
|
|
47
|
+
currentAgentAvatar,
|
|
48
|
+
currentAgentConfig,
|
|
49
|
+
currentAgentConfigSafe,
|
|
50
|
+
currentAgentMeta,
|
|
51
|
+
currentAgentModel,
|
|
52
|
+
currentAgentTitle,
|
|
53
|
+
hasSystemRole,
|
|
54
|
+
};
|