feat:优化webui多个页面的人机交互,修复插件地址问题,放宽插件id限制,增加高级页面缩进,统计页面快捷按钮,优化新手引导
This commit is contained in:
@@ -4,13 +4,49 @@ import { parseResponse, throwIfError } from '@/lib/api-helpers'
|
||||
import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth'
|
||||
|
||||
import type {
|
||||
ApiProviderSetupConfig,
|
||||
BotBasicConfig,
|
||||
EmojiConfig,
|
||||
OtherBasicConfig,
|
||||
ModelSetupConfig,
|
||||
PersonalityConfig,
|
||||
SiliconFlowConfig,
|
||||
} from './types'
|
||||
|
||||
interface ModelInfo {
|
||||
model_identifier: string
|
||||
name: string
|
||||
api_provider: string
|
||||
price_in?: number
|
||||
cache?: boolean
|
||||
cache_price_in?: number
|
||||
price_out?: number
|
||||
force_stream_mode?: boolean
|
||||
visual?: boolean
|
||||
extra_params?: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface ApiProviderConfig {
|
||||
name: string
|
||||
base_url: string
|
||||
api_key: string
|
||||
client_type?: string
|
||||
max_retry?: number
|
||||
timeout?: number
|
||||
retry_interval?: number
|
||||
}
|
||||
|
||||
interface TaskConfig {
|
||||
model_list?: string[]
|
||||
max_tokens?: number
|
||||
temperature?: number
|
||||
slow_threshold?: number
|
||||
selection_strategy?: string
|
||||
}
|
||||
|
||||
interface ModelConfig {
|
||||
models?: ModelInfo[]
|
||||
api_providers?: ApiProviderConfig[]
|
||||
model_task_config?: Record<string, TaskConfig>
|
||||
}
|
||||
|
||||
// ===== 读取配置 =====
|
||||
|
||||
// 读取Bot基础配置
|
||||
@@ -56,73 +92,57 @@ export async function loadPersonalityConfig(): Promise<PersonalityConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
// 读取表情包配置
|
||||
export async function loadEmojiConfig(): Promise<EmojiConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{ config: { emoji?: EmojiConfig } }>(
|
||||
response
|
||||
)
|
||||
const data = throwIfError(result)
|
||||
const emojiConfig = (data.config.emoji || {}) as Partial<EmojiConfig>
|
||||
|
||||
return {
|
||||
emoji_send_num: emojiConfig.emoji_send_num ?? 25,
|
||||
max_reg_num: emojiConfig.max_reg_num ?? 64,
|
||||
do_replace: emojiConfig.do_replace ?? true,
|
||||
check_interval: emojiConfig.check_interval ?? 10,
|
||||
steal_emoji: emojiConfig.steal_emoji ?? true,
|
||||
content_filtration: emojiConfig.content_filtration ?? false,
|
||||
filtration_prompt: emojiConfig.filtration_prompt || '',
|
||||
}
|
||||
}
|
||||
|
||||
// 读取其他基础配置
|
||||
export async function loadOtherBasicConfig(): Promise<OtherBasicConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
expression?: { all_global_jargon?: boolean }
|
||||
}
|
||||
}>(response)
|
||||
const data = throwIfError(result)
|
||||
const config = data.config
|
||||
|
||||
const expressionConfig = config.expression || {}
|
||||
|
||||
return {
|
||||
all_global: expressionConfig.all_global_jargon ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
// 读取硅基流动API配置
|
||||
export async function loadSiliconFlowConfig(): Promise<SiliconFlowConfig> {
|
||||
async function loadModelConfig(): Promise<ModelConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
api_providers?: Array<{ name: string; api_key?: string }>
|
||||
}
|
||||
}>(response)
|
||||
const result = await parseResponse<{ config: ModelConfig }>(response)
|
||||
const data = throwIfError(result)
|
||||
const modelConfig = data.config
|
||||
return data.config || {}
|
||||
}
|
||||
|
||||
// 获取SiliconFlow提供商的API Key
|
||||
const apiProviders = modelConfig.api_providers || []
|
||||
const siliconFlowProvider = apiProviders.find((p) => p.name === 'SiliconFlow')
|
||||
// 读取 API 提供商配置
|
||||
export async function loadApiProviderSetupConfig(): Promise<ApiProviderSetupConfig> {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const models = modelConfig.models || []
|
||||
const taskConfig = modelConfig.model_task_config || {}
|
||||
const plannerName = taskConfig.planner?.model_list?.[0] || ''
|
||||
const replyerName = taskConfig.replyer?.model_list?.[0] || ''
|
||||
const plannerModel = models.find((model) => model.name === plannerName)
|
||||
const replyerModel = models.find((model) => model.name === replyerName)
|
||||
const providerName =
|
||||
plannerModel?.api_provider ||
|
||||
replyerModel?.api_provider ||
|
||||
modelConfig.api_providers?.[0]?.name ||
|
||||
''
|
||||
const provider = modelConfig.api_providers?.find((item) => item.name === providerName)
|
||||
|
||||
return {
|
||||
api_key: siliconFlowProvider?.api_key || '',
|
||||
provider_name: providerName,
|
||||
base_url: provider?.base_url || '',
|
||||
api_key: '',
|
||||
}
|
||||
}
|
||||
|
||||
// 读取基础模型配置
|
||||
export async function loadModelSetupConfig(): Promise<ModelSetupConfig> {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const models = modelConfig.models || []
|
||||
const taskConfig = modelConfig.model_task_config || {}
|
||||
const plannerName = taskConfig.planner?.model_list?.[0] || ''
|
||||
const replyerName = taskConfig.replyer?.model_list?.[0] || ''
|
||||
const plannerModel = models.find((model) => model.name === plannerName)
|
||||
const replyerModel = models.find((model) => model.name === replyerName)
|
||||
|
||||
return {
|
||||
planner_model_name: plannerName,
|
||||
planner_model_identifier: plannerModel?.model_identifier || plannerName,
|
||||
planner_visual: Boolean(plannerModel?.visual),
|
||||
replyer_model_name: replyerName,
|
||||
replyer_model_identifier: replyerModel?.model_identifier || replyerName,
|
||||
replyer_visual: Boolean(replyerModel?.visual),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,19 +163,6 @@ export async function saveBotBasicConfig(config: BotBasicConfig) {
|
||||
// 保存人格配置
|
||||
export async function savePersonalityConfig(config: PersonalityConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/personality', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(config),
|
||||
}
|
||||
)
|
||||
|
||||
const result = await parseResponse(response)
|
||||
return throwIfError(result)
|
||||
}
|
||||
|
||||
// 保存表情包配置
|
||||
export async function saveEmojiConfig(config: EmojiConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/emoji', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(config),
|
||||
@@ -165,58 +172,62 @@ export async function saveEmojiConfig(config: EmojiConfig) {
|
||||
return throwIfError(result)
|
||||
}
|
||||
|
||||
// 保存其他基础配置(黑话)
|
||||
export async function saveOtherBasicConfig(config: OtherBasicConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/expression', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify({ all_global_jargon: config.all_global }),
|
||||
})
|
||||
|
||||
const result = await parseResponse(response)
|
||||
return throwIfError(result)
|
||||
function createBasicModel(
|
||||
modelName: string,
|
||||
modelIdentifier: string,
|
||||
providerName: string,
|
||||
visual: boolean,
|
||||
existing?: ModelInfo
|
||||
): ModelInfo {
|
||||
return {
|
||||
price_in: 0,
|
||||
cache: false,
|
||||
cache_price_in: 0,
|
||||
price_out: 0,
|
||||
force_stream_mode: false,
|
||||
extra_params: {},
|
||||
...existing,
|
||||
visual,
|
||||
model_identifier: modelIdentifier,
|
||||
name: modelName,
|
||||
api_provider: providerName,
|
||||
}
|
||||
}
|
||||
|
||||
// 保存硅基流动API配置
|
||||
export async function saveSiliconFlowConfig(config: SiliconFlowConfig) {
|
||||
// 1. 读取现有配置
|
||||
const response = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
function upsertModel(models: ModelInfo[], model: ModelInfo): ModelInfo[] {
|
||||
const index = models.findIndex((item) => item.name === model.name)
|
||||
if (index >= 0) {
|
||||
return models.map((item, itemIndex) => (itemIndex === index ? model : item))
|
||||
}
|
||||
return [...models, model]
|
||||
}
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
api_providers?: Array<Record<string, unknown>>
|
||||
}
|
||||
}>(response)
|
||||
const currentModelConfig = throwIfError(result)
|
||||
const modelConfig = currentModelConfig.config
|
||||
// 保存 API 提供商配置
|
||||
export async function saveApiProviderSetupConfig(config: ApiProviderSetupConfig) {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const providerName = config.provider_name.trim()
|
||||
|
||||
// 2. 更新SiliconFlow提供商的API Key
|
||||
const apiProviders = modelConfig.api_providers || []
|
||||
const siliconFlowIndex = apiProviders.findIndex((p) => p.name === 'SiliconFlow')
|
||||
|
||||
if (siliconFlowIndex >= 0) {
|
||||
// 更新现有提供商的API Key
|
||||
apiProviders[siliconFlowIndex] = {
|
||||
...apiProviders[siliconFlowIndex],
|
||||
api_key: config.api_key,
|
||||
}
|
||||
} else {
|
||||
// 如果不存在,创建新的SiliconFlow提供商
|
||||
apiProviders.push({
|
||||
name: 'SiliconFlow',
|
||||
base_url: 'https://api.siliconflow.cn/v1',
|
||||
api_key: config.api_key,
|
||||
client_type: 'openai',
|
||||
max_retry: 3,
|
||||
timeout: 120,
|
||||
retry_interval: 5,
|
||||
})
|
||||
const providerIndex = apiProviders.findIndex((provider) => provider.name === providerName)
|
||||
const providerConfig: ApiProviderConfig = {
|
||||
name: providerName,
|
||||
base_url: config.base_url.trim(),
|
||||
api_key: config.api_key.trim(),
|
||||
client_type: 'openai',
|
||||
max_retry: 3,
|
||||
timeout: 120,
|
||||
retry_interval: 5,
|
||||
}
|
||||
|
||||
if (providerIndex >= 0) {
|
||||
apiProviders[providerIndex] = {
|
||||
...apiProviders[providerIndex],
|
||||
...providerConfig,
|
||||
}
|
||||
} else {
|
||||
apiProviders.push(providerConfig)
|
||||
}
|
||||
|
||||
// 3. 保存更新后的配置
|
||||
const updatedConfig = {
|
||||
...modelConfig,
|
||||
api_providers: apiProviders,
|
||||
@@ -232,6 +243,77 @@ export async function saveSiliconFlowConfig(config: SiliconFlowConfig) {
|
||||
return throwIfError(saveResult)
|
||||
}
|
||||
|
||||
// 保存基础模型配置
|
||||
export async function saveModelSetupConfig(
|
||||
config: ModelSetupConfig,
|
||||
providerName: string
|
||||
) {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const trimmedProviderName = providerName.trim()
|
||||
const plannerModelIdentifier = config.planner_model_identifier.trim()
|
||||
const plannerModelName = plannerModelIdentifier
|
||||
const replyerModelIdentifier = config.replyer_model_identifier.trim()
|
||||
const replyerModelName = replyerModelIdentifier
|
||||
|
||||
// 新增或更新 planner/replyer 模型,并仅同步 utils 到 planner。
|
||||
let models = modelConfig.models || []
|
||||
const existingPlannerModel = models.find((model) => model.name === plannerModelName)
|
||||
const existingReplyerModel = models.find((model) => model.name === replyerModelName)
|
||||
models = upsertModel(
|
||||
models,
|
||||
createBasicModel(
|
||||
plannerModelName,
|
||||
plannerModelIdentifier,
|
||||
trimmedProviderName,
|
||||
config.planner_visual,
|
||||
existingPlannerModel
|
||||
)
|
||||
)
|
||||
models = upsertModel(
|
||||
models,
|
||||
createBasicModel(
|
||||
replyerModelName,
|
||||
replyerModelIdentifier,
|
||||
trimmedProviderName,
|
||||
config.replyer_visual,
|
||||
existingReplyerModel
|
||||
)
|
||||
)
|
||||
|
||||
const modelTaskConfig = modelConfig.model_task_config || {}
|
||||
const updatedTaskConfig = {
|
||||
...modelTaskConfig,
|
||||
planner: {
|
||||
...(modelTaskConfig.planner || {}),
|
||||
model_list: [plannerModelName],
|
||||
},
|
||||
replyer: {
|
||||
...(modelTaskConfig.replyer || {}),
|
||||
model_list: [replyerModelName],
|
||||
},
|
||||
utils: {
|
||||
...(modelTaskConfig.utils || {}),
|
||||
model_list: [plannerModelName],
|
||||
},
|
||||
}
|
||||
|
||||
// vlm/voice/embedding 等其他任务配置保持原样。
|
||||
const updatedConfig = {
|
||||
...modelConfig,
|
||||
models,
|
||||
model_task_config: updatedTaskConfig,
|
||||
}
|
||||
|
||||
const saveResponse = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(updatedConfig),
|
||||
})
|
||||
|
||||
const saveResult = await parseResponse(saveResponse)
|
||||
return throwIfError(saveResult)
|
||||
}
|
||||
|
||||
// 标记设置完成
|
||||
export async function completeSetup() {
|
||||
const response = await fetchWithAuth('/api/webui/setup/complete', {
|
||||
|
||||
Reference in New Issue
Block a user