feat: update code

This commit is contained in:
pomelo-nwu
2025-11-05 11:23:27 +08:00
parent 6357a5c87e
commit 2967bec11c
7 changed files with 102 additions and 83 deletions

View File

@@ -309,7 +309,8 @@ If you are experiencing performance issues with file searching (e.g., with `@` c
``` ```
- **`tavilyApiKey`** (string): - **`tavilyApiKey`** (string):
- **Description:** API key for Tavily web search service. Required to enable the `web_search` tool functionality. If not configured, the web search tool will be disabled and skipped. - **Description:** API key for Tavily web search service. Used to enable the `web_search` tool functionality.
- **Note:** This is a legacy configuration format. For Qwen OAuth users, DashScope provider is automatically available without any configuration. For other authentication types, configure Tavily or Google providers using the new `webSearch` configuration format.
- **Default:** `undefined` (web search disabled) - **Default:** `undefined` (web search disabled)
- **Example:** `"tavilyApiKey": "tvly-your-api-key-here"` - **Example:** `"tavilyApiKey": "tvly-your-api-key-here"`
- **`chatCompression`** (object): - **`chatCompression`** (object):
@@ -465,8 +466,8 @@ The CLI automatically loads environment variables from an `.env` file. The loadi
- This is useful for development and testing. - This is useful for development and testing.
- **`TAVILY_API_KEY`**: - **`TAVILY_API_KEY`**:
- Your API key for the Tavily web search service. - Your API key for the Tavily web search service.
- Required to enable the `web_search` tool functionality. - Used to enable the `web_search` tool functionality.
- If not configured, the web search tool will be disabled and skipped. - **Note:** For Qwen OAuth users, DashScope provider is automatically available without any configuration. For other authentication types, configure Tavily or Google providers to enable web search.
- Example: `export TAVILY_API_KEY="tvly-your-api-key-here"` - Example: `export TAVILY_API_KEY="tvly-your-api-key-here"`
## Command-Line Arguments ## Command-Line Arguments

View File

@@ -305,7 +305,8 @@ Settings are organized into categories. All settings should be placed within the
- **Default:** `undefined` - **Default:** `undefined`
- **`advanced.tavilyApiKey`** (string): - **`advanced.tavilyApiKey`** (string):
- **Description:** API key for Tavily web search service. Required to enable the `web_search` tool functionality. If not configured, the web search tool will be disabled and skipped. - **Description:** API key for Tavily web search service. Used to enable the `web_search` tool functionality.
- **Note:** This is a legacy configuration format. For Qwen OAuth users, DashScope provider is automatically available without any configuration. For other authentication types, configure Tavily or Google providers using the new `webSearch` configuration format.
- **Default:** `undefined` - **Default:** `undefined`
#### `mcpServers` #### `mcpServers`
@@ -474,8 +475,8 @@ The CLI automatically loads environment variables from an `.env` file. The loadi
- Set to a string to customize the title of the CLI. - Set to a string to customize the title of the CLI.
- **`TAVILY_API_KEY`**: - **`TAVILY_API_KEY`**:
- Your API key for the Tavily web search service. - Your API key for the Tavily web search service.
- Required to enable the `web_search` tool functionality. - Used to enable the `web_search` tool functionality.
- If not configured, the web search tool will be disabled and skipped. - **Note:** For Qwen OAuth users, DashScope provider is automatically available without any configuration. For other authentication types, configure Tavily or Google providers to enable web search.
- Example: `export TAVILY_API_KEY="tvly-your-api-key-here"` - Example: `export TAVILY_API_KEY="tvly-your-api-key-here"`
## Command-Line Arguments ## Command-Line Arguments

View File

@@ -8,7 +8,7 @@ Use `web_search` to perform a web search and get information from the internet.
### Supported Providers ### Supported Providers
1. **DashScope** (Official, Free) - Default provider, always available when using Qwen OAuth authentication (200 requests/minute, 2000 requests/day) 1. **DashScope** (Official, Free) - Automatically available for Qwen OAuth users (200 requests/minute, 2000 requests/day)
2. **Tavily** - High-quality search API with built-in answer generation 2. **Tavily** - High-quality search API with built-in answer generation
3. **Google Custom Search** - Google's Custom Search JSON API 3. **Google Custom Search** - Google's Custom Search JSON API
@@ -46,8 +46,9 @@ Add to your `settings.json`:
**Notes:** **Notes:**
- DashScope doesn't require an API key (official, free service) - DashScope doesn't require an API key (official, free service)
- Only configure the providers you want to use - **Qwen OAuth users:** DashScope is automatically added to your provider list, even if not explicitly configured
- Set `default` to specify which provider to use by default - Configure additional providers (Tavily, Google) if you want to use them alongside DashScope
- Set `default` to specify which provider to use by default (if not set, priority order: Tavily > Google > DashScope)
### Method 2: Environment Variables ### Method 2: Environment Variables
@@ -132,10 +133,11 @@ web_search(query="best practices for React 19", provider="dashscope")
### DashScope (Official) ### DashScope (Official)
- **Cost:** Free - **Cost:** Free
- **Authentication:** Automatically available with Qwen OAuth - **Authentication:** Automatically available when using Qwen OAuth authentication
- **Configuration:** No API key required - **Configuration:** No API key required, automatically added to provider list for Qwen OAuth users
- **Quota:** 200 requests/minute, 2000 requests/day - **Quota:** 200 requests/minute, 2000 requests/day
- **Best for:** General queries, always available - **Best for:** General queries, always available as fallback for Qwen OAuth users
- **Auto-registration:** If you're using Qwen OAuth, DashScope is automatically added to your provider list even if you don't configure it explicitly
### Tavily ### Tavily
@@ -158,14 +160,18 @@ web_search(query="best practices for React 19", provider="dashscope")
- **Response format:** Returns a concise answer with numbered source citations - **Response format:** Returns a concise answer with numbered source citations
- **Citations:** Source links are appended as a numbered list: [1], [2], etc. - **Citations:** Source links are appended as a numbered list: [1], [2], etc.
- **Multiple providers:** If one provider fails, manually specify another using the `provider` parameter - **Multiple providers:** If one provider fails, manually specify another using the `provider` parameter
- **DashScope availability:** Always available when authenticated with Qwen OAuth, no configuration needed - **DashScope availability:** Automatically available for Qwen OAuth users, no configuration needed
- **Default provider selection:** The system automatically selects a default provider based on availability:
1. Your explicit `default` configuration (highest priority)
2. CLI argument `--web-search-default`
3. First available provider by priority: Tavily > Google > DashScope
## Troubleshooting ## Troubleshooting
**Tool not available?** **Tool not available?**
- Check if at least one provider is configured - **For Qwen OAuth users:** The tool is automatically registered with DashScope provider, no configuration needed
- For DashScope: Ensure you're authenticated with Qwen OAuth - **For other authentication types:** Ensure at least one provider (Tavily or Google) is configured
- For Tavily/Google: Verify your API keys are correct - For Tavily/Google: Verify your API keys are correct
**Provider-specific errors?** **Provider-specific errors?**

View File

@@ -766,7 +766,11 @@ export async function loadCliConfig(
: argv.openaiLogging) ?? false, : argv.openaiLogging) ?? false,
}, },
cliVersion: await getCliVersion(), cliVersion: await getCliVersion(),
webSearch: buildWebSearchConfig(argv, settings), webSearch: buildWebSearchConfig(
argv,
settings,
settings.security?.auth?.selectedType,
),
summarizeToolOutput: settings.model?.summarizeToolOutput, summarizeToolOutput: settings.model?.summarizeToolOutput,
ideMode, ideMode,
chatCompression: settings.model?.chatCompression, chatCompression: settings.model?.chatCompression,

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { AuthType } from '@qwen-code/qwen-code-core';
import type { WebSearchProviderConfig } from '@qwen-code/qwen-code-core'; import type { WebSearchProviderConfig } from '@qwen-code/qwen-code-core';
import type { Settings } from './settings.js'; import type { Settings } from './settings.js';
@@ -33,56 +34,85 @@ export interface WebSearchConfig {
* *
* @param argv - Command line arguments * @param argv - Command line arguments
* @param settings - User settings from settings.json * @param settings - User settings from settings.json
* @param authType - Authentication type (e.g., 'qwen-oauth')
* @returns WebSearch configuration or undefined if no providers available * @returns WebSearch configuration or undefined if no providers available
*/ */
export function buildWebSearchConfig( export function buildWebSearchConfig(
argv: WebSearchCliArgs, argv: WebSearchCliArgs,
settings: Settings, settings: Settings,
authType?: string,
): WebSearchConfig | undefined { ): WebSearchConfig | undefined {
// Priority 1: Use settings.json webSearch config if present const isQwenOAuth = authType === AuthType.QWEN_OAUTH;
// Step 1: Collect providers from settings or command line/env
let providers: WebSearchProviderConfig[] = [];
let userDefault: string | undefined;
if (settings.webSearch) { if (settings.webSearch) {
return settings.webSearch; // Use providers from settings.json
providers = [...settings.webSearch.provider];
userDefault = settings.webSearch.default;
} else {
// Build providers from command line args and environment variables
const tavilyKey =
argv.tavilyApiKey ||
settings.advanced?.tavilyApiKey ||
process.env['TAVILY_API_KEY'];
if (tavilyKey) {
providers.push({
type: 'tavily',
apiKey: tavilyKey,
} as WebSearchProviderConfig);
}
const googleKey = argv.googleApiKey || process.env['GOOGLE_API_KEY'];
const googleEngineId =
argv.googleSearchEngineId || process.env['GOOGLE_SEARCH_ENGINE_ID'];
if (googleKey && googleEngineId) {
providers.push({
type: 'google',
apiKey: googleKey,
searchEngineId: googleEngineId,
} as WebSearchProviderConfig);
}
} }
// Priority 2: Build from command line args and environment variables // Step 2: Ensure dashscope is available for qwen-oauth users
const providers: WebSearchProviderConfig[] = []; if (isQwenOAuth) {
const hasDashscope = providers.some((p) => p.type === 'dashscope');
// DashScope is always available (official, free) if (!hasDashscope) {
providers.push({ type: 'dashscope' } as WebSearchProviderConfig); providers.push({ type: 'dashscope' } as WebSearchProviderConfig);
}
// Tavily from args/env/legacy settings
const tavilyKey =
argv.tavilyApiKey ||
settings.advanced?.tavilyApiKey ||
process.env['TAVILY_API_KEY'];
if (tavilyKey) {
providers.push({
type: 'tavily',
apiKey: tavilyKey,
} as WebSearchProviderConfig);
} }
// Google from args/env // Step 3: If no providers available, return undefined
const googleKey = argv.googleApiKey || process.env['GOOGLE_API_KEY'];
const googleEngineId =
argv.googleSearchEngineId || process.env['GOOGLE_SEARCH_ENGINE_ID'];
if (googleKey && googleEngineId) {
providers.push({
type: 'google',
apiKey: googleKey,
searchEngineId: googleEngineId,
} as WebSearchProviderConfig);
}
// If no providers configured, return undefined
if (providers.length === 0) { if (providers.length === 0) {
return undefined; return undefined;
} }
// Determine default provider // Step 4: Determine default provider
// Priority: CLI arg > has Tavily key > DashScope (fallback) // Priority: user explicit config > CLI arg > first available provider (tavily > google > dashscope)
const defaultProvider = const providerPriority: Array<'tavily' | 'google' | 'dashscope'> = [
argv.webSearchDefault || (tavilyKey ? 'tavily' : 'dashscope'); 'tavily',
'google',
'dashscope',
];
// Determine default provider based on availability
let defaultProvider = userDefault || argv.webSearchDefault;
if (!defaultProvider) {
// Find first available provider by priority order
for (const providerType of providerPriority) {
if (providers.some((p) => p.type === providerType)) {
defaultProvider = providerType;
break;
}
}
// Fallback to first available provider if none found in priority list
if (!defaultProvider) {
defaultProvider = providers[0]?.type || 'dashscope';
}
}
return { return {
provider: providers, provider: providers,

View File

@@ -1166,12 +1166,10 @@ export class Config {
registerCoreTool(TodoWriteTool, this); registerCoreTool(TodoWriteTool, this);
registerCoreTool(ExitPlanModeTool, this); registerCoreTool(ExitPlanModeTool, this);
registerCoreTool(WebFetchTool, this); registerCoreTool(WebFetchTool, this);
// Conditionally register web search tool if any web search provider is configured // Conditionally register web search tool if web search provider is configured
// or if using qwen-oauth authentication // buildWebSearchConfig ensures qwen-oauth users get dashscope provider, so
if ( // if tool is registered, config must exist
this.getWebSearchConfig() || if (this.getWebSearchConfig()) {
this.getAuthType() === AuthType.QWEN_OAUTH
) {
registerCoreTool(WebSearchTool, this); registerCoreTool(WebSearchTool, this);
} }

View File

@@ -17,7 +17,6 @@ import { ToolErrorType } from '../tool-error.js';
import type { Config } from '../../config/config.js'; import type { Config } from '../../config/config.js';
import { ApprovalMode } from '../../config/config.js'; import { ApprovalMode } from '../../config/config.js';
import { AuthType } from '../../core/contentGenerator.js';
import { getErrorMessage } from '../../utils/errors.js'; import { getErrorMessage } from '../../utils/errors.js';
import { buildContentWithSources } from './utils.js'; import { buildContentWithSources } from './utils.js';
import { TavilyProvider } from './providers/tavily-provider.js'; import { TavilyProvider } from './providers/tavily-provider.js';
@@ -44,16 +43,9 @@ class WebSearchToolInvocation extends BaseToolInvocation<
} }
override getDescription(): string { override getDescription(): string {
const webSearchConfig = this.config.getWebSearchConfig(); // If tool is registered, config must exist with a default provider
const authType = this.config.getAuthType(); const webSearchConfig = this.config.getWebSearchConfig()!;
let defaultProvider = webSearchConfig?.default; const provider = this.params.provider || webSearchConfig.default;
// If auth type is QWEN_OAUTH, prefer dashscope as default
if (authType === AuthType.QWEN_OAUTH && !defaultProvider) {
defaultProvider = 'dashscope';
}
const provider = this.params.provider || defaultProvider;
return ` (Searching the web via ${provider})`; return ` (Searching the web via ${provider})`;
} }
@@ -217,21 +209,8 @@ class WebSearchToolInvocation extends BaseToolInvocation<
} }
async execute(signal: AbortSignal): Promise<WebSearchToolResult> { async execute(signal: AbortSignal): Promise<WebSearchToolResult> {
// Guard: Check configuration exists // If tool is registered, config must exist with providers and default
const webSearchConfig = this.config.getWebSearchConfig(); const webSearchConfig = this.config.getWebSearchConfig()!;
if (!webSearchConfig) {
const message =
'Web search is disabled. Please configure web search providers in settings.json.';
return {
llmContent: message,
returnDisplay:
'Web search disabled. Configure providers to enable search.',
error: {
message,
type: ToolErrorType.EXECUTION_FAILED,
},
};
}
try { try {
// Create and select provider // Create and select provider