chenbhao Claude Opus 4.6 commited on
Commit
191e910
·
1 Parent(s): 8b35d5c

refactor(WebSearchTool): remove inline image fetching, add search_images param

Browse files

- Remove automatic image fetching and inline embedding from WebSearchTool
- Add optional search_images parameter to query image-specific results
- Support SEARXNG_BASE_URL env var for SearXNG endpoint configuration
- Remove Output.images field and related inlineImageUtils import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

src/tools/WebSearchTool/WebSearchTool.ts CHANGED
@@ -11,11 +11,13 @@ import {
11
  renderToolUseMessage,
12
  renderToolUseProgressMessage,
13
  } from './UI.js'
14
- import { fetchImagesAsInline } from '../../utils/inlineImageUtils.js'
15
-
16
  const inputSchema = lazySchema(() =>
17
  z.strictObject({
18
  query: z.string().min(2).describe('The search query to use'),
 
 
 
 
19
  }),
20
  )
21
 
@@ -45,9 +47,7 @@ const outputSchema = lazySchema(() =>
45
  }),
46
  )
47
 
48
- export type Output = z.infer<ReturnType<typeof outputSchema>> & {
49
- images?: Array<{ url: string; base64: string; mediaType: string }>
50
- }
51
 
52
  export type { WebSearchProgress } from '../../types/tools.js'
53
  import type { WebSearchProgress } from '../../types/tools.js'
@@ -56,12 +56,17 @@ import type { WebSearchProgress } from '../../types/tools.js'
56
  * 使用 SearXNG 本地搜索
57
  */
58
  async function searchSearXNG(
59
- query: string
 
60
  ): Promise<Array<{ title: string; url: string; snippet?: string; image?: string }>> {
61
  try {
62
- const url = new URL('http://localhost:8080/search')
 
63
  url.searchParams.set('q', query)
64
  url.searchParams.set('format', 'json')
 
 
 
65
 
66
  const controller = new AbortController()
67
  const timeout = setTimeout(() => controller.abort(), 10000)
@@ -243,7 +248,7 @@ export const WebSearchTool = buildTool({
243
  const useTavily = !!process.env.TAVILY_API_KEY
244
  const results = useTavily
245
  ? await searchTavily(input.query)
246
- : await searchSearXNG(input.query)
247
 
248
  const cleaned = results.map(r => ({
249
  ...r,
@@ -260,14 +265,6 @@ export const WebSearchTool = buildTool({
260
  },
261
  ]
262
 
263
- const imageUrls = cleaned
264
- .map((r) => r.image)
265
- .filter((url): url is string => !!url)
266
- .slice(0, 4)
267
-
268
- const images =
269
- imageUrls.length > 0 ? await fetchImagesAsInline(imageUrls, 4) : undefined
270
-
271
  const duration = (performance.now() - start) / 1000
272
 
273
  return {
@@ -275,7 +272,6 @@ export const WebSearchTool = buildTool({
275
  query: input.query,
276
  results: output,
277
  durationSeconds: duration,
278
- images,
279
  },
280
  }
281
  } catch (error) {
@@ -318,17 +314,7 @@ export const WebSearchTool = buildTool({
318
  return {
319
  tool_use_id: toolUseID,
320
  type: 'tool_result',
321
- content: [
322
- { type: 'text', text },
323
- ...(output.images?.map((img) => ({
324
- type: 'image' as const,
325
- source: {
326
- type: 'base64' as const,
327
- data: img.base64,
328
- media_type: img.mediaType as 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp',
329
- },
330
- })) ?? []),
331
- ],
332
  }
333
  },
334
  }) satisfies ToolDef<any, Output, WebSearchProgress>
 
11
  renderToolUseMessage,
12
  renderToolUseProgressMessage,
13
  } from './UI.js'
 
 
14
  const inputSchema = lazySchema(() =>
15
  z.strictObject({
16
  query: z.string().min(2).describe('The search query to use'),
17
+ search_images: z.boolean().optional().describe(
18
+ 'Set to true to search for images specifically (vs general web results). ' +
19
+ 'Use when the user asks to see/look up photos, images, or visual references.'
20
+ ),
21
  }),
22
  )
23
 
 
47
  }),
48
  )
49
 
50
+ export type Output = z.infer<ReturnType<typeof outputSchema>>
 
 
51
 
52
  export type { WebSearchProgress } from '../../types/tools.js'
53
  import type { WebSearchProgress } from '../../types/tools.js'
 
56
  * 使用 SearXNG 本地搜索
57
  */
58
  async function searchSearXNG(
59
+ query: string,
60
+ searchImages?: boolean
61
  ): Promise<Array<{ title: string; url: string; snippet?: string; image?: string }>> {
62
  try {
63
+ const baseUrl = process.env.SEARXNG_BASE_URL || 'http://localhost:8080'
64
+ const url = new URL('/search', baseUrl)
65
  url.searchParams.set('q', query)
66
  url.searchParams.set('format', 'json')
67
+ if (searchImages) {
68
+ url.searchParams.set('categories', 'images')
69
+ }
70
 
71
  const controller = new AbortController()
72
  const timeout = setTimeout(() => controller.abort(), 10000)
 
248
  const useTavily = !!process.env.TAVILY_API_KEY
249
  const results = useTavily
250
  ? await searchTavily(input.query)
251
+ : await searchSearXNG(input.query, input.search_images)
252
 
253
  const cleaned = results.map(r => ({
254
  ...r,
 
265
  },
266
  ]
267
 
 
 
 
 
 
 
 
 
268
  const duration = (performance.now() - start) / 1000
269
 
270
  return {
 
272
  query: input.query,
273
  results: output,
274
  durationSeconds: duration,
 
275
  },
276
  }
277
  } catch (error) {
 
314
  return {
315
  tool_use_id: toolUseID,
316
  type: 'tool_result',
317
+ content: [{ type: 'text', text }],
 
 
 
 
 
 
 
 
 
 
318
  }
319
  },
320
  }) satisfies ToolDef<any, Output, WebSearchProgress>