feat(tools): extend cancellation to browser, web, and process tools
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Tool, ToolResult } from '../types.js';
|
||||
import type { Tool, ToolExecutionContext, ToolResult } from '../types.js';
|
||||
|
||||
/** Configuration for the web search tool. */
|
||||
export interface WebSearchConfig {
|
||||
@@ -49,16 +49,22 @@ async function searchBrave(
|
||||
query: string,
|
||||
count: number,
|
||||
apiKey: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SearchResult[]> {
|
||||
const params = new URLSearchParams({
|
||||
q: query,
|
||||
count: String(count),
|
||||
});
|
||||
|
||||
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
||||
const fetchSignal = signal
|
||||
? AbortSignal.any([signal, timeoutSignal])
|
||||
: timeoutSignal;
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.search.brave.com/res/v1/web/search?${params.toString()}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
||||
signal: fetchSignal,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Subscription-Token': apiKey,
|
||||
@@ -90,6 +96,7 @@ async function searchSearxng(
|
||||
query: string,
|
||||
count: number,
|
||||
endpoint: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SearchResult[]> {
|
||||
// Strip trailing slash from endpoint
|
||||
const baseUrl = endpoint.replace(/\/+$/, '');
|
||||
@@ -99,8 +106,13 @@ async function searchSearxng(
|
||||
categories: 'general',
|
||||
});
|
||||
|
||||
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
||||
const fetchSignal = signal
|
||||
? AbortSignal.any([signal, timeoutSignal])
|
||||
: timeoutSignal;
|
||||
|
||||
const response = await fetch(`${baseUrl}/search?${params.toString()}`, {
|
||||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
||||
signal: fetchSignal,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
@@ -147,8 +159,11 @@ export function createWebSearchTool(config: WebSearchConfig): Tool {
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
execute: async (rawArgs: unknown): Promise<ToolResult> => {
|
||||
execute: async (rawArgs: unknown, context?: ToolExecutionContext): Promise<ToolResult> => {
|
||||
const args = rawArgs as WebSearchArgs;
|
||||
if (context?.signal?.aborted) {
|
||||
return { success: false, output: '', error: 'Operation aborted' };
|
||||
}
|
||||
// Clamp count: use provided value (capped at MAX_RESULTS), or fall back to default
|
||||
const count = Math.min(args.count ?? defaultCount, MAX_RESULTS);
|
||||
|
||||
@@ -163,7 +178,7 @@ export function createWebSearchTool(config: WebSearchConfig): Tool {
|
||||
error: 'Brave Search API key not configured',
|
||||
};
|
||||
}
|
||||
results = await searchBrave(args.query, count, config.apiKey);
|
||||
results = await searchBrave(args.query, count, config.apiKey, context?.signal);
|
||||
} else {
|
||||
// SearXNG provider
|
||||
if (!config.endpoint) {
|
||||
@@ -173,7 +188,7 @@ export function createWebSearchTool(config: WebSearchConfig): Tool {
|
||||
error: 'SearXNG endpoint not configured',
|
||||
};
|
||||
}
|
||||
results = await searchSearxng(args.query, count, config.endpoint);
|
||||
results = await searchSearxng(args.query, count, config.endpoint, context?.signal);
|
||||
}
|
||||
|
||||
if (results.length === 0) {
|
||||
@@ -185,6 +200,9 @@ export function createWebSearchTool(config: WebSearchConfig): Tool {
|
||||
|
||||
return { success: true, output: formatResults(results) };
|
||||
} catch (error) {
|
||||
if (error instanceof Error && (error.name === 'AbortError' || error.message.toLowerCase().includes('aborted'))) {
|
||||
return { success: false, output: '', error: 'Operation aborted' };
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
|
||||
Reference in New Issue
Block a user