@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.
- package/CHANGELOG.md +50 -0
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/portal.json +4 -0
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/portal.json +4 -0
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/portal.json +4 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/en-US/portal.json +4 -0
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/portal.json +4 -0
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/portal.json +4 -0
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/portal.json +4 -0
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/portal.json +4 -0
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/portal.json +4 -0
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/portal.json +4 -0
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/portal.json +4 -0
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/portal.json +4 -0
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/portal.json +4 -0
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/portal.json +4 -0
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/portal.json +4 -0
- package/locales/zh-CN/plugin.json +1 -0
- package/locales/zh-CN/portal.json +4 -0
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/portal.json +4 -0
- package/package.json +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +17 -0
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +18 -0
- package/src/app/(main)/chat/(workspace)/@portal/default.tsx +27 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +52 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/index.tsx +74 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/style.ts +46 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/index.tsx +39 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/Footer.tsx +33 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/ToolRender.tsx +50 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/index.tsx +37 -0
- package/src/app/(main)/chat/(workspace)/@portal/index.tsx +18 -0
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +79 -0
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +27 -22
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx +3 -1
- package/src/app/(main)/chat/(workspace)/_layout/type.ts +1 -0
- package/src/app/(main)/chat/loading.tsx +2 -20
- package/src/components/CircleLoading/index.tsx +21 -0
- package/src/config/modelProviders/google.ts +44 -61
- package/src/const/layoutTokens.test.ts +11 -0
- package/src/const/layoutTokens.ts +4 -0
- package/src/const/message.ts +0 -14
- package/src/database/client/models/__tests__/message.test.ts +9 -12
- package/src/database/client/models/message.ts +6 -0
- package/src/database/server/models/__tests__/message.test.ts +70 -0
- package/src/database/server/models/message.ts +10 -0
- package/src/database/server/schemas/lobechat.ts +3 -1
- package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +3 -17
- package/src/features/Conversation/Messages/Tool/Inspector/index.tsx +26 -2
- package/src/features/Conversation/Messages/Tool/index.tsx +33 -6
- package/src/features/Conversation/Messages/components/Arguments.tsx +1 -1
- package/src/features/PluginAvatar/index.tsx +28 -0
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +44 -0
- package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/index.tsx +8 -1
- package/src/features/{Conversation/Plugins → PluginsUI}/Render/index.tsx +12 -1
- package/src/{features/Conversation/Messages/hooks → hooks}/useYamlArguments.ts +3 -1
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/plugin.ts +1 -0
- package/src/locales/default/portal.ts +4 -0
- package/src/server/routers/lambda/message.ts +12 -0
- package/src/services/message/client.test.ts +35 -0
- package/src/services/message/client.ts +6 -0
- package/src/services/message/server.ts +9 -0
- package/src/store/chat/initialState.ts +10 -1
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/message/action.test.ts +1 -1
- package/src/store/chat/slices/message/action.ts +5 -5
- package/src/store/chat/slices/message/reducer.test.ts +230 -7
- package/src/store/chat/slices/message/reducer.ts +45 -22
- package/src/store/chat/slices/message/selectors.test.ts +133 -2
- package/src/store/chat/slices/message/selectors.ts +7 -0
- package/src/store/chat/slices/plugin/action.test.ts +309 -2
- package/src/store/chat/slices/plugin/action.ts +51 -1
- package/src/store/chat/slices/portal/action.test.ts +109 -0
- package/src/store/chat/slices/portal/action.ts +31 -0
- package/src/store/chat/slices/portal/initialState.ts +8 -0
- package/src/store/chat/slices/portal/selectors.test.ts +73 -0
- package/src/store/chat/slices/portal/selectors.ts +15 -0
- package/src/store/chat/store.ts +7 -1
- package/src/store/tool/selectors/tool.test.ts +14 -0
- package/src/store/tool/selectors/tool.ts +13 -0
- package/src/tools/docks.ts +3 -0
- package/src/types/tool/builtin.ts +11 -1
- package/src/utils/safeParseJSON.test.ts +71 -0
- package/src/utils/safeParseJSON.ts +12 -0
- package/src/const/message.test.ts +0 -55
- package/src/features/Conversation/Plugins/Render/BuiltinType/index.tsx +0 -30
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/BuiltinType/index.test.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/IFrameRender/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/utils.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/Loading.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/MarkdownType/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/Iframe.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/useParseContent.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/postMessage.test.ts +0 -0
- /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
|
};
|
|
@@ -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
|
+
});
|
|
@@ -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;
|
|
File without changes
|
/package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/IFrameRender/index.tsx
RENAMED
|
File without changes
|
/package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/index.tsx
RENAMED
|
File without changes
|
/package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/utils.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.test.ts
RENAMED
|
File without changes
|
|
File without changes
|
/package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.test.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|