@@ -5,13 +5,17 @@ import { StateCreator } from 'zustand/vanilla';
5
5
6
6
import { useClientDataSWR } from '@/libs/swr' ;
7
7
import { fileService } from '@/services/file' ;
8
+ import { searchService } from '@/services/search' ;
8
9
import { imageGenerationService } from '@/services/textToImage' ;
9
10
import { uploadService } from '@/services/upload' ;
10
11
import { chatSelectors } from '@/store/chat/selectors' ;
11
12
import { ChatStore } from '@/store/chat/store' ;
12
13
import { useFileStore } from '@/store/file' ;
14
+ import { CreateMessageParams } from '@/types/message' ;
13
15
import { DallEImageItem } from '@/types/tool/dalle' ;
16
+ import { SearchContent , SearchQuery } from '@/types/tool/search' ;
14
17
import { setNamespace } from '@/utils/storeDebug' ;
18
+ import { nanoid } from '@/utils/uuid' ;
15
19
16
20
const n = setNamespace ( 'tool' ) ;
17
21
@@ -21,8 +25,25 @@ const SWR_FETCH_KEY = 'FetchImageItem';
21
25
*/
22
26
export interface ChatBuiltinToolAction {
23
27
generateImageFromPrompts : ( items : DallEImageItem [ ] , id : string ) => Promise < void > ;
28
+ /**
29
+ * 重新发起搜索
30
+ * @description 会更新插件的 arguments 参数,然后再次搜索
31
+ */
32
+ reSearchWithSearXNG : (
33
+ id : string ,
34
+ data : SearchQuery ,
35
+ options ?: { aiSummary : boolean } ,
36
+ ) => Promise < void > ;
37
+ saveSearXNGSearchResult : ( id : string ) => Promise < void > ;
38
+ searchWithSearXNG : (
39
+ id : string ,
40
+ data : SearchQuery ,
41
+ aiSummary ?: boolean ,
42
+ ) => Promise < void | boolean > ;
24
43
text2image : ( id : string , data : DallEImageItem [ ] ) => Promise < void > ;
44
+
25
45
toggleDallEImageLoading : ( key : string , value : boolean ) => void ;
46
+ toggleSearchLoading : ( id : string , loading : boolean ) => void ;
26
47
updateImageItem : ( id : string , updater : ( data : DallEImageItem [ ] ) => void ) => Promise < void > ;
27
48
useFetchDalleImageItem : ( id : string ) => SWRResponse ;
28
49
}
@@ -82,19 +103,92 @@ export const chatToolSlice: StateCreator<
82
103
} ) ;
83
104
} ) ;
84
105
} ,
106
+ reSearchWithSearXNG : async ( id , data , options ) => {
107
+ get ( ) . toggleSearchLoading ( id , true ) ;
108
+ await get ( ) . updatePluginArguments ( id , data ) ;
109
+
110
+ await get ( ) . searchWithSearXNG ( id , data , options ?. aiSummary ) ;
111
+ } ,
112
+ saveSearXNGSearchResult : async ( id ) => {
113
+ const message = chatSelectors . getMessageById ( id ) ( get ( ) ) ;
114
+ if ( ! message || ! message . plugin ) return ;
115
+
116
+ const { internal_addToolToAssistantMessage, internal_createMessage, openToolUI } = get ( ) ;
117
+ // 1. 创建一个新的 tool call message
118
+ const newToolCallId = `tool_call_${ nanoid ( ) } ` ;
119
+
120
+ const toolMessage : CreateMessageParams = {
121
+ content : message . content ,
122
+ id : undefined ,
123
+ parentId : message . parentId ,
124
+ plugin : message . plugin ,
125
+ pluginState : message . pluginState ,
126
+ role : 'tool' ,
127
+ sessionId : get ( ) . activeId ,
128
+ tool_call_id : newToolCallId ,
129
+ topicId : get ( ) . activeTopicId ,
130
+ } ;
131
+
132
+ const addToolItem = async ( ) => {
133
+ if ( ! message . parentId || ! message . plugin ) return ;
134
+
135
+ await internal_addToolToAssistantMessage ( message . parentId , {
136
+ id : newToolCallId ,
137
+ ...message . plugin ,
138
+ } ) ;
139
+ } ;
140
+
141
+ const [ newMessageId ] = await Promise . all ( [
142
+ // 1. 添加 tool message
143
+ internal_createMessage ( toolMessage ) ,
144
+ // 2. 将这条 tool call message 插入到 ai 消息的 tools 中
145
+ addToolItem ( ) ,
146
+ ] ) ;
147
+
148
+ // 将新创建的 tool message 激活
149
+ openToolUI ( newMessageId , message . plugin . identifier ) ;
150
+ } ,
151
+ searchWithSearXNG : async ( id , params , aiSummary = true ) => {
152
+ get ( ) . toggleSearchLoading ( id , true ) ;
153
+ const data = await searchService . search ( params . query , params . searchEngines ) ;
154
+ await get ( ) . updatePluginState ( id , data ) ;
155
+
156
+ get ( ) . toggleSearchLoading ( id , false ) ;
157
+
158
+ // 只取前 5 个结果作为上下文
159
+ const searchContent : SearchContent [ ] = data . results . slice ( 0 , 5 ) . map ( ( item ) => ( {
160
+ content : item . content ,
161
+ title : item . title ,
162
+ url : item . url ,
163
+ } ) ) ;
164
+
165
+ await get ( ) . internal_updateMessageContent ( id , JSON . stringify ( searchContent ) ) ;
166
+
167
+ // 如果没搜索到结果,那么不触发 ai 总结
168
+ if ( searchContent . length === 0 ) return ;
169
+
170
+ // 如果 aiSummary 为 true,则会自动触发总结
171
+ return aiSummary ;
172
+ } ,
85
173
text2image : async ( id , data ) => {
86
174
// const isAutoGen = settingsSelectors.isDalleAutoGenerating(useGlobalStore.getState());
87
175
// if (!isAutoGen) return;
88
176
89
177
await get ( ) . generateImageFromPrompts ( data , id ) ;
90
178
} ,
179
+
91
180
toggleDallEImageLoading : ( key , value ) => {
92
181
set (
93
182
{ dalleImageLoading : { ...get ( ) . dalleImageLoading , [ key ] : value } } ,
94
183
false ,
95
184
n ( 'toggleDallEImageLoading' ) ,
96
185
) ;
97
186
} ,
187
+
188
+ toggleSearchLoading : ( id , loading ) => {
189
+ set ( { searchLoading : { ...get ( ) . searchLoading , [ id ] : loading } } , false , 'toggleSearchLoading' ) ;
190
+ } ,
191
+
98
192
updateImageItem : async ( id , updater ) => {
99
193
const message = chatSelectors . getMessageById ( id ) ( get ( ) ) ;
100
194
if ( ! message ) return ;
@@ -104,6 +198,7 @@ export const chatToolSlice: StateCreator<
104
198
const nextContent = produce ( data , updater ) ;
105
199
await get ( ) . internal_updateMessageContent ( id , JSON . stringify ( nextContent ) ) ;
106
200
} ,
201
+
107
202
useFetchDalleImageItem : ( id ) =>
108
203
useClientDataSWR ( [ SWR_FETCH_KEY , id ] , async ( ) => {
109
204
const item = await fileService . getFile ( id ) ;
0 commit comments