@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
@@ -1,6 +1,5 @@
1
1
  import { ActionIcon, Icon } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
- import isEqual from 'fast-deep-equal';
4
3
  import {
5
4
  ChevronDown,
6
5
  ChevronRight,
@@ -13,16 +12,11 @@ import { CSSProperties, memo, useState } from 'react';
13
12
  import { useTranslation } from 'react-i18next';
14
13
  import { Flexbox } from 'react-layout-kit';
15
14
 
16
- import PluginAvatar from '@/features/PluginAvatar';
17
- import { useChatStore } from '@/store/chat';
18
- import { chatSelectors } from '@/store/chat/selectors';
19
- import { pluginHelpers, useToolStore } from '@/store/tool';
20
- import { toolSelectors } from '@/store/tool/selectors';
21
15
  import { shinyTextStylish } from '@/styles/loading';
22
16
 
23
17
  import Debug from './Debug';
24
- import Loader from './Loader';
25
18
  import Settings from './Settings';
19
+ import ToolTitle from './ToolTitle';
26
20
 
27
21
  export const useStyles = createStyles(({ css, token }) => ({
28
22
  apiName: css`
@@ -92,11 +86,6 @@ const Inspectors = memo<InspectorProps>(
92
86
 
93
87
  const [showDebug, setShowDebug] = useState(false);
94
88
 
95
- const loading = useChatStore(chatSelectors.isToolCallStreaming(messageId, index));
96
-
97
- const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
98
- const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
99
-
100
89
  return (
101
90
  <Flexbox gap={4}>
102
91
  <Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
@@ -110,15 +99,13 @@ const Inspectors = memo<InspectorProps>(
110
99
  }}
111
100
  paddingInline={4}
112
101
  >
113
- <Flexbox
114
- align={'center'}
115
- className={loading ? styles.shinyText : ''}
116
- gap={4}
117
- horizontal
118
- >
119
- {loading ? <Loader /> : <PluginAvatar identifier={identifier} size={20} />}
120
- <div>{pluginTitle}</div>/<span className={styles.apiName}>{apiName}</span>
121
- </Flexbox>
102
+ <ToolTitle
103
+ apiName={apiName}
104
+ identifier={identifier}
105
+ index={index}
106
+ messageId={messageId}
107
+ toolCallId={id}
108
+ />
122
109
  <Icon icon={showRender ? ChevronDown : ChevronRight} />
123
110
  </Flexbox>
124
111
  <Flexbox horizontal>
@@ -19,60 +19,72 @@ const CustomRender = memo<
19
19
  setShowPluginRender: (show: boolean) => void;
20
20
  showPluginRender: boolean;
21
21
  }
22
- >(({ id, content, pluginState, plugin, requestArgs, showPluginRender, setShowPluginRender }) => {
23
- const [loading, isMessageToolUIOpen] = useChatStore((s) => [
24
- chatSelectors.isPluginApiInvoking(id)(s),
25
- chatPortalSelectors.isPluginUIOpen(id)(s),
26
- ]);
27
- const { direction } = useContext(ConfigProvider.ConfigContext);
28
- const { t } = useTranslation('plugin');
22
+ >(
23
+ ({
24
+ id,
25
+ content,
26
+ pluginState,
27
+ plugin,
28
+ requestArgs,
29
+ showPluginRender,
30
+ setShowPluginRender,
31
+ pluginError,
32
+ }) => {
33
+ const [loading, isMessageToolUIOpen] = useChatStore((s) => [
34
+ chatSelectors.isPluginApiInvoking(id)(s),
35
+ chatPortalSelectors.isPluginUIOpen(id)(s),
36
+ ]);
37
+ const { direction } = useContext(ConfigProvider.ConfigContext);
38
+ const { t } = useTranslation('plugin');
29
39
 
30
- const theme = useTheme();
31
- useEffect(() => {
32
- if (!plugin?.type || loading) return;
40
+ const theme = useTheme();
41
+ useEffect(() => {
42
+ if (!plugin?.type || loading) return;
33
43
 
34
- setShowPluginRender(plugin?.type !== 'default');
35
- }, [plugin?.type, loading]);
44
+ setShowPluginRender(plugin?.type !== 'default');
45
+ }, [plugin?.type, loading]);
36
46
 
37
- if (isMessageToolUIOpen)
38
- return (
39
- <Center paddingBlock={8} style={{ background: theme.colorFillQuaternary, borderRadius: 4 }}>
40
- <Empty
41
- description={t('showInPortal')}
42
- image={
43
- <Icon
44
- color={theme.colorTextQuaternary}
45
- icon={direction === 'rtl' ? LucideSquareArrowLeft : LucideSquareArrowRight}
46
- size={'large'}
47
- />
48
- }
49
- styles={{
50
- image: { height: 24 },
51
- }}
52
- />
53
- </Center>
54
- );
47
+ if (isMessageToolUIOpen)
48
+ return (
49
+ <Center paddingBlock={8} style={{ background: theme.colorFillQuaternary, borderRadius: 4 }}>
50
+ <Empty
51
+ description={t('showInPortal')}
52
+ image={
53
+ <Icon
54
+ color={theme.colorTextQuaternary}
55
+ icon={direction === 'rtl' ? LucideSquareArrowLeft : LucideSquareArrowRight}
56
+ size={'large'}
57
+ />
58
+ }
59
+ styles={{
60
+ image: { height: 24 },
61
+ }}
62
+ />
63
+ </Center>
64
+ );
55
65
 
56
- if (loading) return <Arguments arguments={requestArgs} shine />;
66
+ if (loading) return <Arguments arguments={requestArgs} shine />;
57
67
 
58
- return (
59
- <Flexbox gap={12} id={id} width={'100%'}>
60
- {showPluginRender ? (
61
- <PluginRender
62
- arguments={plugin?.arguments}
63
- content={content}
64
- id={id}
65
- identifier={plugin?.identifier}
66
- loading={loading}
67
- payload={plugin}
68
- pluginState={pluginState}
69
- type={plugin?.type}
70
- />
71
- ) : (
72
- <Arguments arguments={requestArgs} />
73
- )}
74
- </Flexbox>
75
- );
76
- });
68
+ return (
69
+ <Flexbox gap={12} id={id} width={'100%'}>
70
+ {showPluginRender ? (
71
+ <PluginRender
72
+ arguments={plugin?.arguments}
73
+ content={content}
74
+ id={id}
75
+ identifier={plugin?.identifier}
76
+ loading={loading}
77
+ payload={plugin}
78
+ pluginError={pluginError}
79
+ pluginState={pluginState}
80
+ type={plugin?.type}
81
+ />
82
+ ) : (
83
+ <Arguments arguments={requestArgs} />
84
+ )}
85
+ </Flexbox>
86
+ );
87
+ },
88
+ );
77
89
 
78
90
  export default CustomRender;
@@ -12,11 +12,20 @@ export interface BuiltinTypeProps {
12
12
  id: string;
13
13
  identifier?: string;
14
14
  loading?: boolean;
15
+ pluginError?: any;
15
16
  pluginState?: any;
16
17
  }
17
18
 
18
19
  const BuiltinType = memo<BuiltinTypeProps>(
19
- ({ content, arguments: argumentsStr = '', pluginState, id, identifier, loading }) => {
20
+ ({
21
+ content,
22
+ arguments: argumentsStr = '',
23
+ pluginState,
24
+ id,
25
+ identifier,
26
+ loading,
27
+ pluginError,
28
+ }) => {
20
29
  const { isJSON, data } = useParseContent(content);
21
30
 
22
31
  if (!isJSON) {
@@ -35,6 +44,7 @@ const BuiltinType = memo<BuiltinTypeProps>(
35
44
  content={data}
36
45
  identifier={identifier}
37
46
  messageId={id}
47
+ pluginError={pluginError}
38
48
  pluginState={pluginState}
39
49
  />
40
50
  );
@@ -15,6 +15,7 @@ export interface PluginRenderProps {
15
15
  identifier?: string;
16
16
  loading?: boolean;
17
17
  payload?: PluginRequestPayload;
18
+ pluginError?: any;
18
19
  pluginState?: any;
19
20
  type?: LobeToolRenderType;
20
21
  }
@@ -29,6 +30,7 @@ const PluginRender = memo<PluginRenderProps>(
29
30
  identifier,
30
31
  type,
31
32
  loading,
33
+ pluginError,
32
34
  }) => {
33
35
  switch (type) {
34
36
  case 'standalone': {
@@ -43,6 +45,7 @@ const PluginRender = memo<PluginRenderProps>(
43
45
  id={id}
44
46
  identifier={identifier}
45
47
  loading={loading}
48
+ pluginError={pluginError}
46
49
  pluginState={pluginState}
47
50
  />
48
51
  );
@@ -5,7 +5,6 @@ import { useChatStore } from '@/store/chat';
5
5
  import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
6
6
  import { safeParseJSON } from '@/utils/safeParseJSON';
7
7
 
8
- import Footer from '../Footer';
9
8
  import ToolRender from './ToolRender';
10
9
 
11
10
  const ToolUI = () => {
@@ -25,12 +24,9 @@ const ToolUI = () => {
25
24
  if (!args) return;
26
25
 
27
26
  return (
28
- <>
29
- <Flexbox flex={1} height={'100%'} paddingInline={12} style={{ overflow: 'auto' }}>
30
- <ToolRender />
31
- </Flexbox>
32
- <Footer />
33
- </>
27
+ <Flexbox flex={1} height={'100%'} paddingInline={12} style={{ overflow: 'auto' }}>
28
+ <ToolRender />
29
+ </Flexbox>
34
30
  );
35
31
  };
36
32
 
@@ -1,7 +1,7 @@
1
- import { ActionIcon } from '@lobehub/ui';
1
+ import { ActionIcon, Icon } from '@lobehub/ui';
2
2
  import { Typography } from 'antd';
3
3
  import isEqual from 'fast-deep-equal';
4
- import { ArrowLeft } from 'lucide-react';
4
+ import { ArrowLeft, Globe } from 'lucide-react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
@@ -10,6 +10,7 @@ import { useChatStore } from '@/store/chat';
10
10
  import { chatPortalSelectors } from '@/store/chat/selectors';
11
11
  import { pluginHelpers, useToolStore } from '@/store/tool';
12
12
  import { toolSelectors } from '@/store/tool/selectors';
13
+ import { WebBrowsingManifest } from '@/tools/web-browsing';
13
14
 
14
15
  const Header = () => {
15
16
  const [closeToolUI, toolUIIdentifier = ''] = useChatStore((s) => [
@@ -21,6 +22,17 @@ const Header = () => {
21
22
  const pluginMeta = useToolStore(toolSelectors.getMetaById(toolUIIdentifier), isEqual);
22
23
  const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
23
24
 
25
+ if (toolUIIdentifier === WebBrowsingManifest.identifier) {
26
+ return (
27
+ <Flexbox align={'center'} gap={8} horizontal>
28
+ <ActionIcon icon={ArrowLeft} onClick={() => closeToolUI()} />
29
+ <Icon icon={Globe} size={{ fontSize: 16 }} />
30
+ <Typography.Text style={{ fontSize: 16 }} type={'secondary'}>
31
+ {t('search.title')}
32
+ </Typography.Text>
33
+ </Flexbox>
34
+ );
35
+ }
24
36
  return (
25
37
  <Flexbox align={'center'} gap={4} horizontal>
26
38
  <ActionIcon icon={ArrowLeft} onClick={() => closeToolUI()} />
@@ -0,0 +1,27 @@
1
+ import { useAgentStore } from '@/store/agent';
2
+ import { agentSelectors } from '@/store/agent/slices/chat';
3
+ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
4
+
5
+ export const useAgentEnableSearch = () => {
6
+ const [model, provider, agentSearchMode] = useAgentStore((s) => [
7
+ agentSelectors.currentAgentModel(s),
8
+ agentSelectors.currentAgentModelProvider(s),
9
+ agentSelectors.agentSearchMode(s),
10
+ ]);
11
+
12
+ const isModelSupportToolUse = useAiInfraStore(
13
+ aiModelSelectors.isModelSupportToolUse(model, provider),
14
+ );
15
+ const searchImpl = useAiInfraStore(aiModelSelectors.modelBuiltinSearchImpl(model, provider));
16
+
17
+ // 只要是内置的搜索实现,一定可以联网搜索
18
+ if (searchImpl === 'internal') return true;
19
+
20
+ // 如果是关闭状态,一定不能联网搜索
21
+ if (agentSearchMode === 'off') return false;
22
+
23
+ // 如果是智能模式,根据是否支持 Tool Calling 判断
24
+ if (agentSearchMode === 'auto') {
25
+ return isModelSupportToolUse;
26
+ }
27
+ };
@@ -66,6 +66,29 @@ describe('LobePerplexityAI', () => {
66
66
 
67
67
  it('should with search citations', async () => {
68
68
  const data = [
69
+ {
70
+ id: '506d64fb-e7f2-4d94-b80f-158369e9446d',
71
+ model: 'sonar-pro',
72
+ created: 1739896615,
73
+ object: 'chat.completion.chunk',
74
+ choices: [
75
+ {
76
+ finish_reason: null,
77
+ index: 0,
78
+ delta: {
79
+ refusal: null,
80
+ content: '<think>',
81
+ role: 'assistant',
82
+ function_call: null,
83
+ tool_calls: null,
84
+ audio: null,
85
+ },
86
+ logprobs: null,
87
+ },
88
+ ],
89
+ stream_options: null,
90
+ citations: null,
91
+ },
69
92
  {
70
93
  id: '506d64fb-e7f2-4d94-b80f-158369e9446d',
71
94
  model: 'sonar-pro',
@@ -202,6 +225,9 @@ describe('LobePerplexityAI', () => {
202
225
 
203
226
  expect(stream).toEqual(
204
227
  [
228
+ 'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
229
+ 'event: text',
230
+ 'data: "<think>"\n',
205
231
  'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
206
232
  'event: grounding',
207
233
  'data: {"citations":[{"title":"https://www.weather.com.cn/weather/101210101.shtml","url":"https://www.weather.com.cn/weather/101210101.shtml"},{"title":"https://tianqi.moji.com/weather/china/zhejiang/hangzhou","url":"https://tianqi.moji.com/weather/china/zhejiang/hangzhou"},{"title":"https://weather.cma.cn/web/weather/58457.html","url":"https://weather.cma.cn/web/weather/58457.html"},{"title":"https://tianqi.so.com/weather/101210101","url":"https://tianqi.so.com/weather/101210101"},{"title":"https://www.accuweather.com/zh/cn/hangzhou/106832/weather-forecast/106832","url":"https://www.accuweather.com/zh/cn/hangzhou/106832/weather-forecast/106832"},{"title":"https://www.hzqx.com","url":"https://www.hzqx.com"},{"title":"https://www.hzqx.com/pc/hztq/","url":"https://www.hzqx.com/pc/hztq/"}]}\n',
@@ -119,7 +119,7 @@ export const transformOpenAIStream = (
119
119
  if (typeof content === 'string') {
120
120
  // in Perplexity api, the citation is in every chunk, but we only need to return it once
121
121
 
122
- if ('citations' in chunk && !streamContext?.returnedPplxCitation) {
122
+ if ('citations' in chunk && !!chunk.citations && !streamContext?.returnedPplxCitation) {
123
123
  streamContext.returnedPplxCitation = true;
124
124
 
125
125
  const citations = (chunk.citations as any[]).map((item) =>
@@ -1,3 +1,4 @@
1
1
  export { asyncClient } from './async';
2
2
  export { edgeClient } from './edge';
3
3
  export * from './lambda';
4
+ export * from './tools';
@@ -0,0 +1,20 @@
1
+ import { createTRPCClient, httpBatchLink } from '@trpc/client';
2
+ import superjson from 'superjson';
3
+
4
+ import type { ToolsRouter } from '@/server/routers/tools';
5
+
6
+ export const toolsClient = createTRPCClient<ToolsRouter>({
7
+ links: [
8
+ httpBatchLink({
9
+ headers: async () => {
10
+ // dynamic import to avoid circular dependency
11
+ const { createHeaderWithAuth } = await import('@/services/_auth');
12
+
13
+ return createHeaderWithAuth();
14
+ },
15
+ maxURLLength: 2083,
16
+ transformer: superjson,
17
+ url: '/trpc/tools',
18
+ }),
19
+ ],
20
+ });
@@ -135,6 +135,22 @@ export default {
135
135
  plugin: '插件运行中...',
136
136
  },
137
137
  pluginList: '插件列表',
138
+ search: {
139
+ config: {
140
+ addKey: '添加秘钥',
141
+ close: '删除',
142
+ confirm: '已完成配置并重试',
143
+ },
144
+ searchxng: {
145
+ baseURL: '请输入',
146
+ description: '请输入 SearchXNG 的网址,即可开始联网搜索',
147
+ keyPlaceholder: '请输入秘钥',
148
+ title: '配置 SearchXNG 搜索引擎',
149
+ unconfiguredDesc: '请联系管理员完成 SearchXNG 搜索引擎配置,即可开始联网搜索',
150
+ unconfiguredTitle: '暂未配置 SearchXNG 搜索引擎',
151
+ },
152
+ title: '联网搜索',
153
+ },
138
154
  setting: '插件设置',
139
155
  settings: {
140
156
  indexUrl: {
@@ -7,11 +7,6 @@ export default {
7
7
  },
8
8
  },
9
9
  Plugins: '插件',
10
- actions: {
11
- genAiMessage: '创建助手消息',
12
- summary: '总结',
13
- summaryTooltip: '总结当前内容',
14
- },
15
10
  artifacts: {
16
11
  display: {
17
12
  code: '代码',
@@ -7,4 +7,22 @@ export default {
7
7
  images: '图片:',
8
8
  prompt: '提示词',
9
9
  },
10
+ search: {
11
+ createNewSearch: '创建新的搜索记录',
12
+ emptyResult: '没有搜索到结果,请修改关键词后重试',
13
+ genAiMessage: '创建助手消息',
14
+ includedTooltip: '当前搜索结果会进入会话的上下文中',
15
+ keywords: '关键词:',
16
+ scoreTooltip: '相关性分数,该分数越高说明与查询关键词越相关',
17
+ searchBar: {
18
+ button: '搜索',
19
+ placeholder: '关键词',
20
+ tooltip: '将会重新获取搜索结果,并创建一条新的总结消息',
21
+ },
22
+ searchEngine: '搜索引擎:',
23
+ searchResult: '搜索数量:',
24
+ summary: '总结',
25
+ summaryTooltip: '总结当前内容',
26
+ viewMoreResults: '查看更多 {{results}} 个结果',
27
+ },
10
28
  };
@@ -0,0 +1,33 @@
1
+ import qs from 'query-string';
2
+ import urlJoin from 'url-join';
3
+
4
+ import { SearchResponse } from '@/types/tool/search';
5
+
6
+ export class SearXNGClient {
7
+ private baseUrl: string;
8
+
9
+ constructor(baseUrl: string) {
10
+ this.baseUrl = baseUrl;
11
+ }
12
+
13
+ async search(query: string, engines?: string[]): Promise<SearchResponse> {
14
+ try {
15
+ const searchParams = qs.stringify({
16
+ engines: engines?.join(','),
17
+ format: 'json',
18
+ q: query,
19
+ });
20
+
21
+ const response = await fetch(urlJoin(this.baseUrl, `/search?${searchParams}`));
22
+
23
+ if (response.ok) {
24
+ return await response.json();
25
+ }
26
+
27
+ throw new Error(`Failed to search: ${response.statusText}`);
28
+ } catch (error) {
29
+ console.error('Error searching:', error);
30
+ throw error;
31
+ }
32
+ }
33
+ }
@@ -166,6 +166,17 @@ export const messageRouter = router({
166
166
  return ctx.messageModel.updateMessagePlugin(input.id, input.value);
167
167
  }),
168
168
 
169
+ updatePluginError: messageProcedure
170
+ .input(
171
+ z.object({
172
+ id: z.string(),
173
+ value: z.object({}).passthrough().nullable(),
174
+ }),
175
+ )
176
+ .mutation(async ({ input, ctx }) => {
177
+ return ctx.messageModel.updateMessagePlugin(input.id, { error: input.value });
178
+ }),
179
+
169
180
  updatePluginState: messageProcedure
170
181
  .input(
171
182
  z.object({