@lobehub/chat 1.63.2 → 1.64.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.
Files changed (151) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/models.json +25 -16
  4. package/locales/ar/plugin.json +16 -0
  5. package/locales/ar/portal.json +0 -5
  6. package/locales/ar/tool.json +18 -0
  7. package/locales/bg-BG/models.json +25 -16
  8. package/locales/bg-BG/plugin.json +16 -0
  9. package/locales/bg-BG/portal.json +0 -5
  10. package/locales/bg-BG/tool.json +18 -0
  11. package/locales/de-DE/models.json +25 -16
  12. package/locales/de-DE/plugin.json +16 -0
  13. package/locales/de-DE/portal.json +0 -5
  14. package/locales/de-DE/tool.json +18 -0
  15. package/locales/en-US/models.json +24 -15
  16. package/locales/en-US/plugin.json +16 -0
  17. package/locales/en-US/portal.json +0 -5
  18. package/locales/en-US/tool.json +18 -0
  19. package/locales/es-ES/models.json +25 -16
  20. package/locales/es-ES/plugin.json +16 -0
  21. package/locales/es-ES/portal.json +0 -5
  22. package/locales/es-ES/tool.json +18 -0
  23. package/locales/fa-IR/models.json +25 -16
  24. package/locales/fa-IR/plugin.json +16 -0
  25. package/locales/fa-IR/portal.json +0 -5
  26. package/locales/fa-IR/tool.json +18 -0
  27. package/locales/fr-FR/models.json +25 -16
  28. package/locales/fr-FR/plugin.json +16 -0
  29. package/locales/fr-FR/portal.json +0 -5
  30. package/locales/fr-FR/tool.json +18 -0
  31. package/locales/it-IT/models.json +25 -16
  32. package/locales/it-IT/plugin.json +16 -0
  33. package/locales/it-IT/portal.json +0 -5
  34. package/locales/it-IT/tool.json +18 -0
  35. package/locales/ja-JP/models.json +24 -15
  36. package/locales/ja-JP/plugin.json +16 -0
  37. package/locales/ja-JP/portal.json +0 -5
  38. package/locales/ja-JP/tool.json +18 -0
  39. package/locales/ko-KR/models.json +25 -16
  40. package/locales/ko-KR/plugin.json +16 -0
  41. package/locales/ko-KR/portal.json +0 -5
  42. package/locales/ko-KR/tool.json +18 -0
  43. package/locales/nl-NL/models.json +25 -16
  44. package/locales/nl-NL/plugin.json +16 -0
  45. package/locales/nl-NL/portal.json +0 -5
  46. package/locales/nl-NL/tool.json +18 -0
  47. package/locales/pl-PL/models.json +25 -16
  48. package/locales/pl-PL/plugin.json +16 -0
  49. package/locales/pl-PL/portal.json +0 -5
  50. package/locales/pl-PL/tool.json +18 -0
  51. package/locales/pt-BR/models.json +24 -15
  52. package/locales/pt-BR/plugin.json +16 -0
  53. package/locales/pt-BR/portal.json +0 -5
  54. package/locales/pt-BR/tool.json +18 -0
  55. package/locales/ru-RU/models.json +25 -16
  56. package/locales/ru-RU/plugin.json +16 -0
  57. package/locales/ru-RU/portal.json +0 -5
  58. package/locales/ru-RU/tool.json +18 -0
  59. package/locales/tr-TR/models.json +25 -16
  60. package/locales/tr-TR/plugin.json +16 -0
  61. package/locales/tr-TR/portal.json +0 -5
  62. package/locales/tr-TR/tool.json +18 -0
  63. package/locales/vi-VN/models.json +24 -15
  64. package/locales/vi-VN/plugin.json +16 -0
  65. package/locales/vi-VN/portal.json +0 -5
  66. package/locales/vi-VN/tool.json +18 -0
  67. package/locales/zh-CN/models.json +30 -21
  68. package/locales/zh-CN/plugin.json +16 -0
  69. package/locales/zh-CN/portal.json +1 -6
  70. package/locales/zh-CN/tool.json +19 -1
  71. package/locales/zh-TW/models.json +23 -14
  72. package/locales/zh-TW/plugin.json +16 -0
  73. package/locales/zh-TW/portal.json +0 -5
  74. package/locales/zh-TW/tool.json +18 -0
  75. package/package.json +1 -1
  76. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +1 -0
  77. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/SearchTags.tsx +17 -0
  78. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx +8 -2
  79. package/src/config/tools.ts +16 -0
  80. package/src/database/repositories/aiInfra/index.test.ts +29 -0
  81. package/src/features/ChatInput/ActionBar/Search/index.tsx +6 -15
  82. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +76 -0
  83. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/index.tsx +8 -21
  84. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +62 -50
  85. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +11 -1
  86. package/src/features/PluginsUI/Render/index.tsx +3 -0
  87. package/src/features/Portal/Plugins/Body/index.tsx +3 -7
  88. package/src/features/Portal/Plugins/Header.tsx +14 -2
  89. package/src/hooks/useAgentEnableSearch.ts +27 -0
  90. package/src/libs/agent-runtime/perplexity/index.test.ts +26 -0
  91. package/src/libs/agent-runtime/utils/streams/openai.ts +1 -1
  92. package/src/libs/trpc/client/index.ts +1 -0
  93. package/src/libs/trpc/client/tools.ts +20 -0
  94. package/src/locales/default/plugin.ts +16 -0
  95. package/src/locales/default/portal.ts +0 -5
  96. package/src/locales/default/tool.ts +18 -0
  97. package/src/server/modules/SearXNG.ts +33 -0
  98. package/src/server/routers/lambda/message.ts +11 -0
  99. package/src/server/routers/tools/__tests__/fixtures/searXNG.ts +668 -0
  100. package/src/server/routers/tools/__tests__/search.test.ts +47 -0
  101. package/src/server/routers/tools/index.ts +3 -0
  102. package/src/server/routers/tools/search.ts +38 -0
  103. package/src/services/__tests__/__snapshots__/chat.test.ts.snap +1 -0
  104. package/src/services/_auth.ts +4 -4
  105. package/src/services/chat.ts +31 -10
  106. package/src/services/message/_deprecated.ts +4 -0
  107. package/src/services/message/client.ts +4 -0
  108. package/src/services/message/server.ts +5 -5
  109. package/src/services/message/type.ts +2 -0
  110. package/src/services/search.ts +9 -0
  111. package/src/store/aiInfra/slices/aiModel/selectors.ts +12 -5
  112. package/src/store/chat/slices/builtinTool/action.ts +121 -0
  113. package/src/store/chat/slices/builtinTool/initialState.ts +2 -0
  114. package/src/store/chat/slices/builtinTool/selectors.ts +3 -0
  115. package/src/store/chat/slices/message/action.ts +11 -0
  116. package/src/store/chat/slices/plugin/action.test.ts +2 -2
  117. package/src/store/chat/slices/plugin/action.ts +2 -2
  118. package/src/store/tool/selectors/tool.ts +5 -12
  119. package/src/store/tool/slices/builtin/selectors.ts +1 -1
  120. package/src/store/user/slices/modelList/action.ts +6 -0
  121. package/src/store/user/slices/modelList/selectors/keyVaults.ts +1 -0
  122. package/src/tools/index.ts +7 -0
  123. package/src/tools/portals.ts +6 -1
  124. package/src/tools/renders.ts +3 -0
  125. package/src/{features/Portal/Plugins → tools/web-browsing/Portal}/Footer.tsx +13 -10
  126. package/src/tools/web-browsing/Portal/ResultList/SearchItem/CategoryAvatar.tsx +70 -0
  127. package/src/tools/web-browsing/Portal/ResultList/SearchItem/TitleExtra.tsx +38 -0
  128. package/src/tools/web-browsing/Portal/ResultList/SearchItem/Video.tsx +135 -0
  129. package/src/tools/web-browsing/Portal/ResultList/SearchItem/index.tsx +91 -0
  130. package/src/tools/web-browsing/Portal/ResultList/index.tsx +21 -0
  131. package/src/tools/web-browsing/Portal/index.tsx +65 -0
  132. package/src/tools/web-browsing/Render/ConfigForm/Form.tsx +110 -0
  133. package/src/tools/web-browsing/Render/ConfigForm/SearchXNGIcon.tsx +20 -0
  134. package/src/tools/web-browsing/Render/ConfigForm/index.tsx +67 -0
  135. package/src/tools/web-browsing/Render/ConfigForm/style.tsx +63 -0
  136. package/src/tools/web-browsing/Render/SearchQuery/SearchView.tsx +88 -0
  137. package/src/tools/web-browsing/Render/SearchQuery/index.tsx +61 -0
  138. package/src/tools/web-browsing/Render/SearchResult/SearchResultItem.tsx +72 -0
  139. package/src/tools/web-browsing/Render/SearchResult/ShowMore.tsx +68 -0
  140. package/src/tools/web-browsing/Render/SearchResult/index.tsx +105 -0
  141. package/src/tools/web-browsing/Render/index.tsx +57 -0
  142. package/src/tools/web-browsing/components/EngineAvatar.tsx +32 -0
  143. package/src/tools/web-browsing/components/SearchBar.tsx +134 -0
  144. package/src/tools/web-browsing/const.ts +11 -0
  145. package/src/tools/web-browsing/index.ts +102 -0
  146. package/src/types/message/chat.ts +1 -0
  147. package/src/types/message/tools.ts +10 -0
  148. package/src/types/tool/builtin.ts +2 -0
  149. package/src/types/tool/search.ts +38 -0
  150. package/src/types/user/settings/keyVaults.ts +8 -1
  151. package/src/utils/toolManifest.ts +20 -0
@@ -0,0 +1,134 @@
1
+ import { Icon, Tooltip } from '@lobehub/ui';
2
+ import { Button, Checkbox, Input, Select, Space, Typography } from 'antd';
3
+ import { SearchIcon } from 'lucide-react';
4
+ import { ReactNode, memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { useIsMobile } from '@/hooks/useIsMobile';
9
+ import { useChatStore } from '@/store/chat';
10
+ import { chatToolSelectors } from '@/store/chat/selectors';
11
+ import { SearchQuery } from '@/types/tool/search';
12
+
13
+ import { ENGINE_ICON_MAP } from '../const';
14
+ import { EngineAvatar } from './EngineAvatar';
15
+
16
+ interface SearchBarProps {
17
+ aiSummary?: boolean;
18
+ defaultEngines?: string[];
19
+ defaultQuery: string;
20
+ messageId: string;
21
+ onSearch?: (searchQuery: SearchQuery) => void;
22
+ searchAddon?: ReactNode;
23
+ tooltip?: boolean;
24
+ }
25
+
26
+ const SearchBar = memo<SearchBarProps>(
27
+ ({
28
+ defaultEngines = [],
29
+ aiSummary = true,
30
+ defaultQuery,
31
+ tooltip = true,
32
+ searchAddon,
33
+ onSearch,
34
+ messageId,
35
+ }) => {
36
+ const { t } = useTranslation('tool');
37
+ const loading = useChatStore(chatToolSelectors.isSearXNGSearching(messageId));
38
+ const [query, setQuery] = useState(defaultQuery);
39
+ const [engines, setEngines] = useState(defaultEngines);
40
+ const isMobile = useIsMobile();
41
+ const [reSearchWithSearXNG] = useChatStore((s) => [s.reSearchWithSearXNG]);
42
+
43
+ const updateAndSearch = async () => {
44
+ const data: SearchQuery = { query, searchEngines: engines };
45
+ onSearch?.(data);
46
+ await reSearchWithSearXNG(messageId, data, { aiSummary });
47
+ };
48
+
49
+ const searchButton = (
50
+ <Button
51
+ icon={<Icon icon={SearchIcon} />}
52
+ loading={loading}
53
+ onClick={updateAndSearch}
54
+ type={'primary'}
55
+ >
56
+ {isMobile ? undefined : t('search.searchBar.button')}
57
+ </Button>
58
+ );
59
+
60
+ return (
61
+ <Flexbox gap={16}>
62
+ <Flexbox align={'center'} flex={1} gap={8} height={32} horizontal>
63
+ <Space.Compact style={{ width: '100%' }}>
64
+ <Input
65
+ autoFocus
66
+ onChange={(e) => {
67
+ setQuery(e.target.value);
68
+ }}
69
+ onPressEnter={updateAndSearch}
70
+ placeholder={t('search.searchBar.placeholder')}
71
+ style={{ minWidth: isMobile ? undefined : 400 }}
72
+ value={query}
73
+ variant={'filled'}
74
+ />
75
+ {tooltip ? (
76
+ <Tooltip title={t('search.searchBar.tooltip')}>{searchButton}</Tooltip>
77
+ ) : (
78
+ searchButton
79
+ )}
80
+ </Space.Compact>
81
+ {searchAddon}
82
+ </Flexbox>
83
+
84
+ {isMobile ? (
85
+ <Select
86
+ mode={'multiple'}
87
+ onChange={(checkedValue) => {
88
+ setEngines(checkedValue);
89
+ }}
90
+ optionRender={(item) => (
91
+ <Flexbox align={'center'} gap={8} horizontal>
92
+ <EngineAvatar engine={item.value as string} />
93
+ {item.value}
94
+ </Flexbox>
95
+ )}
96
+ options={Object.keys(ENGINE_ICON_MAP).map((item) => ({
97
+ label: (
98
+ <Flexbox align={'center'} gap={8} horizontal>
99
+ <EngineAvatar engine={item} />
100
+ </Flexbox>
101
+ ),
102
+ value: item,
103
+ }))}
104
+ size={'small'}
105
+ value={engines}
106
+ variant={'filled'}
107
+ />
108
+ ) : (
109
+ <Flexbox align={'flex-start'} gap={8} horizontal>
110
+ <Typography.Text style={{ marginTop: 2, wordBreak: 'keep-all' }} type={'secondary'}>
111
+ {t('search.searchEngine')}
112
+ </Typography.Text>
113
+ <Checkbox.Group
114
+ onChange={(checkedValue) => {
115
+ setEngines(checkedValue);
116
+ }}
117
+ options={Object.keys(ENGINE_ICON_MAP).map((item) => ({
118
+ label: (
119
+ <Flexbox align={'center'} gap={8} horizontal>
120
+ <EngineAvatar engine={item} />
121
+ {item}
122
+ </Flexbox>
123
+ ),
124
+ value: item,
125
+ }))}
126
+ value={engines}
127
+ />
128
+ </Flexbox>
129
+ )}
130
+ </Flexbox>
131
+ );
132
+ },
133
+ );
134
+ export default SearchBar;
@@ -0,0 +1,11 @@
1
+ export const ENGINE_ICON_MAP: Record<string, string> = {
2
+ arxiv: 'https://arxiv.org/static/browse/0.3.4/images/icons/favicon-32x32.png',
3
+ bilibili: 'https://www.bilibili.com/favicon.ico',
4
+ bing: 'https://www.bing.com/favicon.ico',
5
+ brave: 'https://brave.com/static-assets/images/brave-favicon.png',
6
+ duckduckgo: 'https://www.duckduckgo.com/favicon.ico',
7
+ google: 'https://www.google.com/favicon.ico',
8
+ npm: 'https://static-production.npmjs.com/da3ab40fb0861d15c83854c29f5f2962.png',
9
+ qwant: 'https://www.qwant.com/favicon.ico',
10
+ youtube: 'https://www.youtube.com/favicon.ico',
11
+ };
@@ -0,0 +1,102 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ import { BuiltinToolManifest } from '@/types/tool';
4
+
5
+ export const WebBrowsingManifest: BuiltinToolManifest = {
6
+ api: [
7
+ {
8
+ description:
9
+ 'A meta search engine. Useful for when you need to answer questions about current events. Input should be a search query. Output is a JSON array of the query results',
10
+ name: 'searchWithSearXNG',
11
+ parameters: {
12
+ properties: {
13
+ query: {
14
+ description: 'The search query',
15
+ type: 'string',
16
+ },
17
+ searchEngines: {
18
+ description: 'The search engine you can use:',
19
+ items: {
20
+ enum: [
21
+ 'google',
22
+ 'bilibili',
23
+ 'bing',
24
+ 'duckduckgo',
25
+ 'npm',
26
+ 'pypi',
27
+ 'github',
28
+ 'arxiv',
29
+ 'google scholar',
30
+ 'z-library',
31
+ 'reddit',
32
+ 'imdb',
33
+ 'brave',
34
+ 'wikipedia',
35
+ 'pinterest',
36
+ 'unsplash',
37
+ 'vimeo',
38
+ 'youtube',
39
+ ],
40
+ type: 'string',
41
+ },
42
+ type: 'array',
43
+ },
44
+ },
45
+ required: ['query'],
46
+ type: 'object',
47
+ },
48
+ },
49
+ ],
50
+ identifier: 'lobe-web-browsing',
51
+ meta: {
52
+ avatar: '🌐',
53
+ title: 'Web Browsing',
54
+ },
55
+ systemRole: `You are a search tool that uses SearxNG, a meta search engine. When given a search query, you will return relevant results from various search engines. Your results will be in JSON format, containing titles, links, and snippets of relevant web pages. If no good results are found, you will say so. You can also provide answers, infoboxes, or suggestions if available.
56
+
57
+ SearXNG combine with these engines:
58
+ - Google: The world's most popular search engine, offering a wide range of general web results.,
59
+ - Bilibili: A Chinese video sharing website themed around animation, comic, and games (ACG) ,(又名 B 站),
60
+ - Bing: Microsoft's search engine, providing web results with a focus on visual search.,
61
+ - DuckDuckGo: A privacy-focused search engine that doesn't track users.,
62
+ - npm: The package manager for JavaScript, used to find Node.js packages.,
63
+ - PyPI: The Python Package Index, used to find Python packages.,
64
+ - GitHub: A platform for version control and collaboration, search for code repositories.,
65
+ - arXiv: A repository of electronic preprints for scientific papers.,
66
+ - Google Scholar: A freely accessible web search engine for scholarly literature.,
67
+ - Z-Library: A shadow library project for file-sharing access to scholarly journal articles and books.,
68
+ - Reddit: A network of communities based on people's interests, search for discussions and content.,
69
+ - IMDb: An online database of information related to films, TV programs, and video games.,
70
+ - Brave: A privacy-focused browser with its own search engine.,
71
+ - Wikipedia: A free online encyclopedia, search for articles on various topics.,
72
+ - Pinterest: An image sharing and social media service, search for images and ideas.,
73
+ - Unsplash: A website dedicated to sharing stock photography, search for high-quality images.,
74
+ - Vimeo: A video hosting, sharing, and services platform.,
75
+ - YouTube: A video sharing platform, search for a wide variety of video content.
76
+
77
+ SearXNG comes with a search syntax by with you can modify the categories, engines, languages and more.
78
+
79
+ ## \`!\` select engine and category
80
+
81
+ To set category and/or engine names use a \`!\` prefix. To give a few examples:
82
+
83
+ - search in category **map** for **paris**
84
+ - \`!map paris\`
85
+ - image search
86
+ - \`!images Wau Holland\`
87
+
88
+ Abbreviations of the engines and languages are also accepted. Engine/category modifiers are chain able and inclusive. E.g. with \`!map !ddg !wp paris\` search in map category and DuckDuckGo and Wikipedia for **paris**.
89
+
90
+ ## \`:\` select language
91
+
92
+ To select language filter use a \`:\` prefix. To give an example:
93
+
94
+ - search Wikipedia by a custom language
95
+ - \`:fr !wp Wau Holland\`
96
+
97
+ You need to summarize in the language of the user's question. If you use search content in your reply, you must use Markdown footnote format to indicate the source, Such as [^1].
98
+
99
+ current date: ${dayjs(new Date()).format('YYYY-MM-DD')}
100
+ `,
101
+ type: 'builtin',
102
+ };
@@ -92,6 +92,7 @@ export interface ChatMessage {
92
92
  parentId?: string;
93
93
 
94
94
  plugin?: ChatPluginPayload;
95
+ pluginError?: any;
95
96
  pluginState?: any;
96
97
  /**
97
98
  * quoted other message's id
@@ -1,3 +1,4 @@
1
+ import { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
1
2
  import { DeepPartial } from 'utility-types';
2
3
  import { z } from 'zod';
3
4
 
@@ -63,3 +64,12 @@ export const MessageToolCallSchema = z.object({
63
64
  id: z.string(),
64
65
  type: z.string(),
65
66
  });
67
+
68
+ /**
69
+ * 聊天消息错误对象
70
+ */
71
+ export interface ChatMessagePluginError {
72
+ body?: any;
73
+ message: string;
74
+ type: IPluginErrorType;
75
+ }
@@ -22,6 +22,7 @@ export interface BuiltinToolManifest {
22
22
  }
23
23
 
24
24
  export interface LobeBuiltinTool {
25
+ hidden?: boolean;
25
26
  identifier: string;
26
27
  manifest: BuiltinToolManifest;
27
28
  type: 'builtin';
@@ -32,6 +33,7 @@ export interface BuiltinRenderProps<Content = any, Arguments = any, State = any>
32
33
  content: Content;
33
34
  identifier?: string;
34
35
  messageId: string;
36
+ pluginError?: any;
35
37
  pluginState?: State;
36
38
  }
37
39
 
@@ -0,0 +1,38 @@
1
+ export interface SearchQuery {
2
+ query: string;
3
+ searchEngines?: string[];
4
+ }
5
+ export const SEARCH_SEARXNG_NOT_CONFIG = 'SearXNG is not configured';
6
+
7
+ export interface SearchResponse {
8
+ answers: any[];
9
+ corrections: any[];
10
+ infoboxes: any[];
11
+ number_of_results: number;
12
+ query: string;
13
+ results: SearchResult[];
14
+ suggestions: string[];
15
+ unresponsive_engines: any[];
16
+ }
17
+
18
+ export interface SearchResult {
19
+ category: string;
20
+ content?: string;
21
+ engine: string;
22
+ engines: string[];
23
+ iframe_src?: string;
24
+ parsed_url: string[];
25
+ positions: number[];
26
+ publishedDate?: string | null;
27
+ score: number;
28
+ template: string;
29
+ thumbnail?: string | null;
30
+ title: string;
31
+ url: string;
32
+ }
33
+
34
+ export interface SearchContent {
35
+ content?: string;
36
+ title: string;
37
+ url: string;
38
+ }
@@ -25,7 +25,14 @@ export interface CloudflareKeyVault {
25
25
  baseURLOrAccountID?: string;
26
26
  }
27
27
 
28
- export interface UserKeyVaults {
28
+ export interface SearchEngineKeyVaults {
29
+ searchxng?: {
30
+ apiKey?: string;
31
+ baseURL?: string;
32
+ };
33
+ }
34
+
35
+ export interface UserKeyVaults extends SearchEngineKeyVaults {
29
36
  ai21?: OpenAICompatibleKeyVault;
30
37
  ai360?: OpenAICompatibleKeyVault;
31
38
  anthropic?: OpenAICompatibleKeyVault;
@@ -1,7 +1,10 @@
1
1
  import { LobeChatPluginManifest, pluginManifestSchema } from '@lobehub/chat-plugin-sdk';
2
+ import { uniqBy } from 'lodash-es';
2
3
 
3
4
  import { API_ENDPOINTS } from '@/services/_url';
5
+ import { ChatCompletionTool } from '@/types/openai/chat';
4
6
  import { OpenAIPluginManifest } from '@/types/openai/plugin';
7
+ import { genToolCallingName } from '@/utils/toolCall';
5
8
 
6
9
  const fetchJSON = async <T = any>(url: string, proxy = false): Promise<T> => {
7
10
  // 2. 发送请求
@@ -122,3 +125,20 @@ export const getToolManifest = async (
122
125
 
123
126
  return data;
124
127
  };
128
+
129
+ /**
130
+ *
131
+ */
132
+ export const convertPluginManifestToToolsCalling = (
133
+ manifests: LobeChatPluginManifest[],
134
+ ): ChatCompletionTool[] => {
135
+ const list = manifests.flatMap((manifest) =>
136
+ manifest.api.map((m) => ({
137
+ description: m.description,
138
+ name: genToolCallingName(manifest.identifier, m.name, manifest.type),
139
+ parameters: m.parameters,
140
+ })),
141
+ );
142
+
143
+ return uniqBy(list, 'name').map((i) => ({ function: i, type: 'function' }));
144
+ };