feat: Optimize the code

This commit is contained in:
pomelo-nwu
2025-10-27 11:24:38 +08:00
parent b1ece177b7
commit 79b4821499
6 changed files with 31 additions and 80 deletions

View File

@@ -4,16 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type {
WebSearchProvider,
WebSearchResult,
WebSearchResultItem,
} from './types.js';
import { WebSearchError } from './errors.js';
import type { WebSearchProvider, WebSearchResult } from './types.js';
/**
* Base implementation for web search providers.
* Provides common functionality for error handling and result formatting.
* Provides common functionality for error handling.
*/
export abstract class BaseWebSearchProvider implements WebSearchProvider {
abstract readonly name: string;
@@ -42,38 +37,22 @@ export abstract class BaseWebSearchProvider implements WebSearchProvider {
*/
async search(query: string, signal: AbortSignal): Promise<WebSearchResult> {
if (!this.isAvailable()) {
throw new WebSearchError(
this.name,
'Provider is not available. Please check your configuration.',
throw new Error(
`[${this.name}] Provider is not available. Please check your configuration.`,
);
}
try {
return await this.performSearch(query, signal);
} catch (error: unknown) {
if (error instanceof WebSearchError) {
if (
error instanceof Error &&
error.message.startsWith(`[${this.name}]`)
) {
throw error;
}
throw new WebSearchError(this.name, 'Search failed', error);
const message = error instanceof Error ? error.message : String(error);
throw new Error(`[${this.name}] Search failed: ${message}`);
}
}
/**
* Format search results into a consistent format.
* @param results Raw results from the provider
* @param query The original search query
* @param answer Optional answer from the provider
* @returns Formatted search results
*/
protected formatResults(
results: WebSearchResultItem[],
query: string,
answer?: string,
): WebSearchResult {
return {
query,
answer,
results,
};
}
}

View File

@@ -1,19 +0,0 @@
/**
* @license
* Copyright 2025 Qwen
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Custom error class for web search operations.
*/
export class WebSearchError extends Error {
constructor(
readonly provider: string,
message: string,
readonly originalError?: unknown,
) {
super(`[${provider}] ${message}`);
this.name = 'WebSearchError';
}
}

View File

@@ -5,7 +5,6 @@
*/
import { BaseWebSearchProvider } from '../base-provider.js';
import { WebSearchError } from '../errors.js';
import type {
WebSearchResult,
WebSearchResultItem,
@@ -104,8 +103,7 @@ export class DashScopeProvider extends BaseWebSearchProvider {
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new WebSearchError(
this.name,
throw new Error(
`API error: ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`,
);
}
@@ -113,10 +111,7 @@ export class DashScopeProvider extends BaseWebSearchProvider {
const data = (await response.json()) as DashScopeSearchResponse;
if (data.status !== 0) {
throw new WebSearchError(
this.name,
`API error: ${data.message || 'Unknown error'}`,
);
throw new Error(`API error: ${data.message || 'Unknown error'}`);
}
const results: WebSearchResultItem[] = (data.data?.docs || []).map(
@@ -129,6 +124,9 @@ export class DashScopeProvider extends BaseWebSearchProvider {
}),
);
return this.formatResults(results, query, undefined);
return {
query,
results,
};
}
}

View File

@@ -5,7 +5,6 @@
*/
import { BaseWebSearchProvider } from '../base-provider.js';
import { WebSearchError } from '../errors.js';
import type {
WebSearchResult,
WebSearchResultItem,
@@ -71,8 +70,7 @@ export class GoogleProvider extends BaseWebSearchProvider {
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new WebSearchError(
this.name,
throw new Error(
`API error: ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`,
);
}
@@ -85,6 +83,9 @@ export class GoogleProvider extends BaseWebSearchProvider {
content: item.snippet,
}));
return this.formatResults(results, query, undefined);
return {
query,
results,
};
}
}

View File

@@ -5,7 +5,6 @@
*/
import { BaseWebSearchProvider } from '../base-provider.js';
import { WebSearchError } from '../errors.js';
import type {
WebSearchResult,
WebSearchResultItem,
@@ -61,8 +60,7 @@ export class TavilyProvider extends BaseWebSearchProvider {
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new WebSearchError(
this.name,
throw new Error(
`API error: ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`,
);
}
@@ -77,6 +75,10 @@ export class TavilyProvider extends BaseWebSearchProvider {
publishedDate: r.published_date,
}));
return this.formatResults(results, query, data.answer?.trim());
return {
query,
answer: data.answer?.trim(),
results,
};
}
}

View File

@@ -8,19 +8,6 @@
* Utility functions for web search formatting and processing.
*/
/**
* Format sources into a numbered list with titles and URLs.
* @param sources Array of source objects with title and url
* @returns Formatted source list string
*/
export function formatSources(
sources: Array<{ title: string; url: string }>,
): string {
return sources
.map((s, i) => `[${i + 1}] ${s.title || 'Untitled'} (${s.url})`)
.join('\n');
}
/**
* Build content string with appended sources section.
* @param content Main content text
@@ -32,7 +19,10 @@ export function buildContentWithSources(
sources: Array<{ title: string; url: string }>,
): string {
if (!sources.length) return content;
return `${content}\n\nSources:\n${formatSources(sources)}`;
const sourceList = sources
.map((s, i) => `[${i + 1}] ${s.title || 'Untitled'} (${s.url})`)
.join('\n');
return `${content}\n\nSources:\n${sourceList}`;
}
/**