@lobehub/chat 1.1.16 → 1.1.18

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 (121) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/plugin.json +1 -0
  3. package/locales/ar/portal.json +4 -0
  4. package/locales/bg-BG/plugin.json +1 -0
  5. package/locales/bg-BG/portal.json +4 -0
  6. package/locales/de-DE/plugin.json +1 -0
  7. package/locales/de-DE/portal.json +4 -0
  8. package/locales/en-US/plugin.json +1 -0
  9. package/locales/en-US/portal.json +4 -0
  10. package/locales/es-ES/plugin.json +1 -0
  11. package/locales/es-ES/portal.json +4 -0
  12. package/locales/fr-FR/plugin.json +1 -0
  13. package/locales/fr-FR/portal.json +4 -0
  14. package/locales/it-IT/plugin.json +1 -0
  15. package/locales/it-IT/portal.json +4 -0
  16. package/locales/ja-JP/plugin.json +1 -0
  17. package/locales/ja-JP/portal.json +4 -0
  18. package/locales/ko-KR/plugin.json +1 -0
  19. package/locales/ko-KR/portal.json +4 -0
  20. package/locales/nl-NL/plugin.json +1 -0
  21. package/locales/nl-NL/portal.json +4 -0
  22. package/locales/pl-PL/plugin.json +1 -0
  23. package/locales/pl-PL/portal.json +4 -0
  24. package/locales/pt-BR/plugin.json +1 -0
  25. package/locales/pt-BR/portal.json +4 -0
  26. package/locales/ru-RU/plugin.json +1 -0
  27. package/locales/ru-RU/portal.json +4 -0
  28. package/locales/tr-TR/plugin.json +1 -0
  29. package/locales/tr-TR/portal.json +4 -0
  30. package/locales/vi-VN/plugin.json +1 -0
  31. package/locales/vi-VN/portal.json +4 -0
  32. package/locales/zh-CN/plugin.json +1 -0
  33. package/locales/zh-CN/portal.json +4 -0
  34. package/locales/zh-TW/plugin.json +1 -0
  35. package/locales/zh-TW/portal.json +4 -0
  36. package/package.json +1 -1
  37. package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +17 -0
  38. package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +18 -0
  39. package/src/app/(main)/chat/(workspace)/@portal/default.tsx +27 -0
  40. package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +52 -0
  41. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/index.tsx +74 -0
  42. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/style.ts +46 -0
  43. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/index.tsx +39 -0
  44. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/Footer.tsx +33 -0
  45. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/ToolRender.tsx +50 -0
  46. package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/index.tsx +37 -0
  47. package/src/app/(main)/chat/(workspace)/@portal/index.tsx +18 -0
  48. package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +79 -0
  49. package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +27 -22
  50. package/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx +3 -1
  51. package/src/app/(main)/chat/(workspace)/_layout/type.ts +1 -0
  52. package/src/app/(main)/chat/loading.tsx +2 -20
  53. package/src/components/CircleLoading/index.tsx +21 -0
  54. package/src/config/modelProviders/google.ts +44 -61
  55. package/src/const/layoutTokens.test.ts +11 -0
  56. package/src/const/layoutTokens.ts +4 -0
  57. package/src/const/message.ts +0 -14
  58. package/src/database/client/models/__tests__/message.test.ts +9 -12
  59. package/src/database/client/models/message.ts +6 -0
  60. package/src/database/server/models/__tests__/message.test.ts +70 -0
  61. package/src/database/server/models/message.ts +10 -0
  62. package/src/database/server/schemas/lobechat.ts +3 -1
  63. package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +3 -17
  64. package/src/features/Conversation/Messages/Tool/Inspector/index.tsx +26 -2
  65. package/src/features/Conversation/Messages/Tool/index.tsx +33 -6
  66. package/src/features/Conversation/Messages/components/Arguments.tsx +1 -1
  67. package/src/features/PluginAvatar/index.tsx +28 -0
  68. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +44 -0
  69. package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/index.tsx +8 -1
  70. package/src/features/{Conversation/Plugins → PluginsUI}/Render/index.tsx +12 -1
  71. package/src/{features/Conversation/Messages/hooks → hooks}/useYamlArguments.ts +3 -1
  72. package/src/locales/default/index.ts +2 -0
  73. package/src/locales/default/plugin.ts +1 -0
  74. package/src/locales/default/portal.ts +4 -0
  75. package/src/server/routers/lambda/message.ts +12 -0
  76. package/src/services/message/client.test.ts +35 -0
  77. package/src/services/message/client.ts +6 -0
  78. package/src/services/message/server.ts +9 -0
  79. package/src/store/chat/initialState.ts +10 -1
  80. package/src/store/chat/selectors.ts +1 -0
  81. package/src/store/chat/slices/message/action.test.ts +1 -1
  82. package/src/store/chat/slices/message/action.ts +5 -5
  83. package/src/store/chat/slices/message/reducer.test.ts +230 -7
  84. package/src/store/chat/slices/message/reducer.ts +45 -22
  85. package/src/store/chat/slices/message/selectors.test.ts +133 -2
  86. package/src/store/chat/slices/message/selectors.ts +7 -0
  87. package/src/store/chat/slices/plugin/action.test.ts +309 -2
  88. package/src/store/chat/slices/plugin/action.ts +51 -1
  89. package/src/store/chat/slices/portal/action.test.ts +109 -0
  90. package/src/store/chat/slices/portal/action.ts +31 -0
  91. package/src/store/chat/slices/portal/initialState.ts +8 -0
  92. package/src/store/chat/slices/portal/selectors.test.ts +73 -0
  93. package/src/store/chat/slices/portal/selectors.ts +15 -0
  94. package/src/store/chat/store.ts +7 -1
  95. package/src/store/tool/selectors/tool.test.ts +14 -0
  96. package/src/store/tool/selectors/tool.ts +13 -0
  97. package/src/tools/docks.ts +3 -0
  98. package/src/types/tool/builtin.ts +11 -1
  99. package/src/utils/safeParseJSON.test.ts +71 -0
  100. package/src/utils/safeParseJSON.ts +12 -0
  101. package/src/const/message.test.ts +0 -55
  102. package/src/features/Conversation/Plugins/Render/BuiltinType/index.tsx +0 -30
  103. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/BuiltinType/index.test.tsx +0 -0
  104. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/IFrameRender/index.tsx +0 -0
  105. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/index.tsx +0 -0
  106. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/utils.ts +0 -0
  107. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/index.tsx +0 -0
  108. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/Loading.tsx +0 -0
  109. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/MarkdownType/index.tsx +0 -0
  110. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/Iframe.tsx +0 -0
  111. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/useParseContent.ts +0 -0
  112. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.test.ts +0 -0
  113. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.ts +0 -0
  114. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.test.ts +0 -0
  115. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.ts +0 -0
  116. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.test.ts +0 -0
  117. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.ts +0 -0
  118. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.test.ts +0 -0
  119. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.ts +0 -0
  120. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/postMessage.test.ts +0 -0
  121. /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/postMessage.ts +0 -0
@@ -230,4 +230,18 @@ describe('toolSelectors', () => {
230
230
  expect(toolSelectors.getManifestLoadingStatus('non-existent')(mockState)).toBe('error');
231
231
  });
232
232
  });
233
+
234
+ describe('isToolHasUI', () => {
235
+ it('should return false if the tool has no UI', () => {
236
+ expect(toolSelectors.isToolHasUI('plugin-1')(mockState)).toBe(false);
237
+ });
238
+
239
+ it('should return true if the tool has UI', () => {
240
+ expect(toolSelectors.isToolHasUI('builtin-1')(mockState)).toBe(true);
241
+ });
242
+
243
+ it('should return false if the tool does not exist', () => {
244
+ expect(toolSelectors.isToolHasUI('non-existent')(mockState)).toBe(false);
245
+ });
246
+ });
233
247
  });
@@ -100,11 +100,24 @@ const getManifestLoadingStatus = (id: string) => (s: ToolStoreState) => {
100
100
  if (!!manifest) return 'success';
101
101
  };
102
102
 
103
+ const isToolHasUI = (id: string) => (s: ToolStoreState) => {
104
+ const manifest = getManifestById(id)(s);
105
+ if (!manifest) return false;
106
+ const builtinTool = s.builtinTools.find((tool) => tool.identifier === id);
107
+
108
+ if (builtinTool && builtinTool.type === 'builtin') {
109
+ return true;
110
+ }
111
+
112
+ return !!manifest.ui;
113
+ };
114
+
103
115
  export const toolSelectors = {
104
116
  enabledSchema,
105
117
  enabledSystemRoles,
106
118
  getManifestById,
107
119
  getManifestLoadingStatus,
108
120
  getMetaById,
121
+ isToolHasUI,
109
122
  metaList,
110
123
  };
@@ -0,0 +1,3 @@
1
+ import { BuiltinDock } from '@/types/tool';
2
+
3
+ export const BuiltinToolsDocks: Record<string, BuiltinDock> = {};
@@ -27,7 +27,8 @@ export interface LobeBuiltinTool {
27
27
  type: 'builtin';
28
28
  }
29
29
 
30
- export interface BuiltinRenderProps<Content = any, State = any> {
30
+ export interface BuiltinRenderProps<Content = any, Arguments = any, State = any> {
31
+ args: Arguments;
31
32
  content: Content;
32
33
  identifier?: string;
33
34
  messageId: string;
@@ -35,3 +36,12 @@ export interface BuiltinRenderProps<Content = any, State = any> {
35
36
  }
36
37
 
37
38
  export type BuiltinRender = <T = any>(props: BuiltinRenderProps<T>) => ReactNode;
39
+
40
+ export interface BuiltinDockProps<Arguments = Record<string, any>, State = any> {
41
+ arguments: Arguments;
42
+ identifier: string;
43
+ messageId: string;
44
+ state: State;
45
+ }
46
+
47
+ export type BuiltinDock = <T = any>(props: BuiltinDockProps<T>) => ReactNode;
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { safeParseJSON } from './safeParseJSON';
4
+
5
+ describe('safeParseJSON', () => {
6
+ it('should parse a valid JSON string', () => {
7
+ const validJSON = '{"name": "John", "age": 30}';
8
+ const result = safeParseJSON(validJSON);
9
+ expect(result).toEqual({ name: 'John', age: 30 });
10
+ });
11
+
12
+ it('should return undefined for invalid JSON', () => {
13
+ const invalidJSON = '{name: John}';
14
+ const result = safeParseJSON(invalidJSON);
15
+ expect(result).toBeUndefined();
16
+ });
17
+
18
+ it('should parse an empty object', () => {
19
+ const emptyObject = '{}';
20
+ const result = safeParseJSON(emptyObject);
21
+ expect(result).toEqual({});
22
+ });
23
+
24
+ it('should parse an array', () => {
25
+ const arrayJSON = '[1, 2, 3]';
26
+ const result = safeParseJSON(arrayJSON);
27
+ expect(result).toEqual([1, 2, 3]);
28
+ });
29
+
30
+ it('should parse nested objects', () => {
31
+ const nestedJSON = '{"user": {"name": "John", "age": 30}}';
32
+ const result = safeParseJSON(nestedJSON);
33
+ expect(result).toEqual({ user: { name: 'John', age: 30 } });
34
+ });
35
+
36
+ it('should return undefined for an empty string', () => {
37
+ const result = safeParseJSON('');
38
+ expect(result).toBeUndefined();
39
+ });
40
+
41
+ it('should return undefined for non-string input', () => {
42
+ // @ts-expect-error: Testing with invalid input type
43
+ const result = safeParseJSON(123);
44
+ expect(result).toBeUndefined();
45
+ });
46
+
47
+ it('should parse JSON with special characters', () => {
48
+ const specialJSON = '{"message": "Hello, \\"world\\"!"}';
49
+ const result = safeParseJSON(specialJSON);
50
+ expect(result).toEqual({ message: 'Hello, "world"!' });
51
+ });
52
+
53
+ it('should parse large JSON without throwing an error', () => {
54
+ const largeJSON = JSON.stringify({ data: Array(1000).fill('test') });
55
+ const result = safeParseJSON(largeJSON);
56
+ expect(result).toHaveProperty('data');
57
+ expect(Array.isArray(result!.data)).toBe(true);
58
+ expect(result!.data).toHaveLength(1000);
59
+ });
60
+
61
+ it('should handle JSON with different data types', () => {
62
+ const mixedJSON = '{"string": "text", "number": 42, "boolean": true, "null": null}';
63
+ const result = safeParseJSON(mixedJSON);
64
+ expect(result).toEqual({
65
+ string: 'text',
66
+ number: 42,
67
+ boolean: true,
68
+ null: null,
69
+ });
70
+ });
71
+ });
@@ -0,0 +1,12 @@
1
+ export const safeParseJSON = <T = Record<string, any>>(text: string) => {
2
+ if (typeof text !== 'string') return undefined;
3
+
4
+ let json: T;
5
+ try {
6
+ json = JSON.parse(text);
7
+ } catch {
8
+ return undefined;
9
+ }
10
+
11
+ return json;
12
+ };
@@ -1,55 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { testFunctionMessageAtEnd } from './message';
4
-
5
- describe('testFunctionMessageAtEnd', () => {
6
- it('should extract tool_calls JSON when present', () => {
7
- const content = 'Some content before {"tool_calls": [{"tool": "example", "args": ["arg1"]}]}';
8
- const result = testFunctionMessageAtEnd(content);
9
- expect(result).toEqual({
10
- content: '{"tool_calls": [{"tool": "example", "args": ["arg1"]}]}',
11
- valid: true,
12
- });
13
- });
14
-
15
- it('should extract tool_calls JSON when there are space at end', () => {
16
- const content =
17
- 'Some content before {"tool_calls": [{"tool": "example", "args": ["arg1"]}]} ';
18
- const result = testFunctionMessageAtEnd(content);
19
- expect(result).toEqual({
20
- content: '{"tool_calls": [{"tool": "example", "args": ["arg1"]}]}',
21
- valid: true,
22
- });
23
- });
24
-
25
- it('should not extract tool_calls JSON when in middle', () => {
26
- const content =
27
- 'Some content before {"tool_calls": [{"tool": "example", "args": ["arg1"]}]}, here are some end content';
28
- const result = testFunctionMessageAtEnd(content);
29
- expect(result).toEqual({ content: '', valid: false });
30
- });
31
-
32
- it('should return an empty string and valid false when JSON is not present', () => {
33
- const content = 'Some content without the JSON structure';
34
- const result = testFunctionMessageAtEnd(content);
35
- expect(result).toEqual({ content: '', valid: false });
36
- });
37
-
38
- it('should return an empty string and valid false when content is empty', () => {
39
- const content = '';
40
- const result = testFunctionMessageAtEnd(content);
41
-
42
- expect(result).toEqual({ content: '', valid: false });
43
- });
44
-
45
- it('should handle null or undefined content gracefully', () => {
46
- const nullContent = null;
47
- const undefinedContent = undefined;
48
-
49
- const resultWithNull = testFunctionMessageAtEnd(nullContent as any);
50
- const resultWithUndefined = testFunctionMessageAtEnd(undefinedContent as any);
51
-
52
- expect(resultWithNull).toEqual({ content: '', valid: false });
53
- expect(resultWithUndefined).toEqual({ content: '', valid: false });
54
- });
55
- });
@@ -1,30 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import { BuiltinToolsRenders } from '@/tools/renders';
4
-
5
- import Loading from '../Loading';
6
- import { useParseContent } from '../useParseContent';
7
-
8
- export interface BuiltinTypeProps {
9
- content: string;
10
- id: string;
11
- identifier?: string;
12
- loading?: boolean;
13
- pluginState?: any;
14
- }
15
-
16
- const BuiltinType = memo<BuiltinTypeProps>(({ content, pluginState, id, identifier, loading }) => {
17
- const { isJSON, data } = useParseContent(content);
18
-
19
- if (!isJSON) {
20
- return loading && <Loading />;
21
- }
22
-
23
- const Render = BuiltinToolsRenders[identifier || ''];
24
-
25
- if (!Render) return;
26
-
27
- return <Render content={data} identifier={identifier} messageId={id} pluginState={pluginState} />;
28
- });
29
-
30
- export default BuiltinType;