@lobehub/chat 1.68.11 → 1.69.1

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 (90) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +8 -0
  4. package/locales/bg-BG/chat.json +8 -0
  5. package/locales/de-DE/chat.json +8 -0
  6. package/locales/en-US/chat.json +8 -0
  7. package/locales/es-ES/chat.json +8 -0
  8. package/locales/fa-IR/chat.json +8 -0
  9. package/locales/fr-FR/chat.json +8 -0
  10. package/locales/it-IT/chat.json +8 -0
  11. package/locales/ja-JP/chat.json +8 -0
  12. package/locales/ko-KR/chat.json +8 -0
  13. package/locales/nl-NL/chat.json +8 -0
  14. package/locales/pl-PL/chat.json +8 -0
  15. package/locales/pt-BR/chat.json +8 -0
  16. package/locales/ru-RU/chat.json +8 -0
  17. package/locales/tr-TR/chat.json +8 -0
  18. package/locales/vi-VN/chat.json +8 -0
  19. package/locales/zh-CN/chat.json +8 -0
  20. package/locales/zh-TW/chat.json +8 -0
  21. package/next.config.ts +6 -0
  22. package/package.json +1 -1
  23. package/packages/web-crawler/src/crawImpl/naive.ts +19 -12
  24. package/packages/web-crawler/src/urlRules.ts +9 -1
  25. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +9 -18
  26. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +2 -5
  27. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +3 -2
  28. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +56 -30
  29. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/HistoryLimitTags.tsx +26 -0
  30. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/{SearchTags.tsx → Tags/SearchTags.tsx} +7 -4
  31. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/{Tags.tsx → Tags/index.tsx} +4 -1
  32. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/index.tsx +1 -1
  33. package/src/config/aiModels/anthropic.ts +16 -1
  34. package/src/config/aiModels/google.ts +0 -1
  35. package/src/config/aiModels/groq.ts +14 -0
  36. package/src/config/aiModels/novita.ts +36 -0
  37. package/src/config/aiModels/siliconcloud.ts +18 -2
  38. package/src/config/modelProviders/anthropic.ts +0 -2
  39. package/src/const/layoutTokens.test.ts +1 -1
  40. package/src/const/layoutTokens.ts +1 -1
  41. package/src/const/models.ts +27 -0
  42. package/src/features/ChatInput/ActionBar/History.tsx +6 -3
  43. package/src/features/ChatInput/ActionBar/Model/ContextCachingSwitch.tsx +20 -0
  44. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +49 -7
  45. package/src/features/ChatInput/ActionBar/Model/ReasoningTokenSlider.tsx +6 -14
  46. package/src/features/ChatInput/ActionBar/Search/ModelBuiltinSearch.tsx +2 -2
  47. package/src/features/ChatInput/ActionBar/Search/SwitchPanel.tsx +2 -2
  48. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +3 -5
  49. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -0
  50. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +5 -1
  51. package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -0
  52. package/src/features/Conversation/components/ChatItem/index.tsx +3 -6
  53. package/src/features/Portal/Thread/Chat/ChatItem.tsx +4 -9
  54. package/src/hooks/useAgentEnableSearch.ts +2 -2
  55. package/src/libs/agent-runtime/anthropic/index.test.ts +36 -7
  56. package/src/libs/agent-runtime/anthropic/index.ts +30 -8
  57. package/src/libs/agent-runtime/azureOpenai/index.ts +4 -9
  58. package/src/libs/agent-runtime/azureai/index.ts +4 -9
  59. package/src/libs/agent-runtime/openai/index.ts +21 -38
  60. package/src/libs/agent-runtime/types/chat.ts +4 -0
  61. package/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +55 -0
  62. package/src/libs/agent-runtime/utils/anthropicHelpers.ts +37 -3
  63. package/src/libs/langchain/loaders/code/__tests__/long.json +2 -2
  64. package/src/libs/langchain/loaders/code/__tests__/long.txt +1 -1
  65. package/src/locales/default/chat.ts +8 -0
  66. package/src/store/agent/initialState.ts +2 -2
  67. package/src/store/agent/selectors.ts +1 -1
  68. package/src/store/agent/slices/chat/{selectors.test.ts → selectors/agent.test.ts} +2 -2
  69. package/src/store/agent/slices/chat/{selectors.ts → selectors/agent.ts} +24 -33
  70. package/src/store/agent/slices/chat/selectors/chatConfig.test.ts +184 -0
  71. package/src/store/agent/slices/chat/selectors/chatConfig.ts +65 -0
  72. package/src/store/agent/slices/chat/selectors/index.ts +2 -0
  73. package/src/store/agent/store.ts +2 -2
  74. package/src/store/chat/helpers.test.ts +7 -7
  75. package/src/store/chat/helpers.ts +11 -7
  76. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +3 -3
  77. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +11 -2
  78. package/src/store/chat/slices/aiChat/actions/helpers.ts +6 -2
  79. package/src/store/chat/slices/builtinTool/actions/searXNG.ts +28 -20
  80. package/src/store/chat/slices/message/selectors.ts +7 -3
  81. package/src/store/chat/slices/thread/selectors/index.ts +7 -3
  82. package/src/tools/web-browsing/Render/PageContent/Result.tsx +4 -2
  83. package/src/tools/web-browsing/Render/index.tsx +2 -0
  84. package/src/types/agent/index.ts +4 -0
  85. package/src/types/aiModel.ts +1 -1
  86. package/src/types/aiProvider.ts +60 -31
  87. /package/packages/web-crawler/src/{__test__ → __tests__}/crawler.test.ts +0 -0
  88. /package/packages/web-crawler/src/crawImpl/{__test__ → __tests__}/jina.test.ts +0 -0
  89. /package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/{KnowledgeTag.tsx → Tags/KnowledgeTag.tsx} +0 -0
  90. /package/src/store/agent/slices/chat/{__snapshots__/selectors.test.ts.snap → selectors/__snapshots__/agent.test.ts.snap} +0 -0
@@ -8,7 +8,7 @@ import { messageService } from '@/services/message';
8
8
  import { sessionService } from '@/services/session';
9
9
  import { topicService } from '@/services/topic';
10
10
  import { useAgentStore } from '@/store/agent';
11
- import { agentSelectors } from '@/store/agent/selectors';
11
+ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
12
12
  import { chatSelectors } from '@/store/chat/selectors';
13
13
  import { messageMapKey } from '@/store/chat/utils/messageMapKey';
14
14
  import { sessionMetaSelectors } from '@/store/session/selectors';
@@ -79,7 +79,7 @@ beforeEach(() => {
79
79
  vi.clearAllMocks();
80
80
  useChatStore.setState(mockState, false);
81
81
  vi.spyOn(agentSelectors, 'currentAgentConfig').mockImplementation(() => DEFAULT_AGENT_CONFIG);
82
- vi.spyOn(agentSelectors, 'currentAgentChatConfig').mockImplementation(
82
+ vi.spyOn(agentChatConfigSelectors, 'currentChatConfig').mockImplementation(
83
83
  () => DEFAULT_AGENT_CHAT_CONFIG,
84
84
  );
85
85
  vi.spyOn(sessionMetaSelectors, 'currentAgentMeta').mockImplementation(() => ({ tags: [] }));
@@ -285,7 +285,7 @@ describe('chatMessage actions', () => {
285
285
  (messageService.createMessage as Mock).mockResolvedValue('new-message-id');
286
286
 
287
287
  // Mock agent config to simulate auto-create topic behavior
288
- (agentSelectors.currentAgentChatConfig as Mock).mockImplementation(() => ({
288
+ (agentChatConfigSelectors.currentChatConfig as Mock).mockImplementation(() => ({
289
289
  autoCreateTopicThreshold,
290
290
  enableAutoCreateTopic,
291
291
  }));
@@ -21,7 +21,12 @@ import { MessageSemanticSearchChunk } from '@/types/rag';
21
21
  import { setNamespace } from '@/utils/storeDebug';
22
22
 
23
23
  import { chatSelectors, topicSelectors } from '../../../selectors';
24
- import { getAgentChatConfig, getAgentConfig, getAgentKnowledge } from './helpers';
24
+ import {
25
+ getAgentChatConfig,
26
+ getAgentConfig,
27
+ getAgentEnableHistoryCount,
28
+ getAgentKnowledge,
29
+ } from './helpers';
25
30
 
26
31
  const n = setNamespace('ai');
27
32
 
@@ -392,7 +397,11 @@ export const generateAIChat: StateCreator<
392
397
  // ================================== //
393
398
 
394
399
  // 1. slice messages with config
395
- let preprocessMsgs = chatHelpers.getSlicedMessagesWithConfig(messages, chatConfig, true);
400
+ let preprocessMsgs = chatHelpers.getSlicedMessages(messages, {
401
+ includeNewUserMessage: true,
402
+ enableHistoryCount: getAgentEnableHistoryCount(),
403
+ historyCount: chatConfig.historyCount,
404
+ });
396
405
 
397
406
  // 2. replace inputMessage template
398
407
  preprocessMsgs = !chatConfig.inputTemplate
@@ -1,9 +1,13 @@
1
1
  import { useAgentStore } from '@/store/agent';
2
- import { agentSelectors } from '@/store/agent/selectors';
2
+ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
3
3
 
4
4
  export const getAgentConfig = () => agentSelectors.currentAgentConfig(useAgentStore.getState());
5
+
5
6
  export const getAgentChatConfig = () =>
6
- agentSelectors.currentAgentChatConfig(useAgentStore.getState());
7
+ agentChatConfigSelectors.currentChatConfig(useAgentStore.getState());
8
+
9
+ export const getAgentEnableHistoryCount = () =>
10
+ agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
7
11
 
8
12
  export const getAgentKnowledge = () =>
9
13
  agentSelectors.currentEnabledKnowledge(useAgentStore.getState());
@@ -52,29 +52,37 @@ export const searchSlice: StateCreator<
52
52
  crawlMultiPages: async (id, params, aiSummary = true) => {
53
53
  const { internal_updateMessageContent } = get();
54
54
  get().toggleSearchLoading(id, true);
55
- const response = await searchService.crawlPages(params.urls);
55
+ try {
56
+ const response = await searchService.crawlPages(params.urls);
56
57
 
57
- await get().updatePluginState(id, response);
58
- get().toggleSearchLoading(id, false);
59
- const { results } = response;
60
-
61
- if (!results) return;
62
-
63
- const content = results.map((item) =>
64
- 'errorMessage' in item
65
- ? item
66
- : {
67
- ...item.data,
68
- // if crawl too many content
69
- // slice the top 10000 char
70
- content: item.data.content?.slice(0, CRAWL_CONTENT_LIMITED_COUNT),
71
- },
72
- );
58
+ await get().updatePluginState(id, response);
59
+ get().toggleSearchLoading(id, false);
60
+ const { results } = response;
73
61
 
74
- await internal_updateMessageContent(id, JSON.stringify(content));
62
+ if (!results) return;
75
63
 
76
- // if aiSummary is true, then trigger ai message
77
- return aiSummary;
64
+ const content = results.map((item) =>
65
+ 'errorMessage' in item
66
+ ? item
67
+ : {
68
+ ...item.data,
69
+ // if crawl too many content
70
+ // slice the top 10000 char
71
+ content: item.data.content?.slice(0, CRAWL_CONTENT_LIMITED_COUNT),
72
+ },
73
+ );
74
+
75
+ await internal_updateMessageContent(id, JSON.stringify(content));
76
+
77
+ // if aiSummary is true, then trigger ai message
78
+ return aiSummary;
79
+ } catch (e) {
80
+ const err = e as Error;
81
+ console.error(e);
82
+ const content = [{ ...err, errorMessage: err.message, errorType: err.name }];
83
+
84
+ await internal_updateMessageContent(id, JSON.stringify(content));
85
+ }
78
86
  },
79
87
 
80
88
  crawlSinglePage: async (id, params, aiSummary) => {
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_USER_AVATAR } from '@/const/meta';
2
2
  import { INBOX_SESSION_ID } from '@/const/session';
3
3
  import { useAgentStore } from '@/store/agent';
4
- import { agentSelectors } from '@/store/agent/selectors';
4
+ import { agentChatConfigSelectors } from '@/store/agent/selectors';
5
5
  import { messageMapKey } from '@/store/chat/utils/messageMapKey';
6
6
  import { useSessionStore } from '@/store/session';
7
7
  import { sessionMetaSelectors } from '@/store/session/selectors';
@@ -84,9 +84,13 @@ const mainAIChats = (s: ChatStoreState): ChatMessage[] => {
84
84
 
85
85
  const mainAIChatsWithHistoryConfig = (s: ChatStoreState): ChatMessage[] => {
86
86
  const chats = mainAIChats(s);
87
- const config = agentSelectors.currentAgentChatConfig(useAgentStore.getState());
87
+ const enableHistoryCount = agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
88
+ const historyCount = agentChatConfigSelectors.historyCount(useAgentStore.getState());
88
89
 
89
- return chatHelpers.getSlicedMessagesWithConfig(chats, config);
90
+ return chatHelpers.getSlicedMessages(chats, {
91
+ enableHistoryCount,
92
+ historyCount,
93
+ });
90
94
  };
91
95
 
92
96
  const mainAIChatsMessageString = (s: ChatStoreState): string => {
@@ -1,6 +1,6 @@
1
1
  import { THREAD_DRAFT_ID } from '@/const/message';
2
2
  import { useAgentStore } from '@/store/agent';
3
- import { agentSelectors } from '@/store/agent/selectors';
3
+ import { agentChatConfigSelectors } from '@/store/agent/selectors';
4
4
  import type { ChatStoreState } from '@/store/chat';
5
5
  import { chatHelpers } from '@/store/chat/helpers';
6
6
  import { ChatMessage } from '@/types/message';
@@ -123,9 +123,13 @@ const portalAIChatsWithHistoryConfig = (s: ChatStoreState) => {
123
123
 
124
124
  const messages = [...parentMessages, ...afterMessages].filter(Boolean) as ChatMessage[];
125
125
 
126
- const config = agentSelectors.currentAgentChatConfig(useAgentStore.getState());
126
+ const enableHistoryCount = agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
127
+ const historyCount = agentChatConfigSelectors.historyCount(useAgentStore.getState());
127
128
 
128
- return chatHelpers.getSlicedMessagesWithConfig(messages, config);
129
+ return chatHelpers.getSlicedMessages(messages, {
130
+ enableHistoryCount,
131
+ historyCount,
132
+ });
129
133
  };
130
134
 
131
135
  const threadSourceMessageIndex = (s: ChatStoreState) => {
@@ -51,12 +51,13 @@ const useStyles = createStyles(({ token, css }) => {
51
51
  }
52
52
  `,
53
53
  footer: css`
54
- padding: ${token.paddingXS}px;
54
+ padding-block: 8px;
55
+ padding-inline: 16px;
55
56
  text-align: center;
56
57
  background-color: ${token.colorFillQuaternary};
57
58
  `,
58
59
  footerText: css`
59
- font-size: ${token.fontSizeSM}px;
60
+ font-size: 12px !important;
60
61
  color: ${token.colorTextTertiary} !important;
61
62
  `,
62
63
  metaInfo: css`
@@ -153,6 +154,7 @@ const CrawlerResultCard = memo<CrawlerData>(({ result, messageId, crawler, origi
153
154
  <Descriptions
154
155
  classNames={{
155
156
  content: styles.footerText,
157
+ label: styles.footerText,
156
158
  }}
157
159
  column={2}
158
160
  items={[
@@ -45,4 +45,6 @@ const WebBrowsing = memo<BuiltinRenderProps<SearchContent[]>>(
45
45
  },
46
46
  );
47
47
 
48
+ WebBrowsing.displayName = 'WebBrowsing';
49
+
48
50
  export default WebBrowsing;
@@ -75,6 +75,10 @@ export interface LobeAgentChatConfig {
75
75
  enableReasoningEffort?: boolean;
76
76
  reasoningBudgetToken?: number;
77
77
 
78
+ /**
79
+ * 禁用上下文缓存
80
+ */
81
+ disableContextCaching?: boolean;
78
82
  /**
79
83
  * 历史消息条数
80
84
  */
@@ -140,7 +140,7 @@ export interface AiModelConfig {
140
140
 
141
141
  export type ModelSearchImplementType = 'tool' | 'params' | 'internal';
142
142
 
143
- export type ExtendParamsType = 'reasoningBudgetToken' | 'enableReasoning';
143
+ export type ExtendParamsType = 'reasoningBudgetToken' | 'enableReasoning' | 'disableContextCaching';
144
144
 
145
145
  export interface AiModelSettings {
146
146
  extendParams?: ExtendParamsType[];
@@ -32,37 +32,6 @@ export const AiProviderSDKEnum = {
32
32
 
33
33
  export type AiProviderSDKType = (typeof AiProviderSDKEnum)[keyof typeof AiProviderSDKEnum];
34
34
 
35
- // create
36
- export const CreateAiProviderSchema = z.object({
37
- config: z.object({}).passthrough().optional(),
38
- description: z.string().optional(),
39
- id: z.string(),
40
- keyVaults: z.any().optional(),
41
- logo: z.string().optional(),
42
- name: z.string(),
43
- sdkType: z.enum(['openai', 'anthropic']).optional(),
44
- source: z.enum(['builtin', 'custom']),
45
- // checkModel: z.string().optional(),
46
- // homeUrl: z.string().optional(),
47
- // modelsUrl: z.string().optional(),
48
- });
49
-
50
- export type CreateAiProviderParams = z.infer<typeof CreateAiProviderSchema>;
51
-
52
- // List Query
53
-
54
- export interface AiProviderListItem {
55
- description?: string;
56
- enabled: boolean;
57
- id: string;
58
- logo?: string;
59
- name?: string;
60
- sort?: number;
61
- source: AiProviderSourceType;
62
- }
63
-
64
- // Detail Query
65
-
66
35
  export interface AiProviderSettings {
67
36
  /**
68
37
  * whether provider show browser request option by default
@@ -115,6 +84,65 @@ export interface AiProviderSettings {
115
84
  smoothing?: SmoothingParams;
116
85
  }
117
86
 
87
+ const AiProviderSettingsSchema = z.object({
88
+ defaultShowBrowserRequest: z.boolean().optional(),
89
+ disableBrowserRequest: z.boolean().optional(),
90
+ modelEditable: z.boolean().optional(),
91
+ proxyUrl: z
92
+ .object({
93
+ desc: z.string().optional(),
94
+ placeholder: z.string(),
95
+ title: z.string().optional(),
96
+ })
97
+ .or(z.literal(false))
98
+ .optional(),
99
+ sdkType: z.enum(['anthropic', 'openai', 'ollama']).optional(),
100
+ searchMode: z.enum(['params', 'internal']).optional(),
101
+ showAddNewModel: z.boolean().optional(),
102
+ showApiKey: z.boolean().optional(),
103
+ showChecker: z.boolean().optional(),
104
+ showDeployName: z.boolean().optional(),
105
+ showModelFetcher: z.boolean().optional(),
106
+ smoothing: z
107
+ .object({
108
+ text: z.boolean().optional(),
109
+ toolsCalling: z.boolean().optional(),
110
+ })
111
+ .optional(),
112
+ });
113
+
114
+ // create
115
+ export const CreateAiProviderSchema = z.object({
116
+ config: z.object({}).passthrough().optional(),
117
+ description: z.string().optional(),
118
+ id: z.string(),
119
+ keyVaults: z.any().optional(),
120
+ logo: z.string().optional(),
121
+ name: z.string(),
122
+ sdkType: z.enum(['openai', 'anthropic']).optional(),
123
+ settings: AiProviderSettingsSchema.optional(),
124
+ source: z.enum(['builtin', 'custom']),
125
+ // checkModel: z.string().optional(),
126
+ // homeUrl: z.string().optional(),
127
+ // modelsUrl: z.string().optional(),
128
+ });
129
+
130
+ export type CreateAiProviderParams = z.infer<typeof CreateAiProviderSchema>;
131
+
132
+ // List Query
133
+
134
+ export interface AiProviderListItem {
135
+ description?: string;
136
+ enabled: boolean;
137
+ id: string;
138
+ logo?: string;
139
+ name?: string;
140
+ sort?: number;
141
+ source: AiProviderSourceType;
142
+ }
143
+
144
+ // Detail Query
145
+
118
146
  export interface AiProviderCard {
119
147
  /**
120
148
  * the default model that used for connection check
@@ -174,6 +202,7 @@ export const UpdateAiProviderSchema = z.object({
174
202
  logo: z.string().nullable().optional(),
175
203
  name: z.string(),
176
204
  sdkType: z.enum(['openai', 'anthropic']).optional(),
205
+ settings: AiProviderSettingsSchema.optional(),
177
206
  });
178
207
 
179
208
  export type UpdateAiProviderParams = z.infer<typeof UpdateAiProviderSchema>;