feat: add include_domains search filter and UI display

This commit is contained in:
JOJO 2026-03-07 17:50:35 +08:00
parent 877bcc2fad
commit c067df4e1b
9 changed files with 78 additions and 8 deletions

View File

@ -1647,6 +1647,13 @@ class MainTerminal:
"country": {
"type": "string",
"description": "国家过滤,仅 topic=general 可用,使用英文小写国名"
},
"include_domains": {
"type": "array",
"description": "仅包含这些域名可选最多300个",
"items": {
"type": "string"
}
}
}),
"required": ["query"]
@ -2209,7 +2216,8 @@ class MainTerminal:
days=arguments.get("days"),
start_date=arguments.get("start_date"),
end_date=arguments.get("end_date"),
country=arguments.get("country")
country=arguments.get("country"),
include_domains=arguments.get("include_domains")
)
if search_response["success"]:

View File

@ -424,6 +424,9 @@ class WebTerminal(MainTerminal):
filters.append(f"{arguments.get('start_date')}~{arguments.get('end_date')}")
if arguments.get("country"):
filters.append(f"country={arguments.get('country')}")
include_domains = arguments.get("include_domains")
if isinstance(include_domains, list) and include_domains:
filters.append(f"include_domains={len(include_domains)}")
filter_text = " | ".join(filter_item for filter_item in filters if filter_item)
self.broadcast('tool_status', {
'tool': tool_name,

View File

@ -2,7 +2,7 @@
import httpx
import json
from typing import Dict, Optional, Any
from typing import Dict, Optional, Any, List
from datetime import datetime
import re
try:
@ -42,7 +42,8 @@ class SearchEngine:
days: Optional[int] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
country: Optional[str] = None
country: Optional[str] = None,
include_domains: Optional[List[str]] = None
) -> Dict:
"""
执行网络搜索
@ -56,6 +57,7 @@ class SearchEngine:
start_date: 起始日期格式YYYY-MM-DD
end_date: 结束日期格式YYYY-MM-DD
country: 国家过滤仅topic=general可用
include_domains: 仅包含这些域名最多300个
Returns:
搜索结果字典
@ -75,7 +77,8 @@ class SearchEngine:
days=days,
start_date=start_date,
end_date=end_date,
country=country
country=country,
include_domains=include_domains
)
if not validation["success"]:
@ -165,7 +168,8 @@ class SearchEngine:
days: Optional[int] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
country: Optional[str] = None
country: Optional[str] = None,
include_domains: Optional[List[str]] = None
) -> Dict[str, Any]:
"""
搜索并返回格式化的摘要
@ -185,7 +189,8 @@ class SearchEngine:
days=days,
start_date=start_date,
end_date=end_date,
country=country
country=country,
include_domains=include_domains
)
if not results["success"]:
@ -326,7 +331,8 @@ class SearchEngine:
days: Optional[int],
start_date: Optional[str],
end_date: Optional[str],
country: Optional[str]
country: Optional[str],
include_domains: Optional[List[str]]
) -> Dict[str, Any]:
"""验证并构建 Tavily 请求参数"""
payload: Dict[str, Any] = {
@ -459,6 +465,35 @@ class SearchEngine:
payload["country"] = normalized_country
filters["country"] = normalized_country
# 域名白名单
if include_domains is not None:
if not isinstance(include_domains, list):
return {
"success": False,
"error": "include_domains 必须是字符串数组",
"results": []
}
cleaned_domains = []
for item in include_domains:
if not isinstance(item, str):
return {
"success": False,
"error": "include_domains 中每一项都必须是字符串",
"results": []
}
domain = item.strip().lower()
if domain:
cleaned_domains.append(domain)
if len(cleaned_domains) > 300:
return {
"success": False,
"error": f"include_domains 最多支持300个域名当前: {len(cleaned_domains)}",
"results": []
}
if cleaned_domains:
payload["include_domains"] = cleaned_domains
filters["include_domains"] = cleaned_domains
return {
"success": True,
"payload": payload,
@ -485,6 +520,8 @@ class SearchEngine:
if "country" in filters:
parts.append(f"Country: {filters['country']}")
if "include_domains" in filters:
parts.append(f"Domains: {len(filters['include_domains'])}")
if not parts:
return ""

View File

@ -196,6 +196,7 @@
:get-tool-description="getToolDescription"
:format-search-topic="formatSearchTopic"
:format-search-time="formatSearchTime"
:format-search-domains="formatSearchDomains"
/>
<VirtualMonitorSurface v-show="chatDisplayMode === 'monitor'" />

View File

@ -55,6 +55,7 @@ import {
buildToolLabel,
formatSearchTopic,
formatSearchTime,
formatSearchDomains,
getLanguageClass
} from './utils/chatDisplay';
import {
@ -4012,6 +4013,7 @@ const appOptions = {
buildToolLabel,
formatSearchTopic,
formatSearchTime,
formatSearchDomains,
getLanguageClass,
scrollToBottom() {

View File

@ -60,6 +60,7 @@
:get-tool-description="getToolDescription"
:format-search-topic="formatSearchTopic"
:format-search-time="formatSearchTime"
:format-search-domains="formatSearchDomains"
/>
<div
v-else
@ -186,6 +187,7 @@
:get-tool-description="getToolDescription"
:format-search-topic="formatSearchTopic"
:format-search-time="formatSearchTime"
:format-search-domains="formatSearchDomains"
:streaming-message="streamingMessage"
:register-collapse-content="registerCollapseContent"
:collapse-key="group.action.blockId || `${index}-tool-${group.actionIndex}`"
@ -320,6 +322,7 @@
:get-tool-description="getToolDescription"
:format-search-topic="formatSearchTopic"
:format-search-time="formatSearchTime"
:format-search-domains="formatSearchDomains"
:streaming-message="streamingMessage"
:register-collapse-content="registerCollapseContent"
:collapse-key="action.blockId || `${index}-tool-${actionIndex}`"
@ -372,6 +375,7 @@ const props = defineProps<{
getToolDescription: (tool: any) => string;
formatSearchTopic: (filters: Record<string, any>) => string;
formatSearchTime: (filters: Record<string, any>) => string;
formatSearchDomains: (filters: Record<string, any>) => string;
}>();
const personalization = usePersonalizationStore();

View File

@ -86,6 +86,7 @@
<div><strong>搜索内容</strong>{{ action.tool.result.query || action.tool.arguments?.query }}</div>
<div><strong>主题</strong>{{ formatSearchTopic(action.tool.result.filters || {}) }}</div>
<div><strong>时间范围</strong>{{ formatSearchTime(action.tool.result.filters || {}) }}</div>
<div><strong>限定网站</strong>{{ formatSearchDomains(action.tool.result.filters || {}) }}</div>
<div><strong>结果数量</strong>{{ action.tool.result.total_results }}</div>
</div>
<div v-if="action.tool.result.results && action.tool.result.results.length" class="search-result-list">
@ -139,6 +140,7 @@ const props = defineProps<{
getToolDescription: (tool: any) => string;
formatSearchTopic: (filters: Record<string, any>) => string;
formatSearchTime: (filters: Record<string, any>) => string;
formatSearchDomains: (filters: Record<string, any>) => string;
}>();
const VISIBLE_LIMIT = 6;

View File

@ -30,6 +30,7 @@
<div><strong>搜索内容</strong>{{ action.tool.result.query || action.tool.arguments.query }}</div>
<div><strong>主题</strong>{{ formatSearchTopic(action.tool.result.filters || {}) }}</div>
<div><strong>时间范围</strong>{{ formatSearchTime(action.tool.result.filters || {}) }}</div>
<div><strong>限定网站</strong>{{ formatSearchDomains(action.tool.result.filters || {}) }}</div>
<div><strong>结果数量</strong>{{ action.tool.result.total_results }}</div>
</div>
<div v-if="action.tool.result.results && action.tool.result.results.length" class="search-result-list">
@ -77,6 +78,7 @@ defineProps<{
getToolDescription: (tool: any) => string;
formatSearchTopic: (filters: Record<string, any>) => string;
formatSearchTime: (filters: Record<string, any>) => string;
formatSearchDomains: (filters: Record<string, any>) => string;
streamingMessage: boolean;
registerCollapseContent?: (key: string, el: Element | null) => void;
collapseKey?: string;

View File

@ -284,6 +284,17 @@ export function formatSearchTime(filters: ToolPayload): string {
return '未限定时间';
}
export function formatSearchDomains(filters: ToolPayload): string {
const domains = filters?.include_domains;
if (!Array.isArray(domains) || domains.length === 0) {
return '未限定网站';
}
const normalized = domains
.map(item => String(item || '').trim())
.filter(Boolean);
return normalized.length ? normalized.join(', ') : '未限定网站';
}
export function getLanguageClass(path: string): string {
if (!path) {
return 'language-plain';