feat(websearch): add overwrite functionality for search service (#4530)
* feat(websearch): add overwrite functionality for search service - Introduced new settings to allow users to override the default search service. - Updated localization files for English, Japanese, Russian, Simplified Chinese, and Traditional Chinese to include new overwrite options and tooltips. - Modified relevant components and services to support the new overwrite feature in the web search settings. * feat(websearch): enhance web search model integration * chore(websearch): unnecessary return
This commit is contained in:
parent
2a0d6eb08a
commit
ab1a5f18c9
@ -2244,7 +2244,7 @@ export function isWebSearchModel(model: Model): boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return model.type?.includes('web_search') || false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isGenerateImageModel(model: Model): boolean {
|
export function isGenerateImageModel(model: Model): boolean {
|
||||||
|
|||||||
@ -27,12 +27,15 @@ export function getWebSearchTools(model: Model): ChatCompletionTool[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
if (model?.id.includes('gemini')) {
|
||||||
{
|
return [
|
||||||
type: 'function',
|
{
|
||||||
function: {
|
type: 'function',
|
||||||
name: 'googleSearch'
|
function: {
|
||||||
|
name: 'googleSearch'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
}
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1292,7 +1292,9 @@
|
|||||||
"description": "Tavily is a search engine tailored for AI agents, delivering real-time, accurate results, intelligent query suggestions, and in-depth research capabilities.",
|
"description": "Tavily is a search engine tailored for AI agents, delivering real-time, accurate results, intelligent query suggestions, and in-depth research capabilities.",
|
||||||
"title": "Tavily"
|
"title": "Tavily"
|
||||||
},
|
},
|
||||||
"title": "Web Search"
|
"title": "Web Search",
|
||||||
|
"overwrite": "Override search service",
|
||||||
|
"overwrite_tooltip": "Force use search service instead of LLM"
|
||||||
},
|
},
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
"title": "Quick Phrases",
|
"title": "Quick Phrases",
|
||||||
|
|||||||
@ -1291,7 +1291,9 @@
|
|||||||
"description": "Tavily は、AI エージェントのために特別に開発された検索エンジンで、最新の結果、インテリジェントな検索提案、そして深い研究能力を提供します",
|
"description": "Tavily は、AI エージェントのために特別に開発された検索エンジンで、最新の結果、インテリジェントな検索提案、そして深い研究能力を提供します",
|
||||||
"title": "Tavily"
|
"title": "Tavily"
|
||||||
},
|
},
|
||||||
"title": "ウェブ検索"
|
"title": "ウェブ検索",
|
||||||
|
"overwrite": "サービス検索を上書き",
|
||||||
|
"overwrite_tooltip": "大規模言語モデルではなく、サービス検索を使用する"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "自動更新チェックを有効にする",
|
"general.auto_check_update.title": "自動更新チェックを有効にする",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -1291,7 +1291,9 @@
|
|||||||
"description": "Tavily — это поисковая система, специально разработанная для ИИ-агентов, предоставляющая актуальные результаты, умные предложения по запросам и глубокие исследовательские возможности",
|
"description": "Tavily — это поисковая система, специально разработанная для ИИ-агентов, предоставляющая актуальные результаты, умные предложения по запросам и глубокие исследовательские возможности",
|
||||||
"title": "Tavily"
|
"title": "Tavily"
|
||||||
},
|
},
|
||||||
"title": "Поиск в Интернете"
|
"title": "Поиск в Интернете",
|
||||||
|
"overwrite": "Переопределить поставщика поиска",
|
||||||
|
"overwrite_tooltip": "Использовать поставщика поиска вместо LLM"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
|
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -1279,6 +1279,8 @@
|
|||||||
"check_success": "验证成功",
|
"check_success": "验证成功",
|
||||||
"enhance_mode": "搜索增强模式",
|
"enhance_mode": "搜索增强模式",
|
||||||
"enhance_mode_tooltip": "使用默认模型提取关键词后搜索",
|
"enhance_mode_tooltip": "使用默认模型提取关键词后搜索",
|
||||||
|
"overwrite": "覆盖服务商搜索",
|
||||||
|
"overwrite_tooltip": "强制使用搜索服务商而不是大语言模型进行搜索",
|
||||||
"get_api_key": "点击这里获取密钥",
|
"get_api_key": "点击这里获取密钥",
|
||||||
"no_provider_selected": "请选择搜索服务商后再检查",
|
"no_provider_selected": "请选择搜索服务商后再检查",
|
||||||
"search_max_result": "搜索结果个数",
|
"search_max_result": "搜索结果个数",
|
||||||
|
|||||||
@ -1291,7 +1291,9 @@
|
|||||||
"description": "Tavily 是一個為 AI 代理量身訂製的搜尋引擎,提供即時、準確的結果、智慧查詢建議和深入的研究能力",
|
"description": "Tavily 是一個為 AI 代理量身訂製的搜尋引擎,提供即時、準確的結果、智慧查詢建議和深入的研究能力",
|
||||||
"title": "Tavily"
|
"title": "Tavily"
|
||||||
},
|
},
|
||||||
"title": "網路搜尋"
|
"title": "網路搜尋",
|
||||||
|
"overwrite": "覆蓋搜尋服務商",
|
||||||
|
"overwrite_tooltip": "強制使用搜尋服務商而不是大語言模型進行搜尋"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "啟用自動更新檢查",
|
"general.auto_check_update.title": "啟用自動更新檢查",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
import { DownOutlined, UpOutlined } from '@ant-design/icons'
|
import { DownOutlined, UpOutlined } from '@ant-design/icons'
|
||||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||||
import { isEmbeddingModel, isFunctionCallingModel, isReasoningModel, isVisionModel } from '@renderer/config/models'
|
import {
|
||||||
|
isEmbeddingModel,
|
||||||
|
isFunctionCallingModel,
|
||||||
|
isReasoningModel,
|
||||||
|
isVisionModel,
|
||||||
|
isWebSearchModel
|
||||||
|
} from '@renderer/config/models'
|
||||||
import { Model, ModelType } from '@renderer/types'
|
import { Model, ModelType } from '@renderer/types'
|
||||||
import { getDefaultGroupName } from '@renderer/utils'
|
import { getDefaultGroupName } from '@renderer/utils'
|
||||||
import { Button, Checkbox, Divider, Flex, Form, Input, message, Modal } from 'antd'
|
import { Button, Checkbox, Divider, Flex, Form, Input, message, Modal } from 'antd'
|
||||||
@ -121,7 +127,8 @@ const ModelEditContent: FC<ModelEditContentProps> = ({ model, onUpdateModel, ope
|
|||||||
...(isVisionModel(model) ? ['vision'] : []),
|
...(isVisionModel(model) ? ['vision'] : []),
|
||||||
...(isEmbeddingModel(model) ? ['embedding'] : []),
|
...(isEmbeddingModel(model) ? ['embedding'] : []),
|
||||||
...(isReasoningModel(model) ? ['reasoning'] : []),
|
...(isReasoningModel(model) ? ['reasoning'] : []),
|
||||||
...(isFunctionCallingModel(model) ? ['function_calling'] : [])
|
...(isFunctionCallingModel(model) ? ['function_calling'] : []),
|
||||||
|
...(isWebSearchModel(model) ? ['web_search'] : [])
|
||||||
] as ModelType[]
|
] as ModelType[]
|
||||||
|
|
||||||
// 合并现有选择和默认类型
|
// 合并现有选择和默认类型
|
||||||
@ -161,6 +168,11 @@ const ModelEditContent: FC<ModelEditContentProps> = ({ model, onUpdateModel, ope
|
|||||||
value: 'vision',
|
value: 'vision',
|
||||||
disabled: isVisionModel(model) && !selectedTypes.includes('vision')
|
disabled: isVisionModel(model) && !selectedTypes.includes('vision')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('models.type.websearch'),
|
||||||
|
value: 'web_search',
|
||||||
|
disabled: isWebSearchModel(model) && !selectedTypes.includes('web_search')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('models.type.embedding'),
|
label: t('models.type.embedding'),
|
||||||
value: 'embedding',
|
value: 'embedding',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { InfoCircleOutlined } from '@ant-design/icons'
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { setEnhanceMode, setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
import { setEnhanceMode, setMaxResult, setOverwrite, setSearchWithTime } from '@renderer/store/websearch'
|
||||||
import { Slider, Switch, Tooltip } from 'antd'
|
import { Slider, Switch, Tooltip } from 'antd'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
@ -12,6 +12,7 @@ const BasicSettings: FC = () => {
|
|||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
||||||
const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode)
|
const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode)
|
||||||
|
const overwrite = useAppSelector((state) => state.websearch.overwrite)
|
||||||
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@ -26,6 +27,16 @@ const BasicSettings: FC = () => {
|
|||||||
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>
|
||||||
|
{t('settings.websearch.overwrite')}
|
||||||
|
<Tooltip title={t('settings.websearch.overwrite_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 5, color: 'var(--color-icon)', cursor: 'pointer' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</SettingRowTitle>
|
||||||
|
<Switch checked={overwrite} onChange={(checked) => dispatch(setOverwrite(checked))} />
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>
|
<SettingRowTitle>
|
||||||
{t('settings.websearch.enhance_mode')}
|
{t('settings.websearch.enhance_mode')}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import {
|
|||||||
filterEmptyMessages,
|
filterEmptyMessages,
|
||||||
filterUserRoleStartMessages
|
filterUserRoleStartMessages
|
||||||
} from '@renderer/services/MessagesService'
|
} from '@renderer/services/MessagesService'
|
||||||
|
import WebSearchService from '@renderer/services/WebSearchService'
|
||||||
import { Assistant, FileType, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types'
|
import { Assistant, FileType, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types'
|
||||||
import { removeSpecialCharactersForTopicName } from '@renderer/utils'
|
import { removeSpecialCharactersForTopicName } from '@renderer/utils'
|
||||||
import {
|
import {
|
||||||
@ -232,7 +233,7 @@ export default class GeminiProvider extends BaseProvider {
|
|||||||
const tools = mcpToolsToGeminiTools(mcpTools)
|
const tools = mcpToolsToGeminiTools(mcpTools)
|
||||||
const toolResponses: MCPToolResponse[] = []
|
const toolResponses: MCPToolResponse[] = []
|
||||||
|
|
||||||
if (assistant.enableWebSearch && isWebSearchModel(model)) {
|
if (!WebSearchService.isOverwriteEnabled() && assistant.enableWebSearch && isWebSearchModel(model)) {
|
||||||
tools.push({
|
tools.push({
|
||||||
// @ts-ignore googleSearch is not a valid tool for Gemini
|
// @ts-ignore googleSearch is not a valid tool for Gemini
|
||||||
googleSearch: {}
|
googleSearch: {}
|
||||||
|
|||||||
@ -58,7 +58,10 @@ export async function fetchChatCompletion({
|
|||||||
|
|
||||||
// Search web
|
// Search web
|
||||||
if (WebSearchService.isWebSearchEnabled() && assistant.enableWebSearch && assistant.model) {
|
if (WebSearchService.isWebSearchEnabled() && assistant.enableWebSearch && assistant.model) {
|
||||||
const webSearchParams = getOpenAIWebSearchParams(assistant, assistant.model)
|
let webSearchParams = getOpenAIWebSearchParams(assistant, assistant.model)
|
||||||
|
if (WebSearchService.isOverwriteEnabled()) {
|
||||||
|
webSearchParams = {}
|
||||||
|
}
|
||||||
|
|
||||||
if (isEmpty(webSearchParams) && !isOpenAIWebSearch(assistant.model)) {
|
if (isEmpty(webSearchParams) && !isOpenAIWebSearch(assistant.model)) {
|
||||||
const lastMessage = findLast(messages, (m) => m.role === 'user')
|
const lastMessage = findLast(messages, (m) => m.role === 'user')
|
||||||
|
|||||||
@ -52,6 +52,16 @@ class WebSearchService {
|
|||||||
return enhanceMode
|
return enhanceMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否启用覆盖搜索
|
||||||
|
* @public
|
||||||
|
* @returns 如果启用覆盖搜索则返回true,否则返回false
|
||||||
|
*/
|
||||||
|
public isOverwriteEnabled(): boolean {
|
||||||
|
const { overwrite } = this.getWebSearchState()
|
||||||
|
return overwrite
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前默认的网络搜索提供商
|
* 获取当前默认的网络搜索提供商
|
||||||
* @public
|
* @public
|
||||||
|
|||||||
@ -14,6 +14,8 @@ export interface WebSearchState {
|
|||||||
excludeDomains: string[]
|
excludeDomains: string[]
|
||||||
// 是否启用搜索增强模式
|
// 是否启用搜索增强模式
|
||||||
enhanceMode: boolean
|
enhanceMode: boolean
|
||||||
|
// 是否覆盖服务商搜索
|
||||||
|
overwrite: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: WebSearchState = {
|
const initialState: WebSearchState = {
|
||||||
@ -38,7 +40,8 @@ const initialState: WebSearchState = {
|
|||||||
searchWithTime: true,
|
searchWithTime: true,
|
||||||
maxResults: 5,
|
maxResults: 5,
|
||||||
excludeDomains: [],
|
excludeDomains: [],
|
||||||
enhanceMode: false
|
enhanceMode: false,
|
||||||
|
overwrite: true
|
||||||
}
|
}
|
||||||
|
|
||||||
const websearchSlice = createSlice({
|
const websearchSlice = createSlice({
|
||||||
@ -71,6 +74,9 @@ const websearchSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setEnhanceMode: (state, action: PayloadAction<boolean>) => {
|
setEnhanceMode: (state, action: PayloadAction<boolean>) => {
|
||||||
state.enhanceMode = action.payload
|
state.enhanceMode = action.payload
|
||||||
|
},
|
||||||
|
setOverwrite: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.overwrite = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -83,7 +89,8 @@ export const {
|
|||||||
setSearchWithTime,
|
setSearchWithTime,
|
||||||
setExcludeDomains,
|
setExcludeDomains,
|
||||||
setMaxResult,
|
setMaxResult,
|
||||||
setEnhanceMode
|
setEnhanceMode,
|
||||||
|
setOverwrite
|
||||||
} = websearchSlice.actions
|
} = websearchSlice.actions
|
||||||
|
|
||||||
export default websearchSlice.reducer
|
export default websearchSlice.reducer
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export type Provider = {
|
|||||||
|
|
||||||
export type ProviderType = 'openai' | 'anthropic' | 'gemini' | 'qwenlm' | 'azure-openai'
|
export type ProviderType = 'openai' | 'anthropic' | 'gemini' | 'qwenlm' | 'azure-openai'
|
||||||
|
|
||||||
export type ModelType = 'text' | 'vision' | 'embedding' | 'reasoning' | 'function_calling'
|
export type ModelType = 'text' | 'vision' | 'embedding' | 'reasoning' | 'function_calling' | 'web_search'
|
||||||
|
|
||||||
export type Model = {
|
export type Model = {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user