mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-04 23:18:44 +08:00
feat: 增加服务端联网搜索设置
This commit is contained in:
@@ -36,20 +36,25 @@ describe('useLlmProviderDirectory', () => {
|
|||||||
mocks.apiGet.mockReset()
|
mocks.apiGet.mockReset()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('仅为 OpenAI 兼容 runtime 显示 API 协议字段', async () => {
|
it('只为 OpenAI 兼容 runtime 或声明 Responses 工具能力的模型显示 API 协议字段', async () => {
|
||||||
mocks.apiGet.mockResolvedValue({
|
mocks.apiGet.mockResolvedValue({
|
||||||
success: true,
|
success: true,
|
||||||
data: [createProvider('openai', 'openai_compatible'), createProvider('deepseek', 'deepseek')],
|
data: [
|
||||||
|
createProvider('openai', 'openai_compatible'),
|
||||||
|
createProvider('deepseek', 'deepseek'),
|
||||||
|
createProvider('google', 'google'),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const Harness = defineComponent({
|
const Harness = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const provider = ref('openai')
|
const provider = ref('openai')
|
||||||
|
const model = ref('')
|
||||||
const directory = useLlmProviderDirectory({
|
const directory = useLlmProviderDirectory({
|
||||||
provider,
|
provider,
|
||||||
apiKey: ref(''),
|
apiKey: ref(''),
|
||||||
baseUrl: ref(''),
|
baseUrl: ref(''),
|
||||||
model: ref(''),
|
model,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -57,7 +62,12 @@ describe('useLlmProviderDirectory', () => {
|
|||||||
selectProvider: (value: string) => {
|
selectProvider: (value: string) => {
|
||||||
provider.value = value
|
provider.value = value
|
||||||
},
|
},
|
||||||
|
selectModel: (value: string) => {
|
||||||
|
model.value = value
|
||||||
|
},
|
||||||
|
loadModels: directory.loadModels,
|
||||||
showApiProtocolField: directory.showApiProtocolField,
|
showApiProtocolField: directory.showApiProtocolField,
|
||||||
|
supportsBuiltinWebSearch: directory.supportsBuiltinWebSearch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: '<div />',
|
template: '<div />',
|
||||||
@@ -71,6 +81,58 @@ describe('useLlmProviderDirectory', () => {
|
|||||||
wrapper.vm.selectProvider('deepseek')
|
wrapper.vm.selectProvider('deepseek')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.showApiProtocolField).toBe(false)
|
||||||
|
|
||||||
|
mocks.apiGet.mockResolvedValueOnce({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
id: 'deepseek-v4-flash',
|
||||||
|
name: 'deepseek-v4-flash',
|
||||||
|
server_tools: [
|
||||||
|
{
|
||||||
|
id: 'web_search',
|
||||||
|
required_api_protocol: 'responses',
|
||||||
|
client_adapter: 'openai_responses',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.vm.loadModels()
|
||||||
|
wrapper.vm.selectModel('deepseek-v4-flash')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.supportsBuiltinWebSearch).toBe(true)
|
||||||
|
expect(wrapper.vm.showApiProtocolField).toBe(true)
|
||||||
|
|
||||||
|
wrapper.vm.selectProvider('google')
|
||||||
|
await nextTick()
|
||||||
|
mocks.apiGet.mockResolvedValueOnce({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
id: 'gemini-3.6-flash-preview',
|
||||||
|
name: 'gemini-3.6-flash-preview',
|
||||||
|
server_tools: [
|
||||||
|
{
|
||||||
|
id: 'web_search',
|
||||||
|
required_api_protocol: 'native',
|
||||||
|
client_adapter: 'google_native',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.vm.loadModels()
|
||||||
|
wrapper.vm.selectModel('gemini-3.6-flash-preview')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.supportsBuiltinWebSearch).toBe(true)
|
||||||
expect(wrapper.vm.showApiProtocolField).toBe(false)
|
expect(wrapper.vm.showApiProtocolField).toBe(false)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ export interface LlmModel {
|
|||||||
source?: string
|
source?: string
|
||||||
release_date?: string | null
|
release_date?: string | null
|
||||||
status?: string | null
|
status?: string | null
|
||||||
|
server_tools?: LlmServerToolCapability[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LlmServerToolCapability {
|
||||||
|
id: string
|
||||||
|
required_api_protocol?: string
|
||||||
|
client_adapter?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LlmProviderAuthSession {
|
export interface LlmProviderAuthSession {
|
||||||
@@ -113,6 +120,10 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
const selectedModel = computed(
|
const selectedModel = computed(
|
||||||
() => models.value.find(item => item.id === normalizeValue(options.model.value)) || null,
|
() => models.value.find(item => item.id === normalizeValue(options.model.value)) || null,
|
||||||
)
|
)
|
||||||
|
const builtinWebSearchCapability = computed(() =>
|
||||||
|
selectedModel.value?.server_tools?.find(tool => tool.id === 'web_search'),
|
||||||
|
)
|
||||||
|
const supportsBuiltinWebSearch = computed(() => Boolean(builtinWebSearchCapability.value))
|
||||||
const providerItems = computed(() => providers.value.map(item => ({ title: item.name, value: item.id })))
|
const providerItems = computed(() => providers.value.map(item => ({ title: item.name, value: item.id })))
|
||||||
const baseUrlPresetItems = computed<LlmProviderUrlPresetItem[]>(() =>
|
const baseUrlPresetItems = computed<LlmProviderUrlPresetItem[]>(() =>
|
||||||
(selectedProvider.value?.base_url_presets || []).map(item => ({
|
(selectedProvider.value?.base_url_presets || []).map(item => ({
|
||||||
@@ -127,8 +138,12 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
Boolean(selectedProvider.value && (selectedProvider.value.oauth_methods || []).length === 0),
|
Boolean(selectedProvider.value && (selectedProvider.value.oauth_methods || []).length === 0),
|
||||||
)
|
)
|
||||||
const showApiKeyField = computed(() => selectedProvider.value?.supports_api_key !== false)
|
const showApiKeyField = computed(() => selectedProvider.value?.supports_api_key !== false)
|
||||||
// OpenAI 兼容接口才需要选择 API 协议(Chat Completions / Responses)。
|
// 通用 OpenAI 兼容入口或要求 Responses 的服务端工具需要显示协议选项。
|
||||||
const showApiProtocolField = computed(() => selectedProvider.value?.runtime === 'openai_compatible')
|
const showApiProtocolField = computed(
|
||||||
|
() =>
|
||||||
|
selectedProvider.value?.runtime === 'openai_compatible' ||
|
||||||
|
builtinWebSearchCapability.value?.required_api_protocol === 'responses',
|
||||||
|
)
|
||||||
const hasUsableCredential = computed(() => {
|
const hasUsableCredential = computed(() => {
|
||||||
if (providerConnected.value) return true
|
if (providerConnected.value) return true
|
||||||
return Boolean(normalizeValue(options.apiKey.value))
|
return Boolean(normalizeValue(options.apiKey.value))
|
||||||
@@ -390,6 +405,7 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
models,
|
models,
|
||||||
selectedProvider,
|
selectedProvider,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
|
supportsBuiltinWebSearch,
|
||||||
loadingProviders,
|
loadingProviders,
|
||||||
loadingModels,
|
loadingModels,
|
||||||
providerConnected,
|
providerConnected,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { copyToClipboard } from '@/@core/utils/navigator'
|
import { copyToClipboard } from '@/@core/utils/navigator'
|
||||||
import { User } from '@/api/types'
|
import type { ApiResponse, User } from '@/api/types'
|
||||||
|
|
||||||
export interface WizardData {
|
export interface WizardData {
|
||||||
basic: {
|
basic: {
|
||||||
@@ -57,6 +57,7 @@ export interface WizardData {
|
|||||||
model: string
|
model: string
|
||||||
thinkingLevel: string
|
thinkingLevel: string
|
||||||
apiProtocol: string
|
apiProtocol: string
|
||||||
|
webSearchMode: string
|
||||||
supportImageInput: boolean
|
supportImageInput: boolean
|
||||||
supportAudioInput: boolean
|
supportAudioInput: boolean
|
||||||
supportAudioOutput: boolean
|
supportAudioOutput: boolean
|
||||||
@@ -249,6 +250,7 @@ const wizardData = ref<WizardData>({
|
|||||||
model: 'deepseek-chat',
|
model: 'deepseek-chat',
|
||||||
thinkingLevel: 'off',
|
thinkingLevel: 'off',
|
||||||
apiProtocol: 'auto',
|
apiProtocol: 'auto',
|
||||||
|
webSearchMode: 'local',
|
||||||
supportImageInput: true,
|
supportImageInput: true,
|
||||||
supportAudioInput: false,
|
supportAudioInput: false,
|
||||||
supportAudioOutput: false,
|
supportAudioOutput: false,
|
||||||
@@ -1451,6 +1453,7 @@ export function useSetupWizard() {
|
|||||||
LLM_MODEL: wizardData.value.agent.model,
|
LLM_MODEL: wizardData.value.agent.model,
|
||||||
LLM_THINKING_LEVEL: wizardData.value.agent.thinkingLevel,
|
LLM_THINKING_LEVEL: wizardData.value.agent.thinkingLevel,
|
||||||
LLM_API_PROTOCOL: wizardData.value.agent.apiProtocol || 'auto',
|
LLM_API_PROTOCOL: wizardData.value.agent.apiProtocol || 'auto',
|
||||||
|
LLM_WEB_SEARCH_MODE: wizardData.value.agent.webSearchMode || 'local',
|
||||||
LLM_SUPPORT_IMAGE_INPUT: wizardData.value.agent.supportImageInput,
|
LLM_SUPPORT_IMAGE_INPUT: wizardData.value.agent.supportImageInput,
|
||||||
LLM_SUPPORT_AUDIO_INPUT: wizardData.value.agent.supportAudioInput,
|
LLM_SUPPORT_AUDIO_INPUT: wizardData.value.agent.supportAudioInput,
|
||||||
LLM_SUPPORT_AUDIO_OUTPUT: wizardData.value.agent.supportAudioOutput,
|
LLM_SUPPORT_AUDIO_OUTPUT: wizardData.value.agent.supportAudioOutput,
|
||||||
@@ -1479,7 +1482,11 @@ export function useSetupWizard() {
|
|||||||
AI_RECOMMEND_MAX_ITEMS: wizardData.value.agent.recommendMaxItems,
|
AI_RECOMMEND_MAX_ITEMS: wizardData.value.agent.recommendMaxItems,
|
||||||
}
|
}
|
||||||
|
|
||||||
await api.post('system/env', agentSettings)
|
const response: Pick<ApiResponse<unknown>, 'success' | 'message'> = await api.post('system/env', agentSettings)
|
||||||
|
if (!response.success) {
|
||||||
|
$toast.error(response.message || t('setupWizard.saveAgentSettingsFailed'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save agent settings failed:', error)
|
console.error('Save agent settings failed:', error)
|
||||||
@@ -1567,6 +1574,7 @@ export function useSetupWizard() {
|
|||||||
wizardData.value.agent.model = result.data.LLM_MODEL || ''
|
wizardData.value.agent.model = result.data.LLM_MODEL || ''
|
||||||
wizardData.value.agent.thinkingLevel = resolveThinkingLevelValue(result.data)
|
wizardData.value.agent.thinkingLevel = resolveThinkingLevelValue(result.data)
|
||||||
wizardData.value.agent.apiProtocol = result.data.LLM_API_PROTOCOL || 'auto'
|
wizardData.value.agent.apiProtocol = result.data.LLM_API_PROTOCOL || 'auto'
|
||||||
|
wizardData.value.agent.webSearchMode = result.data.LLM_WEB_SEARCH_MODE || 'local'
|
||||||
wizardData.value.agent.supportImageInput = result.data.LLM_SUPPORT_IMAGE_INPUT ?? true
|
wizardData.value.agent.supportImageInput = result.data.LLM_SUPPORT_IMAGE_INPUT ?? true
|
||||||
wizardData.value.agent.supportAudioInput = Boolean(result.data.LLM_SUPPORT_AUDIO_INPUT)
|
wizardData.value.agent.supportAudioInput = Boolean(result.data.LLM_SUPPORT_AUDIO_INPUT)
|
||||||
wizardData.value.agent.supportAudioOutput = Boolean(result.data.LLM_SUPPORT_AUDIO_OUTPUT)
|
wizardData.value.agent.supportAudioOutput = Boolean(result.data.LLM_SUPPORT_AUDIO_OUTPUT)
|
||||||
|
|||||||
@@ -1843,6 +1843,15 @@ export default {
|
|||||||
llmApiProtocolAuto: 'Auto (auto)',
|
llmApiProtocolAuto: 'Auto (auto)',
|
||||||
llmApiProtocolChatCompletions: 'Chat Completions',
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
llmApiProtocolResponses: 'Responses',
|
llmApiProtocolResponses: 'Responses',
|
||||||
|
llmWebSearchMode: 'Web Search',
|
||||||
|
llmWebSearchModeHint:
|
||||||
|
'Use MoviePilot local search, model-hosted search, automatic fallback, or disable web access. Hosted search is available only for models that declare support.',
|
||||||
|
llmWebSearchModeBuiltinSupportedHint:
|
||||||
|
'This model supports provider-hosted search. Built-in and Auto use the required protocol without a separate search API key.',
|
||||||
|
llmWebSearchModeLocal: 'MoviePilot local search',
|
||||||
|
llmWebSearchModeBuiltin: 'Model-hosted search',
|
||||||
|
llmWebSearchModeAuto: 'Auto (hosted first)',
|
||||||
|
llmWebSearchModeDisabled: 'Disable web search',
|
||||||
llmTemperature: 'Temperature',
|
llmTemperature: 'Temperature',
|
||||||
llmTemperatureHint:
|
llmTemperatureHint:
|
||||||
'Controls response randomness. Lower values are steadier and higher values are more varied. Backend default is 0.3; 0-2 is usually recommended.',
|
'Controls response randomness. Lower values are steadier and higher values are more varied. Backend default is 0.3; 0-2 is usually recommended.',
|
||||||
|
|||||||
@@ -1827,6 +1827,15 @@ export default {
|
|||||||
llmApiProtocolAuto: '自动 (auto)',
|
llmApiProtocolAuto: '自动 (auto)',
|
||||||
llmApiProtocolChatCompletions: 'Chat Completions',
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
llmApiProtocolResponses: 'Responses',
|
llmApiProtocolResponses: 'Responses',
|
||||||
|
llmWebSearchMode: '联网搜索',
|
||||||
|
llmWebSearchModeHint:
|
||||||
|
'选择 MoviePilot 本地搜索、模型服务端搜索、自动回退或完全关闭;服务端搜索仅在当前模型声明支持时可用',
|
||||||
|
llmWebSearchModeBuiltinSupportedHint:
|
||||||
|
'当前模型支持官方托管搜索;选择“模型服务端”或“自动”时会按所需协议调用,无需额外搜索密钥',
|
||||||
|
llmWebSearchModeLocal: 'MoviePilot 本地搜索',
|
||||||
|
llmWebSearchModeBuiltin: '模型服务端搜索',
|
||||||
|
llmWebSearchModeAuto: '自动(服务端优先)',
|
||||||
|
llmWebSearchModeDisabled: '关闭联网搜索',
|
||||||
llmTemperature: '温度参数',
|
llmTemperature: '温度参数',
|
||||||
llmTemperatureHint: '控制回复随机性,数值越低越稳定,越高越发散;后端默认 0.3,通常建议 0-2',
|
llmTemperatureHint: '控制回复随机性,数值越低越稳定,越高越发散;后端默认 0.3,通常建议 0-2',
|
||||||
llmProviderAuth: '提供商授权',
|
llmProviderAuth: '提供商授权',
|
||||||
|
|||||||
@@ -1826,6 +1826,15 @@ export default {
|
|||||||
llmApiProtocolAuto: '自動 (auto)',
|
llmApiProtocolAuto: '自動 (auto)',
|
||||||
llmApiProtocolChatCompletions: 'Chat Completions',
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
llmApiProtocolResponses: 'Responses',
|
llmApiProtocolResponses: 'Responses',
|
||||||
|
llmWebSearchMode: '聯網搜尋',
|
||||||
|
llmWebSearchModeHint:
|
||||||
|
'選擇 MoviePilot 本地搜尋、模型服務端搜尋、自動回退或完全關閉;服務端搜尋僅在目前模型宣告支援時可用',
|
||||||
|
llmWebSearchModeBuiltinSupportedHint:
|
||||||
|
'目前模型支援官方代管搜尋;選擇「模型服務端」或「自動」時會按所需協議呼叫,無需額外搜尋金鑰',
|
||||||
|
llmWebSearchModeLocal: 'MoviePilot 本地搜尋',
|
||||||
|
llmWebSearchModeBuiltin: '模型服務端搜尋',
|
||||||
|
llmWebSearchModeAuto: '自動(服務端優先)',
|
||||||
|
llmWebSearchModeDisabled: '關閉聯網搜尋',
|
||||||
llmTemperature: '溫度參數',
|
llmTemperature: '溫度參數',
|
||||||
llmTemperatureHint: '控制回覆隨機性,數值越低越穩定,越高越發散;後端預設 0.3,通常建議 0-2',
|
llmTemperatureHint: '控制回覆隨機性,數值越低越穩定,越高越發散;後端預設 0.3,通常建議 0-2',
|
||||||
llmProviderAuth: '提供商授權',
|
llmProviderAuth: '提供商授權',
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const SystemSettings = ref<any>({
|
|||||||
LLM_MODEL: 'deepseek-chat',
|
LLM_MODEL: 'deepseek-chat',
|
||||||
LLM_THINKING_LEVEL: 'off',
|
LLM_THINKING_LEVEL: 'off',
|
||||||
LLM_API_PROTOCOL: 'auto',
|
LLM_API_PROTOCOL: 'auto',
|
||||||
|
LLM_WEB_SEARCH_MODE: 'local',
|
||||||
LLM_SUPPORT_IMAGE_INPUT: false,
|
LLM_SUPPORT_IMAGE_INPUT: false,
|
||||||
LLM_SUPPORT_AUDIO_INPUT: false,
|
LLM_SUPPORT_AUDIO_INPUT: false,
|
||||||
LLM_SUPPORT_AUDIO_OUTPUT: false,
|
LLM_SUPPORT_AUDIO_OUTPUT: false,
|
||||||
@@ -223,6 +224,7 @@ type LlmSettingsSnapshot = {
|
|||||||
LLM_MODEL: string
|
LLM_MODEL: string
|
||||||
LLM_THINKING_LEVEL: string
|
LLM_THINKING_LEVEL: string
|
||||||
LLM_API_PROTOCOL: string
|
LLM_API_PROTOCOL: string
|
||||||
|
LLM_WEB_SEARCH_MODE: string
|
||||||
LLM_API_KEY: string
|
LLM_API_KEY: string
|
||||||
LLM_BASE_URL: string
|
LLM_BASE_URL: string
|
||||||
LLM_USE_PROXY: boolean
|
LLM_USE_PROXY: boolean
|
||||||
@@ -327,6 +329,7 @@ const {
|
|||||||
showBaseUrlField,
|
showBaseUrlField,
|
||||||
showApiKeyField,
|
showApiKeyField,
|
||||||
showApiProtocolField: showLlmApiProtocolField,
|
showApiProtocolField: showLlmApiProtocolField,
|
||||||
|
supportsBuiltinWebSearch,
|
||||||
canRefreshModels,
|
canRefreshModels,
|
||||||
setBaseUrlPreset,
|
setBaseUrlPreset,
|
||||||
authDialogVisible,
|
authDialogVisible,
|
||||||
@@ -409,6 +412,7 @@ function buildLlmSnapshot(): LlmSettingsSnapshot {
|
|||||||
LLM_MODEL: String(SystemSettings.value.Basic.LLM_MODEL ?? ''),
|
LLM_MODEL: String(SystemSettings.value.Basic.LLM_MODEL ?? ''),
|
||||||
LLM_THINKING_LEVEL: String(SystemSettings.value.Basic.LLM_THINKING_LEVEL ?? 'off'),
|
LLM_THINKING_LEVEL: String(SystemSettings.value.Basic.LLM_THINKING_LEVEL ?? 'off'),
|
||||||
LLM_API_PROTOCOL: String(SystemSettings.value.Basic.LLM_API_PROTOCOL ?? 'auto'),
|
LLM_API_PROTOCOL: String(SystemSettings.value.Basic.LLM_API_PROTOCOL ?? 'auto'),
|
||||||
|
LLM_WEB_SEARCH_MODE: String(SystemSettings.value.Basic.LLM_WEB_SEARCH_MODE ?? 'local'),
|
||||||
LLM_API_KEY: String(SystemSettings.value.Basic.LLM_API_KEY ?? ''),
|
LLM_API_KEY: String(SystemSettings.value.Basic.LLM_API_KEY ?? ''),
|
||||||
LLM_BASE_URL: String(SystemSettings.value.Basic.LLM_BASE_URL ?? ''),
|
LLM_BASE_URL: String(SystemSettings.value.Basic.LLM_BASE_URL ?? ''),
|
||||||
LLM_USE_PROXY: Boolean(SystemSettings.value.Basic.LLM_USE_PROXY),
|
LLM_USE_PROXY: Boolean(SystemSettings.value.Basic.LLM_USE_PROXY),
|
||||||
@@ -429,6 +433,7 @@ function buildLlmTestPayload(snapshot: LlmSettingsSnapshot) {
|
|||||||
model: snapshot.LLM_MODEL.trim(),
|
model: snapshot.LLM_MODEL.trim(),
|
||||||
thinking_level: snapshot.LLM_THINKING_LEVEL.trim(),
|
thinking_level: snapshot.LLM_THINKING_LEVEL.trim(),
|
||||||
api_protocol: snapshot.LLM_API_PROTOCOL.trim() || 'auto',
|
api_protocol: snapshot.LLM_API_PROTOCOL.trim() || 'auto',
|
||||||
|
web_search_mode: snapshot.LLM_WEB_SEARCH_MODE.trim() || 'local',
|
||||||
api_key: snapshot.LLM_API_KEY.trim(),
|
api_key: snapshot.LLM_API_KEY.trim(),
|
||||||
base_url: snapshot.LLM_BASE_URL.trim(),
|
base_url: snapshot.LLM_BASE_URL.trim(),
|
||||||
use_proxy: snapshot.LLM_USE_PROXY,
|
use_proxy: snapshot.LLM_USE_PROXY,
|
||||||
@@ -532,6 +537,23 @@ const apiProtocolItems = computed(() => [
|
|||||||
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const webSearchModeItems = computed(() => [
|
||||||
|
{ title: t('setting.system.llmWebSearchModeLocal'), value: 'local' },
|
||||||
|
{
|
||||||
|
title: t('setting.system.llmWebSearchModeBuiltin'),
|
||||||
|
value: 'builtin',
|
||||||
|
disabled: !supportsBuiltinWebSearch.value,
|
||||||
|
},
|
||||||
|
{ title: t('setting.system.llmWebSearchModeAuto'), value: 'auto' },
|
||||||
|
{ title: t('setting.system.llmWebSearchModeDisabled'), value: 'disabled' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const webSearchModeHint = computed(() =>
|
||||||
|
supportsBuiltinWebSearch.value
|
||||||
|
? t('setting.system.llmWebSearchModeBuiltinSupportedHint')
|
||||||
|
: t('setting.system.llmWebSearchModeHint'),
|
||||||
|
)
|
||||||
|
|
||||||
const activeTab = ref('system')
|
const activeTab = ref('system')
|
||||||
|
|
||||||
// 元数据语言
|
// 元数据语言
|
||||||
@@ -1376,6 +1398,16 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE" cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="SystemSettings.Basic.LLM_WEB_SEARCH_MODE"
|
||||||
|
:label="t('setting.system.llmWebSearchMode')"
|
||||||
|
:hint="webSearchModeHint"
|
||||||
|
persistent-hint
|
||||||
|
:items="webSearchModeItems"
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE" cols="12" md="6">
|
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE" cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model.number="SystemSettings.Basic.LLM_MAX_CONTEXT_TOKENS"
|
v-model.number="SystemSettings.Basic.LLM_MAX_CONTEXT_TOKENS"
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ const {
|
|||||||
showBaseUrlField,
|
showBaseUrlField,
|
||||||
showApiKeyField,
|
showApiKeyField,
|
||||||
showApiProtocolField,
|
showApiProtocolField,
|
||||||
|
supportsBuiltinWebSearch,
|
||||||
canRefreshModels,
|
canRefreshModels,
|
||||||
setBaseUrlPreset,
|
setBaseUrlPreset,
|
||||||
authDialogVisible,
|
authDialogVisible,
|
||||||
@@ -198,6 +199,23 @@ const apiProtocolItems = computed(() => [
|
|||||||
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const webSearchModeItems = computed(() => [
|
||||||
|
{ title: t('setting.system.llmWebSearchModeLocal'), value: 'local' },
|
||||||
|
{
|
||||||
|
title: t('setting.system.llmWebSearchModeBuiltin'),
|
||||||
|
value: 'builtin',
|
||||||
|
disabled: !supportsBuiltinWebSearch.value,
|
||||||
|
},
|
||||||
|
{ title: t('setting.system.llmWebSearchModeAuto'), value: 'auto' },
|
||||||
|
{ title: t('setting.system.llmWebSearchModeDisabled'), value: 'disabled' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const webSearchModeHint = computed(() =>
|
||||||
|
supportsBuiltinWebSearch.value
|
||||||
|
? t('setting.system.llmWebSearchModeBuiltinSupportedHint')
|
||||||
|
: t('setting.system.llmWebSearchModeHint'),
|
||||||
|
)
|
||||||
|
|
||||||
const audioProviderItems = computed(() => [
|
const audioProviderItems = computed(() => [
|
||||||
{ title: t('setting.system.audioProviderOpenAiAudio'), value: 'openai' },
|
{ title: t('setting.system.audioProviderOpenAiAudio'), value: 'openai' },
|
||||||
{ title: t('setting.system.audioProviderChatAudio'), value: 'openai_chat_audio' },
|
{ title: t('setting.system.audioProviderChatAudio'), value: 'openai_chat_audio' },
|
||||||
@@ -483,6 +501,18 @@ onMounted(async () => {
|
|||||||
</VAlert>
|
</VAlert>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.agent.webSearchMode"
|
||||||
|
:label="t('setting.system.llmWebSearchMode')"
|
||||||
|
:hint="webSearchModeHint"
|
||||||
|
:items="webSearchModeItems"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model.number="wizardData.agent.maxContextTokens"
|
v-model.number="wizardData.agent.maxContextTokens"
|
||||||
|
|||||||
Reference in New Issue
Block a user