mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 00:36:41 +08:00
feat: 后端修改前端智能助手设置 (#597)
This commit is contained in:
@@ -85,6 +85,7 @@ interface UseLlmProviderDirectoryOptions {
|
|||||||
baseUrlPreset?: Ref<string>
|
baseUrlPreset?: Ref<string>
|
||||||
useProxy?: Ref<boolean>
|
useProxy?: Ref<boolean>
|
||||||
userAgent?: Ref<string>
|
userAgent?: Ref<string>
|
||||||
|
apiProtocol?: Ref<string>
|
||||||
model: Ref<string>
|
model: Ref<string>
|
||||||
maxContextTokens?: Ref<number>
|
maxContextTokens?: Ref<number>
|
||||||
authConnected?: Ref<boolean>
|
authConnected?: Ref<boolean>
|
||||||
@@ -122,10 +123,12 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
const providerConnected = computed(() => Boolean(selectedProvider.value?.auth_status?.connected))
|
const providerConnected = computed(() => Boolean(selectedProvider.value?.auth_status?.connected))
|
||||||
const showBaseUrlField = computed(
|
const showBaseUrlField = computed(() =>
|
||||||
() => 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)。
|
||||||
|
const showApiProtocolField = computed(() => selectedProvider.value?.runtime === 'openai')
|
||||||
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))
|
||||||
@@ -195,6 +198,9 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
if (options.maxContextTokens) {
|
if (options.maxContextTokens) {
|
||||||
options.maxContextTokens.value = 64
|
options.maxContextTokens.value = 64
|
||||||
}
|
}
|
||||||
|
if (options.apiProtocol) {
|
||||||
|
options.apiProtocol.value = 'auto'
|
||||||
|
}
|
||||||
models.value = []
|
models.value = []
|
||||||
options.model.value = ''
|
options.model.value = ''
|
||||||
syncAuthConnected()
|
syncAuthConnected()
|
||||||
@@ -269,9 +275,7 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
updateProviderAuthStatus(normalizeValue(options.provider.value), payload.auth_status)
|
updateProviderAuthStatus(normalizeValue(options.provider.value), payload.auth_status)
|
||||||
|
|
||||||
const currentModelId = normalizeValue(options.model.value)
|
const currentModelId = normalizeValue(options.model.value)
|
||||||
const matchedModel = currentModelId
|
const matchedModel = currentModelId ? models.value.find(item => item.id === currentModelId) : null
|
||||||
? models.value.find(item => item.id === currentModelId)
|
|
||||||
: null
|
|
||||||
|
|
||||||
if (matchedModel) {
|
if (matchedModel) {
|
||||||
applyModelMetadata(matchedModel.id)
|
applyModelMetadata(matchedModel.id)
|
||||||
@@ -301,9 +305,7 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
authPolling.value = true
|
authPolling.value = true
|
||||||
clearPollTimer()
|
clearPollTimer()
|
||||||
try {
|
try {
|
||||||
const result: { [key: string]: any } = await api.post(
|
const result: { [key: string]: any } = await api.post(`llm/provider-auth/${authSession.value.session_id}/poll`)
|
||||||
`llm/provider-auth/${authSession.value.session_id}/poll`,
|
|
||||||
)
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.message || 'Poll LLM auth failed')
|
throw new Error(result.message || 'Poll LLM auth failed')
|
||||||
}
|
}
|
||||||
@@ -393,6 +395,7 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions)
|
|||||||
providerConnected,
|
providerConnected,
|
||||||
showBaseUrlField,
|
showBaseUrlField,
|
||||||
showApiKeyField,
|
showApiKeyField,
|
||||||
|
showApiProtocolField,
|
||||||
hasUsableCredential,
|
hasUsableCredential,
|
||||||
canRefreshModels,
|
canRefreshModels,
|
||||||
setBaseUrlPreset,
|
setBaseUrlPreset,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export interface WizardData {
|
|||||||
authConnected: boolean
|
authConnected: boolean
|
||||||
model: string
|
model: string
|
||||||
thinkingLevel: string
|
thinkingLevel: string
|
||||||
|
apiProtocol: string
|
||||||
supportImageInput: boolean
|
supportImageInput: boolean
|
||||||
supportAudioInput: boolean
|
supportAudioInput: boolean
|
||||||
supportAudioOutput: boolean
|
supportAudioOutput: boolean
|
||||||
@@ -142,7 +143,9 @@ export interface ValidationErrorState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeThinkingLevelValue(value?: unknown) {
|
function normalizeThinkingLevelValue(value?: unknown) {
|
||||||
const normalized = String(value ?? '').trim().toLowerCase()
|
const normalized = String(value ?? '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
if (!normalized) return ''
|
if (!normalized) return ''
|
||||||
|
|
||||||
const aliasMap: Record<string, string> = {
|
const aliasMap: Record<string, string> = {
|
||||||
@@ -245,6 +248,7 @@ const wizardData = ref<WizardData>({
|
|||||||
authConnected: false,
|
authConnected: false,
|
||||||
model: 'deepseek-chat',
|
model: 'deepseek-chat',
|
||||||
thinkingLevel: 'off',
|
thinkingLevel: 'off',
|
||||||
|
apiProtocol: 'auto',
|
||||||
supportImageInput: true,
|
supportImageInput: true,
|
||||||
supportAudioInput: false,
|
supportAudioInput: false,
|
||||||
supportAudioOutput: false,
|
supportAudioOutput: false,
|
||||||
@@ -593,10 +597,7 @@ export function useSetupWizard() {
|
|||||||
errors.push(t('downloader.passwordRequired'))
|
errors.push(t('downloader.passwordRequired'))
|
||||||
validationErrors.value.downloader.password = true
|
validationErrors.value.downloader.password = true
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (wizardData.value.downloader.type === 'transmission' || wizardData.value.downloader.type === 'rtorrent') {
|
||||||
wizardData.value.downloader.type === 'transmission'
|
|
||||||
|| wizardData.value.downloader.type === 'rtorrent'
|
|
||||||
) {
|
|
||||||
if (!wizardData.value.downloader.config?.username?.trim()) {
|
if (!wizardData.value.downloader.config?.username?.trim()) {
|
||||||
errors.push(t('downloader.usernameRequired'))
|
errors.push(t('downloader.usernameRequired'))
|
||||||
validationErrors.value.downloader.username = true
|
validationErrors.value.downloader.username = true
|
||||||
@@ -799,7 +800,10 @@ export function useSetupWizard() {
|
|||||||
validationErrors.value.agent.maxContextTokens = true
|
validationErrors.value.agent.maxContextTokens = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wizardData.value.agent.recommendEnabled && (!wizardData.value.agent.recommendMaxItems || wizardData.value.agent.recommendMaxItems < 1)) {
|
if (
|
||||||
|
wizardData.value.agent.recommendEnabled &&
|
||||||
|
(!wizardData.value.agent.recommendMaxItems || wizardData.value.agent.recommendMaxItems < 1)
|
||||||
|
) {
|
||||||
errors.push(t('setupWizard.agent.recommendMaxItemsRequired'))
|
errors.push(t('setupWizard.agent.recommendMaxItemsRequired'))
|
||||||
validationErrors.value.agent.recommendMaxItems = true
|
validationErrors.value.agent.recommendMaxItems = true
|
||||||
}
|
}
|
||||||
@@ -1446,6 +1450,7 @@ export function useSetupWizard() {
|
|||||||
LLM_PROVIDER: wizardData.value.agent.provider,
|
LLM_PROVIDER: wizardData.value.agent.provider,
|
||||||
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_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,
|
||||||
@@ -1469,8 +1474,7 @@ export function useSetupWizard() {
|
|||||||
AUDIO_OUTPUT_INCLUDE_TEXT: wizardData.value.agent.audioOutputIncludeText,
|
AUDIO_OUTPUT_INCLUDE_TEXT: wizardData.value.agent.audioOutputIncludeText,
|
||||||
AI_AGENT_JOB_INTERVAL: wizardData.value.agent.enabled ? wizardData.value.agent.jobInterval : 0,
|
AI_AGENT_JOB_INTERVAL: wizardData.value.agent.enabled ? wizardData.value.agent.jobInterval : 0,
|
||||||
AI_AGENT_RETRY_TRANSFER: wizardData.value.agent.enabled ? wizardData.value.agent.retryTransfer : false,
|
AI_AGENT_RETRY_TRANSFER: wizardData.value.agent.enabled ? wizardData.value.agent.retryTransfer : false,
|
||||||
AI_RECOMMEND_ENABLED:
|
AI_RECOMMEND_ENABLED: wizardData.value.agent.enabled && wizardData.value.agent.recommendEnabled,
|
||||||
wizardData.value.agent.enabled && wizardData.value.agent.recommendEnabled,
|
|
||||||
AI_RECOMMEND_USER_PREFERENCE: wizardData.value.agent.recommendUserPreference,
|
AI_RECOMMEND_USER_PREFERENCE: wizardData.value.agent.recommendUserPreference,
|
||||||
AI_RECOMMEND_MAX_ITEMS: wizardData.value.agent.recommendMaxItems,
|
AI_RECOMMEND_MAX_ITEMS: wizardData.value.agent.recommendMaxItems,
|
||||||
}
|
}
|
||||||
@@ -1562,6 +1566,7 @@ export function useSetupWizard() {
|
|||||||
wizardData.value.agent.authConnected = false
|
wizardData.value.agent.authConnected = false
|
||||||
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.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)
|
||||||
|
|||||||
@@ -1799,6 +1799,12 @@ export default {
|
|||||||
'When enabled, Agent connections to the current LLM provider use the system proxy from advanced settings.',
|
'When enabled, Agent connections to the current LLM provider use the system proxy from advanced settings.',
|
||||||
llmUserAgent: 'User-Agent',
|
llmUserAgent: 'User-Agent',
|
||||||
llmUserAgentHint: 'User-Agent sent to OpenAI-compatible APIs. Leave empty to use the SDK default.',
|
llmUserAgentHint: 'User-Agent sent to OpenAI-compatible APIs. Leave empty to use the SDK default.',
|
||||||
|
llmApiProtocol: 'API Protocol',
|
||||||
|
llmApiProtocolHint:
|
||||||
|
'Request protocol for OpenAI-compatible APIs: auto-select by model capability, or force Chat Completions / Responses.',
|
||||||
|
llmApiProtocolAuto: 'Auto (auto)',
|
||||||
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
|
llmApiProtocolResponses: 'Responses',
|
||||||
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.',
|
||||||
|
|||||||
@@ -1784,6 +1784,11 @@ export default {
|
|||||||
llmUseProxyHint: '启用后,Agent 连接当前 LLM 提供商时会应用高级设置中的系统代理',
|
llmUseProxyHint: '启用后,Agent 连接当前 LLM 提供商时会应用高级设置中的系统代理',
|
||||||
llmUserAgent: 'User-Agent',
|
llmUserAgent: 'User-Agent',
|
||||||
llmUserAgentHint: 'OpenAI 兼容接口请求使用的 User-Agent,留空则使用 SDK 默认值',
|
llmUserAgentHint: 'OpenAI 兼容接口请求使用的 User-Agent,留空则使用 SDK 默认值',
|
||||||
|
llmApiProtocol: 'API 协议',
|
||||||
|
llmApiProtocolHint: 'OpenAI 兼容接口的请求协议:自动按模型能力选择,或强制使用 Chat Completions / Responses',
|
||||||
|
llmApiProtocolAuto: '自动 (auto)',
|
||||||
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
|
llmApiProtocolResponses: 'Responses',
|
||||||
llmTemperature: '温度参数',
|
llmTemperature: '温度参数',
|
||||||
llmTemperatureHint: '控制回复随机性,数值越低越稳定,越高越发散;后端默认 0.3,通常建议 0-2',
|
llmTemperatureHint: '控制回复随机性,数值越低越稳定,越高越发散;后端默认 0.3,通常建议 0-2',
|
||||||
llmProviderAuth: '提供商授权',
|
llmProviderAuth: '提供商授权',
|
||||||
|
|||||||
@@ -1783,6 +1783,11 @@ export default {
|
|||||||
llmUseProxyHint: '啟用後,Agent 連接目前 LLM 提供商時會套用進階設定中的系統代理',
|
llmUseProxyHint: '啟用後,Agent 連接目前 LLM 提供商時會套用進階設定中的系統代理',
|
||||||
llmUserAgent: 'User-Agent',
|
llmUserAgent: 'User-Agent',
|
||||||
llmUserAgentHint: 'OpenAI 兼容接口請求使用的 User-Agent,留空則使用 SDK 預設值',
|
llmUserAgentHint: 'OpenAI 兼容接口請求使用的 User-Agent,留空則使用 SDK 預設值',
|
||||||
|
llmApiProtocol: 'API 協議',
|
||||||
|
llmApiProtocolHint: 'OpenAI 兼容接口的請求協議:自動按模型能力選擇,或強制使用 Chat Completions / Responses',
|
||||||
|
llmApiProtocolAuto: '自動 (auto)',
|
||||||
|
llmApiProtocolChatCompletions: 'Chat Completions',
|
||||||
|
llmApiProtocolResponses: 'Responses',
|
||||||
llmTemperature: '溫度參數',
|
llmTemperature: '溫度參數',
|
||||||
llmTemperatureHint: '控制回覆隨機性,數值越低越穩定,越高越發散;後端預設 0.3,通常建議 0-2',
|
llmTemperatureHint: '控制回覆隨機性,數值越低越穩定,越高越發散;後端預設 0.3,通常建議 0-2',
|
||||||
llmProviderAuth: '提供商授權',
|
llmProviderAuth: '提供商授權',
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const SystemSettings = ref<any>({
|
|||||||
LLM_PROVIDER: 'deepseek',
|
LLM_PROVIDER: 'deepseek',
|
||||||
LLM_MODEL: 'deepseek-chat',
|
LLM_MODEL: 'deepseek-chat',
|
||||||
LLM_THINKING_LEVEL: 'off',
|
LLM_THINKING_LEVEL: 'off',
|
||||||
|
LLM_API_PROTOCOL: 'auto',
|
||||||
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,
|
||||||
@@ -221,6 +222,7 @@ type LlmSettingsSnapshot = {
|
|||||||
LLM_PROVIDER: string
|
LLM_PROVIDER: string
|
||||||
LLM_MODEL: string
|
LLM_MODEL: string
|
||||||
LLM_THINKING_LEVEL: string
|
LLM_THINKING_LEVEL: string
|
||||||
|
LLM_API_PROTOCOL: 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
|
||||||
@@ -292,6 +294,13 @@ const llmUserAgentRef = computed({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const llmApiProtocolRef = computed({
|
||||||
|
get: () => String(SystemSettings.value.Basic.LLM_API_PROTOCOL ?? 'auto'),
|
||||||
|
set: value => {
|
||||||
|
SystemSettings.value.Basic.LLM_API_PROTOCOL = value || 'auto'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const llmModelRef = computed({
|
const llmModelRef = computed({
|
||||||
get: () => String(SystemSettings.value.Basic.LLM_MODEL ?? ''),
|
get: () => String(SystemSettings.value.Basic.LLM_MODEL ?? ''),
|
||||||
set: value => {
|
set: value => {
|
||||||
@@ -317,6 +326,7 @@ const {
|
|||||||
providerConnected,
|
providerConnected,
|
||||||
showBaseUrlField,
|
showBaseUrlField,
|
||||||
showApiKeyField,
|
showApiKeyField,
|
||||||
|
showApiProtocolField: showLlmApiProtocolField,
|
||||||
canRefreshModels,
|
canRefreshModels,
|
||||||
setBaseUrlPreset,
|
setBaseUrlPreset,
|
||||||
authDialogVisible,
|
authDialogVisible,
|
||||||
@@ -339,6 +349,7 @@ const {
|
|||||||
baseUrlPreset: llmBaseUrlPresetRef,
|
baseUrlPreset: llmBaseUrlPresetRef,
|
||||||
useProxy: llmUseProxyRef,
|
useProxy: llmUseProxyRef,
|
||||||
userAgent: llmUserAgentRef,
|
userAgent: llmUserAgentRef,
|
||||||
|
apiProtocol: llmApiProtocolRef,
|
||||||
model: llmModelRef,
|
model: llmModelRef,
|
||||||
maxContextTokens: llmMaxContextRef,
|
maxContextTokens: llmMaxContextRef,
|
||||||
})
|
})
|
||||||
@@ -397,6 +408,7 @@ function buildLlmSnapshot(): LlmSettingsSnapshot {
|
|||||||
LLM_PROVIDER: String(SystemSettings.value.Basic.LLM_PROVIDER ?? ''),
|
LLM_PROVIDER: String(SystemSettings.value.Basic.LLM_PROVIDER ?? ''),
|
||||||
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_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),
|
||||||
@@ -416,6 +428,7 @@ function buildLlmTestPayload(snapshot: LlmSettingsSnapshot) {
|
|||||||
provider: snapshot.LLM_PROVIDER.trim(),
|
provider: snapshot.LLM_PROVIDER.trim(),
|
||||||
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_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,
|
||||||
@@ -513,6 +526,12 @@ const thinkingLevelItems = computed(() => [
|
|||||||
{ title: t('setting.system.llmThinkingLevelXhigh'), value: 'xhigh' },
|
{ title: t('setting.system.llmThinkingLevelXhigh'), value: 'xhigh' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const apiProtocolItems = computed(() => [
|
||||||
|
{ title: t('setting.system.llmApiProtocolAuto'), value: 'auto' },
|
||||||
|
{ title: t('setting.system.llmApiProtocolChatCompletions'), value: 'chat_completions' },
|
||||||
|
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
||||||
|
])
|
||||||
|
|
||||||
const activeTab = ref('system')
|
const activeTab = ref('system')
|
||||||
|
|
||||||
// 元数据语言
|
// 元数据语言
|
||||||
@@ -608,9 +627,7 @@ function addSecurityDomain() {
|
|||||||
function addImageProxyAllowedPrivateRange() {
|
function addImageProxyAllowedPrivateRange() {
|
||||||
if (
|
if (
|
||||||
newImageProxyAllowedPrivateRange.value &&
|
newImageProxyAllowedPrivateRange.value &&
|
||||||
!SystemSettings.value.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.includes(
|
!SystemSettings.value.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.includes(newImageProxyAllowedPrivateRange.value)
|
||||||
newImageProxyAllowedPrivateRange.value,
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
SystemSettings.value.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.push(newImageProxyAllowedPrivateRange.value)
|
SystemSettings.value.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.push(newImageProxyAllowedPrivateRange.value)
|
||||||
newImageProxyAllowedPrivateRange.value = ''
|
newImageProxyAllowedPrivateRange.value = ''
|
||||||
@@ -1214,6 +1231,16 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
|
|||||||
@update:model-value="handleLlmProviderChanged"
|
@update:model-value="handleLlmProviderChanged"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE && showLlmApiProtocolField" cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="SystemSettings.Basic.LLM_API_PROTOCOL"
|
||||||
|
:label="t('setting.system.llmApiProtocol')"
|
||||||
|
:hint="t('setting.system.llmApiProtocolHint')"
|
||||||
|
persistent-hint
|
||||||
|
:items="apiProtocolItems"
|
||||||
|
prepend-inner-icon="mdi-swap-horizontal"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE && showBaseUrlField" cols="12" md="6">
|
<VCol v-if="SystemSettings.Basic.AI_AGENT_ENABLE && showBaseUrlField" cols="12" md="6">
|
||||||
<VCombobox
|
<VCombobox
|
||||||
:model-value="SystemSettings.Basic.LLM_BASE_URL"
|
:model-value="SystemSettings.Basic.LLM_BASE_URL"
|
||||||
@@ -2265,9 +2292,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
|
|||||||
v-for="(range, index) in SystemSettings.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES"
|
v-for="(range, index) in SystemSettings.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES"
|
||||||
:key="index"
|
:key="index"
|
||||||
closable
|
closable
|
||||||
@click:close="
|
@click:close="SystemSettings.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.splice(index, 1)"
|
||||||
SystemSettings.Advanced.IMAGE_PROXY_ALLOWED_PRIVATE_RANGES.splice(index, 1)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ range }}
|
{{ range }}
|
||||||
</VChip>
|
</VChip>
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ const userAgentRef = computed({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const apiProtocolRef = computed({
|
||||||
|
get: () => wizardData.value.agent.apiProtocol,
|
||||||
|
set: value => {
|
||||||
|
wizardData.value.agent.apiProtocol = value || 'auto'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const modelRef = computed({
|
const modelRef = computed({
|
||||||
get: () => wizardData.value.agent.model,
|
get: () => wizardData.value.agent.model,
|
||||||
set: value => {
|
set: value => {
|
||||||
@@ -86,6 +93,7 @@ const {
|
|||||||
providerConnected,
|
providerConnected,
|
||||||
showBaseUrlField,
|
showBaseUrlField,
|
||||||
showApiKeyField,
|
showApiKeyField,
|
||||||
|
showApiProtocolField,
|
||||||
canRefreshModels,
|
canRefreshModels,
|
||||||
setBaseUrlPreset,
|
setBaseUrlPreset,
|
||||||
authDialogVisible,
|
authDialogVisible,
|
||||||
@@ -108,6 +116,7 @@ const {
|
|||||||
baseUrlPreset: baseUrlPresetRef,
|
baseUrlPreset: baseUrlPresetRef,
|
||||||
useProxy: useProxyRef,
|
useProxy: useProxyRef,
|
||||||
userAgent: userAgentRef,
|
userAgent: userAgentRef,
|
||||||
|
apiProtocol: apiProtocolRef,
|
||||||
model: modelRef,
|
model: modelRef,
|
||||||
maxContextTokens: maxContextTokensRef,
|
maxContextTokens: maxContextTokensRef,
|
||||||
authConnected: authConnectedRef,
|
authConnected: authConnectedRef,
|
||||||
@@ -183,6 +192,12 @@ const thinkingLevelItems = computed(() => [
|
|||||||
{ title: t('setting.system.llmThinkingLevelXhigh'), value: 'xhigh' },
|
{ title: t('setting.system.llmThinkingLevelXhigh'), value: 'xhigh' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const apiProtocolItems = computed(() => [
|
||||||
|
{ title: t('setting.system.llmApiProtocolAuto'), value: 'auto' },
|
||||||
|
{ title: t('setting.system.llmApiProtocolChatCompletions'), value: 'chat_completions' },
|
||||||
|
{ title: t('setting.system.llmApiProtocolResponses'), value: 'responses' },
|
||||||
|
])
|
||||||
|
|
||||||
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' },
|
||||||
@@ -324,16 +339,30 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
|
<VCol v-if="showApiProtocolField" cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.agent.apiProtocol"
|
||||||
|
:label="t('setting.system.llmApiProtocol')"
|
||||||
|
:hint="t('setting.system.llmApiProtocolHint')"
|
||||||
|
:items="apiProtocolItems"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-swap-horizontal"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
<VCol v-if="showBaseUrlField" cols="12" md="6">
|
<VCol v-if="showBaseUrlField" cols="12" md="6">
|
||||||
<VCombobox
|
<VCombobox
|
||||||
:model-value="wizardData.agent.baseUrl"
|
:model-value="wizardData.agent.baseUrl"
|
||||||
@update:model-value="(value: any) => {
|
@update:model-value="
|
||||||
if (typeof value === 'object' && value !== null) {
|
(value: any) => {
|
||||||
setBaseUrlPreset(value.id, value.value);
|
if (typeof value === 'object' && value !== null) {
|
||||||
} else {
|
setBaseUrlPreset(value.id, value.value)
|
||||||
setBaseUrlPreset('', value || '');
|
} else {
|
||||||
|
setBaseUrlPreset('', value || '')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}"
|
"
|
||||||
:label="t('setting.system.llmBaseUrl')"
|
:label="t('setting.system.llmBaseUrl')"
|
||||||
:hint="t('setting.system.llmBaseUrlHint')"
|
:hint="t('setting.system.llmBaseUrlHint')"
|
||||||
:placeholder="selectedProvider?.default_base_url || 'https://api.deepseek.com'"
|
:placeholder="selectedProvider?.default_base_url || 'https://api.deepseek.com'"
|
||||||
@@ -366,9 +395,7 @@ onMounted(async () => {
|
|||||||
:hint="selectedProvider?.api_key_hint || t('setting.system.llmApiKeyHint')"
|
:hint="selectedProvider?.api_key_hint || t('setting.system.llmApiKeyHint')"
|
||||||
:placeholder="t('setting.system.llmApiKeyPlaceholder')"
|
:placeholder="t('setting.system.llmApiKeyPlaceholder')"
|
||||||
:error="validationErrors.agent.apiKey"
|
:error="validationErrors.agent.apiKey"
|
||||||
:error-messages="
|
:error-messages="validationErrors.agent.apiKey ? [t('setupWizard.agent.authOrApiKeyRequired')] : []"
|
||||||
validationErrors.agent.apiKey ? [t('setupWizard.agent.authOrApiKeyRequired')] : []
|
|
||||||
"
|
|
||||||
persistent-hint
|
persistent-hint
|
||||||
prepend-inner-icon="mdi-key-variant"
|
prepend-inner-icon="mdi-key-variant"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -384,7 +411,9 @@ onMounted(async () => {
|
|||||||
{{ selectedProvider?.description || t('setting.system.llmProviderAuthHint') }}
|
{{ selectedProvider?.description || t('setting.system.llmProviderAuthHint') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="providerConnected" class="text-body-2 mt-2">
|
<div v-if="providerConnected" class="text-body-2 mt-2">
|
||||||
{{ t('setting.system.llmProviderConnectedAs', { label: providerAuthLabel || selectedProvider?.name }) }}
|
{{
|
||||||
|
t('setting.system.llmProviderConnectedAs', { label: providerAuthLabel || selectedProvider?.name })
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -417,10 +446,12 @@ onMounted(async () => {
|
|||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VCombobox
|
<VCombobox
|
||||||
:model-value="wizardData.agent.model"
|
:model-value="wizardData.agent.model"
|
||||||
@update:model-value="(val: any) => {
|
@update:model-value="
|
||||||
wizardData.agent.model = typeof val === 'object' && val !== null ? val.id : val;
|
(val: any) => {
|
||||||
handleModelChanged();
|
wizardData.agent.model = typeof val === 'object' && val !== null ? val.id : val
|
||||||
}"
|
handleModelChanged()
|
||||||
|
}
|
||||||
|
"
|
||||||
:label="t('setting.system.llmModel')"
|
:label="t('setting.system.llmModel')"
|
||||||
:hint="t('setting.system.llmModelHint')"
|
:hint="t('setting.system.llmModelHint')"
|
||||||
:items="llmModels"
|
:items="llmModels"
|
||||||
|
|||||||
Reference in New Issue
Block a user