@lobehub/chat 1.69.2 → 1.69.4

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +3 -3
  4. package/packages/web-crawler/package.json +2 -1
  5. package/packages/web-crawler/src/crawImpl/browserless.ts +2 -1
  6. package/packages/web-crawler/src/urlRules.ts +6 -0
  7. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +161 -0
  8. package/src/app/[variants]/(auth)/next-auth/signin/page.tsx +11 -0
  9. package/src/app/[variants]/(main)/profile/(home)/features/SSOProvidersList/index.tsx +1 -1
  10. package/src/{app/[variants]/(main)/profile/(home)/features/SSOProvidersList → components/NextAuth}/AuthIcons.tsx +8 -6
  11. package/src/features/Conversation/Extras/Usage/UsageDetail/ModelCard.tsx +6 -6
  12. package/src/features/Conversation/Extras/Usage/UsageDetail/index.tsx +3 -3
  13. package/src/libs/agent-runtime/mistral/index.test.ts +0 -3
  14. package/src/libs/agent-runtime/mistral/index.ts +10 -7
  15. package/src/libs/agent-runtime/utils/debugStream.ts +1 -1
  16. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +8 -2
  17. package/src/libs/next-auth/auth.config.ts +1 -0
  18. package/src/middleware.ts +1 -1
  19. package/src/services/chat.ts +19 -13
  20. package/src/store/agent/index.ts +1 -1
  21. package/src/store/agent/slices/chat/selectors/chatConfig.ts +3 -2
  22. package/src/store/agent/store.ts +2 -0
  23. package/src/store/aiInfra/index.ts +1 -1
  24. package/src/store/aiInfra/store.ts +2 -0
  25. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +13 -16
  26. package/src/store/session/index.ts +1 -1
  27. package/src/store/session/store.ts +2 -0
  28. package/src/store/tool/index.ts +1 -1
  29. package/src/store/tool/store.ts +2 -0
  30. package/src/store/user/store.ts +2 -0
  31. package/src/types/agent/chatConfig.ts +66 -0
  32. package/src/types/agent/index.ts +4 -61
  33. package/src/store/chat/slices/aiChat/actions/helpers.ts +0 -13
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.69.4](https://github.com/lobehub/lobe-chat/compare/v1.69.3...v1.69.4)
6
+
7
+ <sup>Released on **2025-03-09**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix mistral can not chat.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix mistral can not chat, closes [#6828](https://github.com/lobehub/lobe-chat/issues/6828) ([00cba71](https://github.com/lobehub/lobe-chat/commit/00cba71))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.69.3](https://github.com/lobehub/lobe-chat/compare/v1.69.2...v1.69.3)
31
+
32
+ <sup>Released on **2025-03-08**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Add login ui for next-auth.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Add login ui for next-auth, closes [#6434](https://github.com/lobehub/lobe-chat/issues/6434) ([541f275](https://github.com/lobehub/lobe-chat/commit/541f275))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.69.2](https://github.com/lobehub/lobe-chat/compare/v1.69.1...v1.69.2)
6
56
 
7
57
  <sup>Released on **2025-03-07**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix mistral can not chat."
6
+ ]
7
+ },
8
+ "date": "2025-03-09",
9
+ "version": "1.69.4"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Add login ui for next-auth."
15
+ ]
16
+ },
17
+ "date": "2025-03-08",
18
+ "version": "1.69.3"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.69.2",
3
+ "version": "1.69.4",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -122,7 +122,7 @@
122
122
  "@cyntler/react-doc-viewer": "^1.17.0",
123
123
  "@electric-sql/pglite": "0.2.13",
124
124
  "@google-cloud/vertexai": "^1.9.2",
125
- "@google/generative-ai": "^0.22.0",
125
+ "@google/generative-ai": "^0.24.0",
126
126
  "@huggingface/inference": "^2.8.1",
127
127
  "@icons-pack/react-simple-icons": "9.6.0",
128
128
  "@khmyznikov/pwa-install": "0.3.9",
@@ -165,7 +165,7 @@
165
165
  "epub2": "^3.0.2",
166
166
  "fast-deep-equal": "^3.1.3",
167
167
  "file-type": "^20.0.0",
168
- "framer-motion": "^11.16.0",
168
+ "framer-motion": "^12.0.0",
169
169
  "gpt-tokenizer": "^2.8.1",
170
170
  "html-to-text": "^9.0.5",
171
171
  "i18next": "^24.2.1",
@@ -8,6 +8,7 @@
8
8
  "@mozilla/readability": "^0.5.0",
9
9
  "happy-dom": "^17.0.0",
10
10
  "node-html-markdown": "^1.3.0",
11
- "query-string": "^9.1.1"
11
+ "query-string": "^9.1.1",
12
+ "url-join": "^5"
12
13
  }
13
14
  }
@@ -1,4 +1,5 @@
1
1
  import qs from 'query-string';
2
+ import urlJoin from 'url-join';
2
3
 
3
4
  import { CrawlImpl, CrawlSuccessResult } from '../type';
4
5
  import { htmlToMarkdown } from '../utils/htmlToMarkdown';
@@ -25,7 +26,7 @@ export const browserless: CrawlImpl = async (url, { filterOptions }) => {
25
26
 
26
27
  try {
27
28
  const res = await fetch(
28
- qs.stringifyUrl({ query: { token: BROWSERLESS_TOKEN }, url: `${BASE_URL}/content` }),
29
+ qs.stringifyUrl({ query: { token: BROWSERLESS_TOKEN }, url: urlJoin(BASE_URL, '/content') }),
29
30
  {
30
31
  body: JSON.stringify(input),
31
32
  headers: {
@@ -61,4 +61,10 @@ export const crawUrlRules: CrawlUrlRule[] = [
61
61
  },
62
62
  urlPattern: 'https://www.qiumiwu.com/standings/(.*)',
63
63
  },
64
+
65
+ // mozilla use jina
66
+ {
67
+ impls: ['jina'],
68
+ urlPattern: 'https://developer.mozilla.org(.*)',
69
+ },
64
70
  ];
@@ -0,0 +1,161 @@
1
+ 'use client';
2
+
3
+ import { LobeChat } from '@lobehub/ui/brand';
4
+ import { Button, Col, Flex, Row, Skeleton, Typography } from 'antd';
5
+ import { createStyles } from 'antd-style';
6
+ import { AuthError } from 'next-auth';
7
+ import { signIn } from 'next-auth/react';
8
+ import { useRouter, useSearchParams } from 'next/navigation';
9
+ import { memo } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+
12
+ import BrandWatermark from '@/components/BrandWatermark';
13
+ import AuthIcons from '@/components/NextAuth/AuthIcons';
14
+ import { DOCUMENTS_REFER_URL, PRIVACY_URL, TERMS_URL } from '@/const/url';
15
+ import { useUserStore } from '@/store/user';
16
+
17
+ const { Title, Paragraph } = Typography;
18
+
19
+ const useStyles = createStyles(({ css, token }) => ({
20
+ button: css`
21
+ text-transform: capitalize;
22
+ `,
23
+ container: css`
24
+ min-width: 360px;
25
+ border: 1px solid ${token.colorBorder};
26
+ border-radius: ${token.borderRadiusLG}px;
27
+ background: ${token.colorBgContainer};
28
+ `,
29
+ contentCard: css`
30
+ padding-block: 2.5rem;
31
+ padding-inline: 2rem;
32
+ `,
33
+ description: css`
34
+ margin: 0;
35
+ color: ${token.colorTextSecondary};
36
+ `,
37
+ footer: css`
38
+ padding: 1rem;
39
+ border-block-start: 1px solid ${token.colorBorder};
40
+ border-radius: 0 0 8px 8px;
41
+
42
+ color: ${token.colorTextDescription};
43
+
44
+ background: ${token.colorBgElevated};
45
+ `,
46
+ text: css`
47
+ text-align: center;
48
+ `,
49
+ title: css`
50
+ margin: 0;
51
+ color: ${token.colorTextHeading};
52
+ `,
53
+ }));
54
+
55
+ const BtnListLoading = memo(() => {
56
+ return (
57
+ <Flex gap={'small'} vertical>
58
+ <Skeleton.Button active style={{ minWidth: 300 }} />
59
+ <Skeleton.Button active style={{ minWidth: 300 }} />
60
+ <Skeleton.Button active style={{ minWidth: 300 }} />
61
+ </Flex>
62
+ );
63
+ });
64
+
65
+ /**
66
+ * Follow the implementation from AuthJS official documentation,
67
+ * but using client components.
68
+ * ref: https://authjs.dev/guides/pages/signin
69
+ */
70
+ export default memo(() => {
71
+ const { styles } = useStyles();
72
+ const { t } = useTranslation('clerk');
73
+ const router = useRouter();
74
+
75
+ const oAuthSSOProviders = useUserStore((s) => s.oAuthSSOProviders);
76
+
77
+ const searchParams = useSearchParams();
78
+
79
+ // Redirect back to the page url
80
+ const callbackUrl = searchParams.get('callbackUrl') ?? '';
81
+
82
+ const handleSignIn = async (provider: string) => {
83
+ try {
84
+ await signIn(provider, { redirectTo: callbackUrl });
85
+ } catch (error) {
86
+ // Signin can fail for a number of reasons, such as the user
87
+ // not existing, or the user not having the correct role.
88
+ // In some cases, you may want to redirect to a custom error
89
+ if (error instanceof AuthError) {
90
+ return router.push(`/next-auth/?error=${error.type}`);
91
+ }
92
+
93
+ // Otherwise if a redirects happens Next.js can handle it
94
+ // so you can just re-thrown the error and let Next.js handle it.
95
+ // Docs: https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
96
+ throw error;
97
+ }
98
+ };
99
+
100
+ const footerBtns = [
101
+ { href: DOCUMENTS_REFER_URL, id: 0, label: t('footerPageLink__help') },
102
+ { href: PRIVACY_URL, id: 1, label: t('footerPageLink__privacy') },
103
+ { href: TERMS_URL, id: 2, label: t('footerPageLink__terms') },
104
+ ];
105
+
106
+ return (
107
+ <div className={styles.container}>
108
+ <div className={styles.contentCard}>
109
+ {/* Card Body */}
110
+ <Flex gap="large" vertical>
111
+ {/* Header */}
112
+ <div className={styles.text}>
113
+ <Title className={styles.title} level={4}>
114
+ <div>
115
+ <LobeChat size={48} />
116
+ </div>
117
+ {t('signIn.start.title', { applicationName: 'LobeChat' })}
118
+ </Title>
119
+ <Paragraph className={styles.description}>{t('signIn.start.subtitle')}</Paragraph>
120
+ </div>
121
+ {/* Content */}
122
+ <Flex gap="small" vertical>
123
+ {oAuthSSOProviders ? (
124
+ oAuthSSOProviders.map((provider) => (
125
+ <Button
126
+ className={styles.button}
127
+ icon={AuthIcons(provider, 16)}
128
+ key={provider}
129
+ onClick={() => handleSignIn(provider)}
130
+ >
131
+ {provider}
132
+ </Button>
133
+ ))
134
+ ) : (
135
+ <BtnListLoading />
136
+ )}
137
+ </Flex>
138
+ </Flex>
139
+ </div>
140
+ <div className={styles.footer}>
141
+ {/* Footer */}
142
+ <Row>
143
+ <Col span={12}>
144
+ <Flex justify="left" style={{ height: '100%' }}>
145
+ <BrandWatermark />
146
+ </Flex>
147
+ </Col>
148
+ <Col offset={4} span={8}>
149
+ <Flex justify="right">
150
+ {footerBtns.map((btn) => (
151
+ <Button key={btn.id} onClick={() => router.push(btn.href)} size="small" type="text">
152
+ {btn.label}
153
+ </Button>
154
+ ))}
155
+ </Flex>
156
+ </Col>
157
+ </Row>
158
+ </div>
159
+ </div>
160
+ );
161
+ });
@@ -0,0 +1,11 @@
1
+ import { Suspense } from 'react';
2
+
3
+ import Loading from '@/components/Loading/BrandTextLoading';
4
+
5
+ import AuthSignInBox from './AuthSignInBox';
6
+
7
+ export default () => (
8
+ <Suspense fallback={<Loading />}>
9
+ <AuthSignInBox />
10
+ </Suspense>
11
+ );
@@ -10,7 +10,7 @@ import { userService } from '@/services/user';
10
10
  import { useUserStore } from '@/store/user';
11
11
  import { userProfileSelectors } from '@/store/user/selectors';
12
12
 
13
- import AuthIcons from './AuthIcons';
13
+ import AuthIcons from '@/components/NextAuth/AuthIcons';
14
14
 
15
15
  const { Item } = List;
16
16
 
@@ -12,10 +12,6 @@ import {
12
12
  } from '@lobehub/ui/icons';
13
13
  import React from 'react';
14
14
 
15
- const iconProps = {
16
- size: 32,
17
- };
18
-
19
15
  const iconComponents: { [key: string]: React.ElementType } = {
20
16
  'auth0': Auth0,
21
17
  'authelia': Authelia.Color,
@@ -29,9 +25,15 @@ const iconComponents: { [key: string]: React.ElementType } = {
29
25
  'zitadel': Zitadel.Color,
30
26
  };
31
27
 
32
- const AuthIcons = (id: string) => {
28
+ /**
29
+ * Get the auth icons component for the given id
30
+ * @param id
31
+ * @param size default is 36
32
+ * @returns
33
+ */
34
+ const AuthIcons = (id: string, size = 36) => {
33
35
  const IconComponent = iconComponents[id] || iconComponents.default;
34
- return <IconComponent {...iconProps} />;
36
+ return <IconComponent size={size}/>;
35
37
  };
36
38
 
37
39
  export default AuthIcons;
@@ -112,12 +112,6 @@ const ModelCard = memo<ModelCardProps>(({ pricing, id, provider, displayName })
112
112
  </Flexbox>
113
113
  </Tooltip>
114
114
  )}
115
- <Tooltip title={t('messages.modelCard.pricing.inputTokens', { amount: inputPrice })}>
116
- <Flexbox gap={2} horizontal>
117
- <Icon icon={ArrowUpFromDot} />
118
- {inputPrice}
119
- </Flexbox>
120
- </Tooltip>
121
115
  {pricing?.writeCacheInput && (
122
116
  <Tooltip
123
117
  title={t('messages.modelCard.pricing.writeCacheInputTokens', {
@@ -130,6 +124,12 @@ const ModelCard = memo<ModelCardProps>(({ pricing, id, provider, displayName })
130
124
  </Flexbox>
131
125
  </Tooltip>
132
126
  )}
127
+ <Tooltip title={t('messages.modelCard.pricing.inputTokens', { amount: inputPrice })}>
128
+ <Flexbox gap={2} horizontal>
129
+ <Icon icon={ArrowUpFromDot} />
130
+ {inputPrice}
131
+ </Flexbox>
132
+ </Tooltip>
133
133
  <Tooltip title={t('messages.modelCard.pricing.outputTokens', { amount: outputPrice })}>
134
134
  <Flexbox gap={2} horizontal>
135
135
  <Icon icon={ArrowDownToDot} />
@@ -138,7 +138,7 @@ const TokenDetail = memo<TokenDetailProps>(({ usage, model, provider }) => {
138
138
  </Flexbox>
139
139
  )}
140
140
  {outputDetails.length > 1 && (
141
- <>
141
+ <Flexbox gap={4}>
142
142
  <Flexbox
143
143
  align={'center'}
144
144
  gap={4}
@@ -146,12 +146,12 @@ const TokenDetail = memo<TokenDetailProps>(({ usage, model, provider }) => {
146
146
  justify={'space-between'}
147
147
  width={'100%'}
148
148
  >
149
- <div style={{ color: theme.colorTextDescription }}>
149
+ <div style={{ color: theme.colorTextDescription, fontSize: 12 }}>
150
150
  {t('messages.tokenDetails.outputTitle')}
151
151
  </div>
152
152
  </Flexbox>
153
153
  <TokenProgress data={outputDetails} showIcon />
154
- </>
154
+ </Flexbox>
155
155
  )}
156
156
  <Flexbox>
157
157
  <TokenProgress data={totalDetail} showIcon />
@@ -57,9 +57,6 @@ describe('specific LobeMistralAI tests', () => {
57
57
  messages: [{ content: 'Hello', role: 'user' }],
58
58
  model: 'open-mistral-7b',
59
59
  stream: true,
60
- stream_options: {
61
- include_usage: true,
62
- },
63
60
  temperature: 0.35,
64
61
  top_p: 1,
65
62
  },
@@ -1,8 +1,8 @@
1
+ import type { ChatModelCard } from '@/types/llm';
2
+
1
3
  import { ModelProvider } from '../types';
2
4
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
5
 
4
- import type { ChatModelCard } from '@/types/llm';
5
-
6
6
  export interface MistralModelCard {
7
7
  capabilities: {
8
8
  function_calling: boolean;
@@ -16,6 +16,9 @@ export interface MistralModelCard {
16
16
  export const LobeMistralAI = LobeOpenAICompatibleFactory({
17
17
  baseURL: 'https://api.mistral.ai/v1',
18
18
  chatCompletion: {
19
+ // Mistral API does not support stream_options: { include_usage: true }
20
+ // refs: https://github.com/lobehub/lobe-chat/issues/6825
21
+ excludeUsage: true,
19
22
  handlePayload: (payload) => ({
20
23
  ...(payload.max_tokens !== undefined && { max_tokens: payload.max_tokens }),
21
24
  messages: payload.messages as any,
@@ -33,12 +36,14 @@ export const LobeMistralAI = LobeOpenAICompatibleFactory({
33
36
  models: async ({ client }) => {
34
37
  const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
35
38
 
36
- const modelsPage = await client.models.list() as any;
39
+ const modelsPage = (await client.models.list()) as any;
37
40
  const modelList: MistralModelCard[] = modelsPage.data;
38
41
 
39
42
  return modelList
40
43
  .map((model) => {
41
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
44
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
45
+ (m) => model.id.toLowerCase() === m.id.toLowerCase(),
46
+ );
42
47
 
43
48
  return {
44
49
  contextWindowTokens: model.max_context_length,
@@ -47,9 +52,7 @@ export const LobeMistralAI = LobeOpenAICompatibleFactory({
47
52
  enabled: knownModel?.enabled || false,
48
53
  functionCall: model.capabilities.function_calling,
49
54
  id: model.id,
50
- reasoning:
51
- knownModel?.abilities?.reasoning
52
- || false,
55
+ reasoning: knownModel?.abilities?.reasoning || false,
53
56
  vision: model.capabilities.vision,
54
57
  };
55
58
  })
@@ -35,7 +35,7 @@ export const debugStream = async (stream: ReadableStream) => {
35
35
 
36
36
  console.log(`[chunk ${chunk}] ${getTime()}`);
37
37
  console.log(chunkValue);
38
- console.log(`\n`);
38
+ console.log('');
39
39
 
40
40
  finished = done;
41
41
  chunk++;
@@ -57,6 +57,7 @@ interface OpenAICompatibleFactoryOptions<T extends Record<string, any> = any> {
57
57
  apiKey?: string;
58
58
  baseURL?: string;
59
59
  chatCompletion?: {
60
+ excludeUsage?: boolean;
60
61
  handleError?: (
61
62
  error: any,
62
63
  options: ConstructorOptions<T>,
@@ -224,12 +225,17 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
224
225
  ...postPayload,
225
226
  messages,
226
227
  ...(chatCompletion?.noUserId ? {} : { user: options?.user }),
227
- stream_options: postPayload.stream ? { include_usage: true } : undefined,
228
+ stream_options:
229
+ postPayload.stream && !chatCompletion?.excludeUsage
230
+ ? { include_usage: true }
231
+ : undefined,
228
232
  };
229
233
 
230
234
  if (debug?.chatCompletion?.()) {
231
- console.log('[requestPayload]:', JSON.stringify(finalPayload, null, 2));
235
+ console.log('[requestPayload]');
236
+ console.log(JSON.stringify(finalPayload), '\n');
232
237
  }
238
+
233
239
  response = await this.client.chat.completions.create(finalPayload, {
234
240
  // https://github.com/lobehub/lobe-chat/pull/318
235
241
  headers: { Accept: '*/*', ...options?.requestHeaders },
@@ -42,6 +42,7 @@ export default {
42
42
  debug: authEnv.NEXT_AUTH_DEBUG,
43
43
  pages: {
44
44
  error: '/next-auth/error',
45
+ signIn: '/next-auth/signin',
45
46
  },
46
47
  providers: initSSOProviders(),
47
48
  secret: authEnv.NEXT_AUTH_SECRET,
package/src/middleware.ts CHANGED
@@ -36,7 +36,7 @@ export const config = {
36
36
 
37
37
  '/login(.*)',
38
38
  '/signup(.*)',
39
- '/next-auth/error',
39
+ '/next-auth/(.*)',
40
40
  // ↓ cloud ↓
41
41
  ],
42
42
  };
@@ -17,13 +17,19 @@ import {
17
17
  } from '@/libs/agent-runtime';
18
18
  import { filesPrompts } from '@/prompts/files';
19
19
  import { BuiltinSystemRolePrompts } from '@/prompts/systemRole';
20
- import { aiModelSelectors, aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
21
- import { getAgentChatConfig } from '@/store/chat/slices/aiChat/actions/helpers';
22
- import { useSessionStore } from '@/store/session';
20
+ import { getAgentStoreState } from '@/store/agent';
21
+ import { agentChatConfigSelectors } from '@/store/agent/selectors';
22
+ import {
23
+ aiModelSelectors,
24
+ aiProviderSelectors,
25
+ getAiInfraStoreState,
26
+ useAiInfraStore,
27
+ } from '@/store/aiInfra';
28
+ import { getSessionStoreState } from '@/store/session';
23
29
  import { sessionMetaSelectors } from '@/store/session/selectors';
24
- import { useToolStore } from '@/store/tool';
30
+ import { getToolStoreState } from '@/store/tool';
25
31
  import { pluginSelectors, toolSelectors } from '@/store/tool/selectors';
26
- import { useUserStore } from '@/store/user';
32
+ import { getUserStoreState, useUserStore } from '@/store/user';
27
33
  import {
28
34
  modelConfigSelectors,
29
35
  modelProviderSelectors,
@@ -46,10 +52,10 @@ import { API_ENDPOINTS } from './_url';
46
52
  const isCanUseFC = (model: string, provider: string) => {
47
53
  // TODO: remove isDeprecatedEdition condition in V2.0
48
54
  if (isDeprecatedEdition) {
49
- return modelProviderSelectors.isModelEnabledFunctionCall(model)(useUserStore.getState());
55
+ return modelProviderSelectors.isModelEnabledFunctionCall(model)(getUserStoreState());
50
56
  }
51
57
 
52
- return aiModelSelectors.isModelSupportToolUse(model, provider)(useAiInfraStore.getState());
58
+ return aiModelSelectors.isModelSupportToolUse(model, provider)(getAiInfraStoreState());
53
59
  };
54
60
 
55
61
  /**
@@ -169,7 +175,7 @@ class ChatService {
169
175
  );
170
176
 
171
177
  // =================== 0. process search =================== //
172
- const chatConfig = getAgentChatConfig();
178
+ const chatConfig = agentChatConfigSelectors.currentChatConfig(getAgentStoreState());
173
179
 
174
180
  const enabledSearch = chatConfig.searchMode !== 'off';
175
181
  const isModelHasBuiltinSearch = aiModelSelectors.isModelHasBuiltinSearch(
@@ -200,7 +206,7 @@ class ChatService {
200
206
 
201
207
  // ============ 2. preprocess tools ============ //
202
208
 
203
- let filterTools = toolSelectors.enabledSchema(pluginIds)(useToolStore.getState());
209
+ let filterTools = toolSelectors.enabledSchema(pluginIds)(getToolStoreState());
204
210
 
205
211
  // check this model can use function call
206
212
  const canUseFC = isCanUseFC(payload.model, payload.provider!);
@@ -378,7 +384,7 @@ class ChatService {
378
384
  * @param options
379
385
  */
380
386
  runPluginApi = async (params: PluginRequestPayload, options?: FetchOptions) => {
381
- const s = useToolStore.getState();
387
+ const s = getToolStoreState();
382
388
 
383
389
  const settings = pluginSelectors.getPluginSettingsById(params.identifier)(s);
384
390
  const manifest = pluginSelectors.getToolManifestById(params.identifier)(s);
@@ -537,7 +543,7 @@ class ChatService {
537
543
  const hasTools = tools && tools?.length > 0;
538
544
  const hasFC = hasTools && isCanUseFC(model, provider);
539
545
  const toolsSystemRoles =
540
- hasFC && toolSelectors.enabledSystemRoles(tools)(useToolStore.getState());
546
+ hasFC && toolSelectors.enabledSystemRoles(tools)(getToolStoreState());
541
547
 
542
548
  const injectSystemRoles = BuiltinSystemRolePrompts({
543
549
  historySummary: options?.historySummary,
@@ -565,9 +571,9 @@ class ChatService {
565
571
  };
566
572
 
567
573
  private mapTrace = (trace?: TracePayload, tag?: TraceTagMap): TracePayload => {
568
- const tags = sessionMetaSelectors.currentAgentMeta(useSessionStore.getState()).tags || [];
574
+ const tags = sessionMetaSelectors.currentAgentMeta(getSessionStoreState()).tags || [];
569
575
 
570
- const enabled = preferenceSelectors.userAllowTrace(useUserStore.getState());
576
+ const enabled = preferenceSelectors.userAllowTrace(getUserStoreState());
571
577
 
572
578
  if (!enabled) return { ...trace, enabled: false };
573
579
 
@@ -1,2 +1,2 @@
1
1
  export type { AgentStore } from './store';
2
- export { useAgentStore } from './store';
2
+ export { getAgentStoreState, useAgentStore } from './store';
@@ -1,4 +1,5 @@
1
1
  import { contextCachingModels, thinkingWithToolClaudeModels } from '@/const/models';
2
+ import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
2
3
  import { AgentStoreState } from '@/store/agent/initialState';
3
4
  import { LobeAgentChatConfig } from '@/types/agent';
4
5
 
@@ -30,10 +31,10 @@ const enableHistoryCount = (s: AgentStoreState) => {
30
31
  return chatConfig.enableHistoryCount;
31
32
  };
32
33
 
33
- const historyCount = (s: AgentStoreState) => {
34
+ const historyCount = (s: AgentStoreState): number => {
34
35
  const chatConfig = currentAgentChatConfig(s);
35
36
 
36
- return chatConfig.historyCount;
37
+ return chatConfig.historyCount || (DEFAULT_AGENT_CHAT_CONFIG.historyCount as number);
37
38
  };
38
39
 
39
40
  const displayMode = (s: AgentStoreState) => {
@@ -20,3 +20,5 @@ const createStore: StateCreator<AgentStore, [['zustand/devtools', never]]> = (..
20
20
  const devtools = createDevtools('agent');
21
21
 
22
22
  export const useAgentStore = createWithEqualityFn<AgentStore>()(devtools(createStore), shallow);
23
+
24
+ export const getAgentStoreState = () => useAgentStore.getState();
@@ -1,2 +1,2 @@
1
1
  export * from './selectors';
2
- export { useAiInfraStore } from './store';
2
+ export { getAiInfraStoreState,useAiInfraStore } from './store';
@@ -23,3 +23,5 @@ const createStore: StateCreator<AiInfraStore, [['zustand/devtools', never]]> = (
23
23
  const devtools = createDevtools('aiInfra');
24
24
 
25
25
  export const useAiInfraStore = createWithEqualityFn<AiInfraStore>()(devtools(createStore), shallow);
26
+
27
+ export const getAiInfraStoreState = () => useAiInfraStore.getState();
@@ -5,13 +5,14 @@ import { template } from 'lodash-es';
5
5
  import { StateCreator } from 'zustand/vanilla';
6
6
 
7
7
  import { LOADING_FLAT, MESSAGE_CANCEL_FLAT } from '@/const/message';
8
- import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
9
8
  import { TraceEventType, TraceNameMap } from '@/const/trace';
10
9
  import { isServerMode } from '@/const/version';
11
10
  import { knowledgeBaseQAPrompts } from '@/prompts/knowledgeBaseQA';
12
11
  import { chatService } from '@/services/chat';
13
12
  import { messageService } from '@/services/message';
14
13
  import { useAgentStore } from '@/store/agent';
14
+ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
15
+ import { getAgentStoreState } from '@/store/agent/store';
15
16
  import { chatHelpers } from '@/store/chat/helpers';
16
17
  import { ChatStore } from '@/store/chat/store';
17
18
  import { messageMapKey } from '@/store/chat/utils/messageMapKey';
@@ -21,12 +22,6 @@ import { MessageSemanticSearchChunk } from '@/types/rag';
21
22
  import { setNamespace } from '@/utils/storeDebug';
22
23
 
23
24
  import { chatSelectors, topicSelectors } from '../../../selectors';
24
- import {
25
- getAgentChatConfig,
26
- getAgentConfig,
27
- getAgentEnableHistoryCount,
28
- getAgentKnowledge,
29
- } from './helpers';
30
25
 
31
26
  const n = setNamespace('ai');
32
27
 
@@ -163,7 +158,7 @@ export const generateAIChat: StateCreator<
163
158
  threadId: activeThreadId,
164
159
  };
165
160
 
166
- const agentConfig = getAgentChatConfig();
161
+ const agentConfig = agentChatConfigSelectors.currentChatConfig(getAgentStoreState());
167
162
 
168
163
  let tempMessageId: string | undefined = undefined;
169
164
  let newTopicId: string | undefined = undefined;
@@ -288,7 +283,7 @@ export const generateAIChat: StateCreator<
288
283
  // create a new array to avoid the original messages array change
289
284
  const messages = [...originalMessages];
290
285
 
291
- const { model, provider, chatConfig } = getAgentConfig();
286
+ const { model, provider, chatConfig } = agentSelectors.currentAgentConfig(getAgentStoreState());
292
287
 
293
288
  let fileChunks: MessageSemanticSearchChunk[] | undefined;
294
289
  let ragQueryId;
@@ -312,7 +307,7 @@ export const generateAIChat: StateCreator<
312
307
  chunks,
313
308
  userQuery: lastMsg.content,
314
309
  rewriteQuery,
315
- knowledge: getAgentKnowledge(),
310
+ knowledge: agentSelectors.currentEnabledKnowledge(getAgentStoreState()),
316
311
  });
317
312
 
318
313
  // 3. add the retrieve context messages to the messages history
@@ -354,11 +349,10 @@ export const generateAIChat: StateCreator<
354
349
  }
355
350
 
356
351
  // 5. summary history if context messages is larger than historyCount
357
- const historyCount =
358
- chatConfig.historyCount || (DEFAULT_AGENT_CHAT_CONFIG.historyCount as number);
352
+ const historyCount = agentChatConfigSelectors.historyCount(getAgentStoreState());
359
353
 
360
354
  if (
361
- chatConfig.enableHistoryCount &&
355
+ agentChatConfigSelectors.enableHistoryCount(getAgentStoreState()) &&
362
356
  chatConfig.enableCompressHistory &&
363
357
  originalMessages.length > historyCount
364
358
  ) {
@@ -387,7 +381,7 @@ export const generateAIChat: StateCreator<
387
381
  n('generateMessage(start)', { messageId, messages }) as string,
388
382
  );
389
383
 
390
- const agentConfig = getAgentConfig();
384
+ const agentConfig = agentSelectors.currentAgentConfig(getAgentStoreState());
391
385
  const chatConfig = agentConfig.chatConfig;
392
386
 
393
387
  const compiler = template(chatConfig.inputTemplate, { interpolate: /{{([\S\s]+?)}}/g });
@@ -397,10 +391,13 @@ export const generateAIChat: StateCreator<
397
391
  // ================================== //
398
392
 
399
393
  // 1. slice messages with config
394
+ const historyCount = agentChatConfigSelectors.historyCount(getAgentStoreState());
395
+ const enableHistoryCount = agentChatConfigSelectors.enableHistoryCount(getAgentStoreState());
396
+
400
397
  let preprocessMsgs = chatHelpers.getSlicedMessages(messages, {
401
398
  includeNewUserMessage: true,
402
- enableHistoryCount: getAgentEnableHistoryCount(),
403
- historyCount: chatConfig.historyCount,
399
+ enableHistoryCount,
400
+ historyCount,
404
401
  });
405
402
 
406
403
  // 2. replace inputMessage template
@@ -1,2 +1,2 @@
1
1
  export type { SessionStore } from './store';
2
- export { useSessionStore } from './store';
2
+ export { getSessionStoreState,useSessionStore } from './store';
@@ -31,3 +31,5 @@ export const useSessionStore = createWithEqualityFn<SessionStore>()(
31
31
  ),
32
32
  shallow,
33
33
  );
34
+
35
+ export const getSessionStoreState = () => useSessionStore.getState();
@@ -1,2 +1,2 @@
1
1
  export * from './helpers';
2
- export { useToolStore } from './store';
2
+ export { getToolStoreState, useToolStore } from './store';
@@ -30,3 +30,5 @@ const createStore: StateCreator<ToolStore, [['zustand/devtools', never]]> = (...
30
30
  const devtools = createDevtools('tools');
31
31
 
32
32
  export const useToolStore = createWithEqualityFn<ToolStore>()(devtools(createStore), shallow);
33
+
34
+ export const getToolStoreState = () => useToolStore.getState();
@@ -40,3 +40,5 @@ export const useUserStore = createWithEqualityFn<UserStore>()(
40
40
  subscribeWithSelector(devtools(createStore)),
41
41
  shallow,
42
42
  );
43
+
44
+ export const getUserStoreState = () => useUserStore.getState();
@@ -0,0 +1,66 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
2
+ import { z } from 'zod';
3
+
4
+ import { SearchMode } from '@/types/search';
5
+
6
+ export interface WorkingModel {
7
+ model: string;
8
+ provider: string;
9
+ }
10
+
11
+ export interface LobeAgentChatConfig {
12
+ displayMode?: 'chat' | 'docs';
13
+
14
+ enableAutoCreateTopic?: boolean;
15
+ autoCreateTopicThreshold: number;
16
+
17
+ enableMaxTokens?: boolean;
18
+
19
+ /**
20
+ * 是否开启推理
21
+ */
22
+ enableReasoning?: boolean;
23
+ /**
24
+ * 自定义推理强度
25
+ */
26
+ enableReasoningEffort?: boolean;
27
+ reasoningBudgetToken?: number;
28
+
29
+ /**
30
+ * 禁用上下文缓存
31
+ */
32
+ disableContextCaching?: boolean;
33
+ /**
34
+ * 历史消息条数
35
+ */
36
+ historyCount?: number;
37
+ /**
38
+ * 开启历史记录条数
39
+ */
40
+ enableHistoryCount?: boolean;
41
+ /**
42
+ * 历史消息长度压缩阈值
43
+ */
44
+ enableCompressHistory?: boolean;
45
+
46
+ inputTemplate?: string;
47
+
48
+ searchMode?: SearchMode;
49
+ searchFCModel?: WorkingModel;
50
+ useModelBuiltinSearch?: boolean;
51
+ }
52
+ /* eslint-enable */
53
+
54
+ export const AgentChatConfigSchema = z.object({
55
+ autoCreateTopicThreshold: z.number().default(2),
56
+ displayMode: z.enum(['chat', 'docs']).optional(),
57
+ enableAutoCreateTopic: z.boolean().optional(),
58
+ enableCompressHistory: z.boolean().optional(),
59
+ enableHistoryCount: z.boolean().optional(),
60
+ enableMaxTokens: z.boolean().optional(),
61
+ enableReasoning: z.boolean().optional(),
62
+ enableReasoningEffort: z.boolean().optional(),
63
+ historyCount: z.number().optional(),
64
+ reasoningBudgetToken: z.number().optional(),
65
+ searchMode: z.enum(['off', 'on', 'auto']).optional(),
66
+ });
@@ -1,9 +1,8 @@
1
- import { z } from 'zod';
2
-
3
1
  import { FileItem } from '@/types/files';
4
2
  import { KnowledgeBaseItem } from '@/types/knowledgeBase';
5
3
  import { FewShots, LLMParams } from '@/types/llm';
6
- import { SearchMode } from '@/types/search';
4
+
5
+ import { LobeAgentChatConfig } from './chatConfig';
7
6
 
8
7
  export type TTSServer = 'openai' | 'edge' | 'microsoft';
9
8
 
@@ -55,64 +54,8 @@ export interface LobeAgentConfig {
55
54
  tts: LobeAgentTTSConfig;
56
55
  }
57
56
 
58
- /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
59
-
60
- export interface LobeAgentChatConfig {
61
- displayMode?: 'chat' | 'docs';
62
-
63
- enableAutoCreateTopic?: boolean;
64
- autoCreateTopicThreshold: number;
65
-
66
- enableMaxTokens?: boolean;
67
-
68
- /**
69
- * 是否开启推理
70
- */
71
- enableReasoning?: boolean;
72
- /**
73
- * 自定义推理强度
74
- */
75
- enableReasoningEffort?: boolean;
76
- reasoningBudgetToken?: number;
77
-
78
- /**
79
- * 禁用上下文缓存
80
- */
81
- disableContextCaching?: boolean;
82
- /**
83
- * 历史消息条数
84
- */
85
- historyCount?: number;
86
- /**
87
- * 开启历史记录条数
88
- */
89
- enableHistoryCount?: boolean;
90
- /**
91
- * 历史消息长度压缩阈值
92
- */
93
- enableCompressHistory?: boolean;
94
-
95
- inputTemplate?: string;
96
-
97
- searchMode?: SearchMode;
98
- useModelBuiltinSearch?: boolean;
99
- }
100
- /* eslint-enable */
101
-
102
- export const AgentChatConfigSchema = z.object({
103
- autoCreateTopicThreshold: z.number().default(2),
104
- displayMode: z.enum(['chat', 'docs']).optional(),
105
- enableAutoCreateTopic: z.boolean().optional(),
106
- enableCompressHistory: z.boolean().optional(),
107
- enableHistoryCount: z.boolean().optional(),
108
- enableMaxTokens: z.boolean().optional(),
109
- enableReasoning: z.boolean().optional(),
110
- enableReasoningEffort: z.boolean().optional(),
111
- historyCount: z.number().optional(),
112
- reasoningBudgetToken: z.number().optional(),
113
- searchMode: z.enum(['off', 'on', 'auto']).optional(),
114
- });
115
-
116
57
  export type LobeAgentConfigKeys =
117
58
  | keyof LobeAgentConfig
118
59
  | ['params', keyof LobeAgentConfig['params']];
60
+
61
+ export * from './chatConfig';
@@ -1,13 +0,0 @@
1
- import { useAgentStore } from '@/store/agent';
2
- import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
3
-
4
- export const getAgentConfig = () => agentSelectors.currentAgentConfig(useAgentStore.getState());
5
-
6
- export const getAgentChatConfig = () =>
7
- agentChatConfigSelectors.currentChatConfig(useAgentStore.getState());
8
-
9
- export const getAgentEnableHistoryCount = () =>
10
- agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
11
-
12
- export const getAgentKnowledge = () =>
13
- agentSelectors.currentEnabledKnowledge(useAgentStore.getState());