refactor(api): migrate config-api to ApiResponse pattern
This commit is contained in:
@@ -2,146 +2,96 @@
|
|||||||
* 配置API客户端
|
* 配置API客户端
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { parseResponse } from '@/lib/api-helpers'
|
||||||
import { fetchWithAuth } from '@/lib/fetch-with-auth'
|
import { fetchWithAuth } from '@/lib/fetch-with-auth'
|
||||||
import type {
|
import type { ApiResponse } from '@/types/api'
|
||||||
ConfigSchema,
|
import type { ConfigSchema } from '@/types/config-schema'
|
||||||
ConfigSchemaResponse,
|
|
||||||
ConfigDataResponse,
|
|
||||||
ConfigUpdateResponse,
|
|
||||||
} from '@/types/config-schema'
|
|
||||||
|
|
||||||
const API_BASE = '/api/webui/config'
|
const API_BASE = '/api/webui/config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取麦麦主程序配置架构
|
* 获取麦麦主程序配置架构
|
||||||
*/
|
*/
|
||||||
export async function getBotConfigSchema(): Promise<ConfigSchema> {
|
export async function getBotConfigSchema(): Promise<ApiResponse<ConfigSchema>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/schema/bot`)
|
const response = await fetchWithAuth(`${API_BASE}/schema/bot`)
|
||||||
const data: ConfigSchemaResponse = await response.json()
|
return parseResponse<ConfigSchema>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取配置架构失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.schema
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取模型配置架构
|
* 获取模型配置架构
|
||||||
*/
|
*/
|
||||||
export async function getModelConfigSchema(): Promise<ConfigSchema> {
|
export async function getModelConfigSchema(): Promise<ApiResponse<ConfigSchema>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/schema/model`)
|
const response = await fetchWithAuth(`${API_BASE}/schema/model`)
|
||||||
const data: ConfigSchemaResponse = await response.json()
|
return parseResponse<ConfigSchema>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取模型配置架构失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.schema
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定配置节的架构
|
* 获取指定配置节的架构
|
||||||
*/
|
*/
|
||||||
export async function getConfigSectionSchema(sectionName: string): Promise<ConfigSchema> {
|
export async function getConfigSectionSchema(sectionName: string): Promise<ApiResponse<ConfigSchema>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/schema/section/${sectionName}`)
|
const response = await fetchWithAuth(`${API_BASE}/schema/section/${sectionName}`)
|
||||||
const data: ConfigSchemaResponse = await response.json()
|
return parseResponse<ConfigSchema>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(`获取配置节 ${sectionName} 架构失败`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.schema
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取麦麦主程序配置数据
|
* 获取麦麦主程序配置数据
|
||||||
*/
|
*/
|
||||||
export async function getBotConfig(): Promise<Record<string, unknown>> {
|
export async function getBotConfig(): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/bot`)
|
const response = await fetchWithAuth(`${API_BASE}/bot`)
|
||||||
const data: ConfigDataResponse = await response.json()
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取配置数据失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取模型配置数据
|
* 获取模型配置数据
|
||||||
*/
|
*/
|
||||||
export async function getModelConfig(): Promise<Record<string, unknown>> {
|
export async function getModelConfig(): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/model`)
|
const response = await fetchWithAuth(`${API_BASE}/model`)
|
||||||
const data: ConfigDataResponse = await response.json()
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取模型配置数据失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新麦麦主程序配置
|
* 更新麦麦主程序配置
|
||||||
*/
|
*/
|
||||||
export async function updateBotConfig(config: Record<string, unknown>): Promise<void> {
|
export async function updateBotConfig(
|
||||||
|
config: Record<string, unknown>
|
||||||
|
): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/bot`, {
|
const response = await fetchWithAuth(`${API_BASE}/bot`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(config),
|
body: JSON.stringify(config),
|
||||||
})
|
})
|
||||||
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
const data: ConfigUpdateResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || '保存配置失败')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取麦麦主程序配置的原始 TOML 内容
|
* 获取麦麦主程序配置的原始 TOML 内容
|
||||||
*/
|
*/
|
||||||
export async function getBotConfigRaw(): Promise<string> {
|
export async function getBotConfigRaw(): Promise<ApiResponse<string>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/bot/raw`)
|
const response = await fetchWithAuth(`${API_BASE}/bot/raw`)
|
||||||
const data: { success: boolean; content: string } = await response.json()
|
return parseResponse<string>(response)
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取配置源代码失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新麦麦主程序配置(原始 TOML 内容)
|
* 更新麦麦主程序配置(原始 TOML 内容)
|
||||||
*/
|
*/
|
||||||
export async function updateBotConfigRaw(rawContent: string): Promise<void> {
|
export async function updateBotConfigRaw(rawContent: string): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/bot/raw`, {
|
const response = await fetchWithAuth(`${API_BASE}/bot/raw`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ raw_content: rawContent }),
|
body: JSON.stringify({ raw_content: rawContent }),
|
||||||
})
|
})
|
||||||
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
const data: ConfigUpdateResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || '保存配置失败')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新模型配置
|
* 更新模型配置
|
||||||
*/
|
*/
|
||||||
export async function updateModelConfig(config: Record<string, unknown>): Promise<void> {
|
export async function updateModelConfig(
|
||||||
|
config: Record<string, unknown>
|
||||||
|
): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/model`, {
|
const response = await fetchWithAuth(`${API_BASE}/model`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(config),
|
body: JSON.stringify(config),
|
||||||
})
|
})
|
||||||
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
const data: ConfigUpdateResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || '保存配置失败')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,17 +100,12 @@ export async function updateModelConfig(config: Record<string, unknown>): Promis
|
|||||||
export async function updateBotConfigSection(
|
export async function updateBotConfigSection(
|
||||||
sectionName: string,
|
sectionName: string,
|
||||||
sectionData: unknown
|
sectionData: unknown
|
||||||
): Promise<void> {
|
): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/bot/section/${sectionName}`, {
|
const response = await fetchWithAuth(`${API_BASE}/bot/section/${sectionName}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(sectionData),
|
body: JSON.stringify(sectionData),
|
||||||
})
|
})
|
||||||
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
const data: ConfigUpdateResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || `保存配置节 ${sectionName} 失败`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,17 +114,12 @@ export async function updateBotConfigSection(
|
|||||||
export async function updateModelConfigSection(
|
export async function updateModelConfigSection(
|
||||||
sectionName: string,
|
sectionName: string,
|
||||||
sectionData: unknown
|
sectionData: unknown
|
||||||
): Promise<void> {
|
): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`${API_BASE}/model/section/${sectionName}`, {
|
const response = await fetchWithAuth(`${API_BASE}/model/section/${sectionName}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(sectionData),
|
body: JSON.stringify(sectionData),
|
||||||
})
|
})
|
||||||
|
return parseResponse<Record<string, unknown>>(response)
|
||||||
const data: ConfigUpdateResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || `保存配置节 ${sectionName} 失败`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,28 +151,14 @@ export async function fetchProviderModels(
|
|||||||
providerName: string,
|
providerName: string,
|
||||||
parser: 'openai' | 'gemini' = 'openai',
|
parser: 'openai' | 'gemini' = 'openai',
|
||||||
endpoint: string = '/models'
|
endpoint: string = '/models'
|
||||||
): Promise<ModelListItem[]> {
|
): Promise<ApiResponse<ModelListItem[]>> {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
provider_name: providerName,
|
provider_name: providerName,
|
||||||
parser,
|
parser,
|
||||||
endpoint,
|
endpoint,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetchWithAuth(`/api/webui/models/list?${params}`)
|
const response = await fetchWithAuth(`/api/webui/models/list?${params}`)
|
||||||
|
return parseResponse<ModelListItem[]>(response)
|
||||||
// 处理非 2xx 响应
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({}))
|
|
||||||
throw new Error(errorData.detail || `获取模型列表失败 (${response.status})`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: FetchModelsResponse = await response.json()
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error('获取模型列表失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.models
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,20 +176,14 @@ export interface TestConnectionResult {
|
|||||||
* 测试提供商连接状态(通过提供商名称)
|
* 测试提供商连接状态(通过提供商名称)
|
||||||
* @param providerName 提供商名称
|
* @param providerName 提供商名称
|
||||||
*/
|
*/
|
||||||
export async function testProviderConnection(providerName: string): Promise<TestConnectionResult> {
|
export async function testProviderConnection(
|
||||||
|
providerName: string
|
||||||
|
): Promise<ApiResponse<TestConnectionResult>> {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
provider_name: providerName,
|
provider_name: providerName,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetchWithAuth(`/api/webui/models/test-connection-by-name?${params}`, {
|
const response = await fetchWithAuth(`/api/webui/models/test-connection-by-name?${params}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
|
return parseResponse<TestConnectionResult>(response)
|
||||||
// 处理非 2xx 响应
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({}))
|
|
||||||
throw new Error(errorData.detail || `测试连接失败 (${response.status})`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,16 @@ function BotConfigPageContent() {
|
|||||||
// 加载源代码
|
// 加载源代码
|
||||||
const loadSourceCode = useCallback(async () => {
|
const loadSourceCode = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const raw = await getBotConfigRaw()
|
const result = await getBotConfigRaw()
|
||||||
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
variant: 'destructive',
|
||||||
|
title: '加载失败',
|
||||||
|
description: result.error,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const raw = result.data
|
||||||
// 将 TOML 基本字符串中的转义序列转换为实际字符以便在编辑器中正确显示
|
// 将 TOML 基本字符串中的转义序列转换为实际字符以便在编辑器中正确显示
|
||||||
// 使用正则表达式只处理双引号字符串内的转义序列,不影响单引号字符串
|
// 使用正则表达式只处理双引号字符串内的转义序列,不影响单引号字符串
|
||||||
const unescaped = raw.replace(/"([^"]*)"/g, (_match, content) => {
|
const unescaped = raw.replace(/"([^"]*)"/g, (_match, content) => {
|
||||||
@@ -289,8 +298,17 @@ function BotConfigPageContent() {
|
|||||||
const loadConfig = useCallback(async () => {
|
const loadConfig = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const config = await getBotConfig()
|
const result = await getBotConfig()
|
||||||
parseAndSetConfig(config)
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parseAndSetConfig(result.data)
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
initialLoadRef.current = false
|
initialLoadRef.current = false
|
||||||
|
|
||||||
@@ -382,7 +400,18 @@ function BotConfigPageContent() {
|
|||||||
.replace(/\r/g, '\\r') // 回车符
|
.replace(/\r/g, '\\r') // 回车符
|
||||||
return `"${encoded}"`
|
return `"${encoded}"`
|
||||||
})
|
})
|
||||||
await updateBotConfigRaw(escaped)
|
const result = await updateBotConfigRaw(escaped)
|
||||||
|
if (!result.success) {
|
||||||
|
setHasTomlError(true)
|
||||||
|
const errorMsg = result.error
|
||||||
|
setTomlErrorMessage(errorMsg)
|
||||||
|
toast({
|
||||||
|
variant: 'destructive',
|
||||||
|
title: '保存失败',
|
||||||
|
description: errorMsg,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
setHasTomlError(false)
|
setHasTomlError(false)
|
||||||
setTomlErrorMessage('')
|
setTomlErrorMessage('')
|
||||||
@@ -423,8 +452,16 @@ function BotConfigPageContent() {
|
|||||||
} else {
|
} else {
|
||||||
// 切换回可视化时,直接重新加载配置但不显示全局 loading
|
// 切换回可视化时,直接重新加载配置但不显示全局 loading
|
||||||
try {
|
try {
|
||||||
const config = await getBotConfig()
|
const result = await getBotConfig()
|
||||||
parseAndSetConfig(config)
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parseAndSetConfig(result.data)
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载配置失败:', error)
|
console.error('加载配置失败:', error)
|
||||||
@@ -444,7 +481,16 @@ function BotConfigPageContent() {
|
|||||||
// 取消待处理的自动保存
|
// 取消待处理的自动保存
|
||||||
cancelPendingAutoSave()
|
cancelPendingAutoSave()
|
||||||
|
|
||||||
await updateBotConfig(buildFullConfig())
|
const result = await updateBotConfig(buildFullConfig())
|
||||||
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
@@ -474,7 +520,16 @@ function BotConfigPageContent() {
|
|||||||
// 取消待处理的自动保存
|
// 取消待处理的自动保存
|
||||||
cancelPendingAutoSave()
|
cancelPendingAutoSave()
|
||||||
|
|
||||||
await updateBotConfig(buildFullConfig())
|
const result = await updateBotConfig(buildFullConfig())
|
||||||
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
|
|||||||
@@ -187,6 +187,13 @@ export function useAutoSave(
|
|||||||
const saveSection = useCallback(
|
const saveSection = useCallback(
|
||||||
async (sectionName: ConfigSectionName, sectionData: unknown) => {
|
async (sectionName: ConfigSectionName, sectionData: unknown) => {
|
||||||
try {
|
try {
|
||||||
|
setAutoSaving(true)
|
||||||
|
const result = await updateBotConfigSection(sectionName, sectionData)
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
setHasUnsavedChanges(false)
|
||||||
|
onSaveSuccess?.()
|
||||||
setAutoSaving(true)
|
setAutoSaving(true)
|
||||||
await updateBotConfigSection(sectionName, sectionData)
|
await updateBotConfigSection(sectionName, sectionData)
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
|
|||||||
@@ -179,7 +179,17 @@ function ModelConfigPageContent() {
|
|||||||
const loadConfig = useCallback(async () => {
|
const loadConfig = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const config = await getModelConfig()
|
const result = await getModelConfig()
|
||||||
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = result.data
|
||||||
const modelList = (config.models as ModelInfo[]) || []
|
const modelList = (config.models as ModelInfo[]) || []
|
||||||
setModels(modelList)
|
setModels(modelList)
|
||||||
|
|
||||||
@@ -288,11 +298,30 @@ function ModelConfigPageContent() {
|
|||||||
try {
|
try {
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
clearAutoSaveTimers()
|
clearAutoSaveTimers()
|
||||||
const config = await getModelConfig()
|
const resultGet = await getModelConfig()
|
||||||
|
if (!resultGet.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultGet.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = resultGet.data
|
||||||
// 清理每个模型中的 null 值
|
// 清理每个模型中的 null 值
|
||||||
config.models = models.map(cleanModelForSave)
|
config.models = models.map(cleanModelForSave)
|
||||||
config.model_task_config = taskConfig
|
config.model_task_config = taskConfig
|
||||||
await updateModelConfig(config)
|
const resultUpdate = await updateModelConfig(config)
|
||||||
|
if (!resultUpdate.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultUpdate.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
@@ -318,11 +347,30 @@ function ModelConfigPageContent() {
|
|||||||
// 先取消自动保存定时器
|
// 先取消自动保存定时器
|
||||||
clearAutoSaveTimers()
|
clearAutoSaveTimers()
|
||||||
|
|
||||||
const config = await getModelConfig()
|
const resultGet = await getModelConfig()
|
||||||
|
if (!resultGet.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultGet.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = resultGet.data
|
||||||
// 清理每个模型中的 null 值
|
// 清理每个模型中的 null 值
|
||||||
config.models = models.map(cleanModelForSave)
|
config.models = models.map(cleanModelForSave)
|
||||||
config.model_task_config = taskConfig
|
config.model_task_config = taskConfig
|
||||||
await updateModelConfig(config)
|
const resultUpdate = await updateModelConfig(config)
|
||||||
|
if (!resultUpdate.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultUpdate.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* 监听 models 和 taskConfig 变化,自动保存到服务器
|
* 监听 models 和 taskConfig 变化,自动保存到服务器
|
||||||
*/
|
*/
|
||||||
import { useRef, useEffect, useCallback } from 'react'
|
import { useRef, useEffect, useCallback } from 'react'
|
||||||
|
import type { RefObject } from 'react'
|
||||||
import { updateModelConfigSection } from '@/lib/config-api'
|
import { updateModelConfigSection } from '@/lib/config-api'
|
||||||
import type { ModelInfo, ModelTaskConfig } from '../types'
|
import type { ModelInfo, ModelTaskConfig } from '../types'
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ interface UseModelAutoSaveReturn {
|
|||||||
/** 清除所有待执行的保存定时器 */
|
/** 清除所有待执行的保存定时器 */
|
||||||
clearTimers: () => void
|
clearTimers: () => void
|
||||||
/** 初始加载状态标记引用 (用于设置初始加载完成) */
|
/** 初始加载状态标记引用 (用于设置初始加载完成) */
|
||||||
initialLoadRef: React.MutableRefObject<boolean>
|
initialLoadRef: RefObject<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +85,10 @@ export function useModelAutoSave(
|
|||||||
onSavingChange?.(true)
|
onSavingChange?.(true)
|
||||||
// 清理每个模型中的 null 值
|
// 清理每个模型中的 null 值
|
||||||
const cleanedModels = newModels.map(cleanModelForSave)
|
const cleanedModels = newModels.map(cleanModelForSave)
|
||||||
await updateModelConfigSection('models', cleanedModels)
|
const result = await updateModelConfigSection('models', cleanedModels)
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
onUnsavedChange?.(false)
|
onUnsavedChange?.(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('自动保存模型列表失败:', error)
|
console.error('自动保存模型列表失败:', error)
|
||||||
@@ -98,7 +102,10 @@ export function useModelAutoSave(
|
|||||||
const autoSaveTaskConfig = useCallback(async (newTaskConfig: ModelTaskConfig) => {
|
const autoSaveTaskConfig = useCallback(async (newTaskConfig: ModelTaskConfig) => {
|
||||||
try {
|
try {
|
||||||
onSavingChange?.(true)
|
onSavingChange?.(true)
|
||||||
await updateModelConfigSection('model_task_config', newTaskConfig)
|
const result = await updateModelConfigSection('model_task_config', newTaskConfig)
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
onUnsavedChange?.(false)
|
onUnsavedChange?.(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('自动保存任务配置失败:', error)
|
console.error('自动保存任务配置失败:', error)
|
||||||
|
|||||||
@@ -88,11 +88,15 @@ export function useModelFetcher(options: UseModelFetcherOptions): UseModelFetche
|
|||||||
setModelFetchError(null)
|
setModelFetchError(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const models = await fetchProviderModels(
|
const result = await fetchProviderModels(
|
||||||
providerName,
|
providerName,
|
||||||
template.modelFetcher.parser,
|
template.modelFetcher.parser,
|
||||||
template.modelFetcher.endpoint
|
template.modelFetcher.endpoint
|
||||||
)
|
)
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
const models = result.data
|
||||||
setAvailableModels(models)
|
setAvailableModels(models)
|
||||||
// 更新缓存
|
// 更新缓存
|
||||||
modelListCache.set(cacheKey, { models, timestamp: Date.now() })
|
modelListCache.set(cacheKey, { models, timestamp: Date.now() })
|
||||||
|
|||||||
@@ -206,7 +206,17 @@ function ModelProviderConfigPageContent() {
|
|||||||
const loadConfig = async () => {
|
const loadConfig = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const config = await getModelConfig()
|
const result = await getModelConfig()
|
||||||
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = result.data
|
||||||
setProviders((config.api_providers as APIProvider[]) || [])
|
setProviders((config.api_providers as APIProvider[]) || [])
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
initialLoadRef.current = false
|
initialLoadRef.current = false
|
||||||
@@ -246,7 +256,17 @@ function ModelProviderConfigPageContent() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = await getModelConfig()
|
const resultGet = await getModelConfig()
|
||||||
|
if (!resultGet.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultGet.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = resultGet.data
|
||||||
|
|
||||||
// 获取所有有效的 provider 名称
|
// 获取所有有效的 provider 名称
|
||||||
const validProviderNames = new Set(cleanedProviders.map(p => p.name))
|
const validProviderNames = new Set(cleanedProviders.map(p => p.name))
|
||||||
@@ -260,7 +280,16 @@ function ModelProviderConfigPageContent() {
|
|||||||
config.api_providers = cleanedProviders
|
config.api_providers = cleanedProviders
|
||||||
config.models = filteredModels
|
config.models = filteredModels
|
||||||
|
|
||||||
await updateModelConfig(config)
|
const resultUpdate = await updateModelConfig(config)
|
||||||
|
if (!resultUpdate.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultUpdate.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
@@ -284,7 +313,12 @@ function ModelProviderConfigPageContent() {
|
|||||||
context: 'auto' | 'manual' | 'restart' = 'auto'
|
context: 'auto' | 'manual' | 'restart' = 'auto'
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const config = await getModelConfig()
|
const result = await getModelConfig()
|
||||||
|
if (!result.success) {
|
||||||
|
console.error('加载配置失败:', result.error)
|
||||||
|
return { shouldProceed: true, providers: newProviders }
|
||||||
|
}
|
||||||
|
const config = result.data
|
||||||
const oldProviderNames = new Set(providers.map(p => p.name))
|
const oldProviderNames = new Set(providers.map(p => p.name))
|
||||||
const newProviderNames = new Set(newProviders.map(p => p.name))
|
const newProviderNames = new Set(newProviders.map(p => p.name))
|
||||||
|
|
||||||
@@ -334,7 +368,17 @@ function ModelProviderConfigPageContent() {
|
|||||||
|
|
||||||
setDeleteConfirmState(prev => ({ ...prev, isOpen: false }))
|
setDeleteConfirmState(prev => ({ ...prev, isOpen: false }))
|
||||||
|
|
||||||
const config = await getModelConfig()
|
const resultGet = await getModelConfig()
|
||||||
|
if (!resultGet.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: resultGet.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
savingFlag(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = resultGet.data
|
||||||
|
|
||||||
// 清理 providers 数据
|
// 清理 providers 数据
|
||||||
const cleanedProviders = deleteConfirmState.pendingProviders.map(cleanProviderData)
|
const cleanedProviders = deleteConfirmState.pendingProviders.map(cleanProviderData)
|
||||||
@@ -348,7 +392,7 @@ function ModelProviderConfigPageContent() {
|
|||||||
return validProviderNames.has(model.api_provider)
|
return validProviderNames.has(model.api_provider)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取被删除的模型名称
|
// 获取被削除的模型名称
|
||||||
const deletedModelNames = new Set(
|
const deletedModelNames = new Set(
|
||||||
deleteConfirmState.affectedModels.map((m: any) => m.name)
|
deleteConfirmState.affectedModels.map((m: any) => m.name)
|
||||||
)
|
)
|
||||||
@@ -371,7 +415,16 @@ function ModelProviderConfigPageContent() {
|
|||||||
config.models = filteredModels
|
config.models = filteredModels
|
||||||
config.model_task_config = modelTaskConfig
|
config.model_task_config = modelTaskConfig
|
||||||
|
|
||||||
await updateModelConfig(config)
|
const resultUpdate = await updateModelConfig(config)
|
||||||
|
if (!resultUpdate.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultUpdate.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
savingFlag(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
setProviders(deleteConfirmState.pendingProviders)
|
setProviders(deleteConfirmState.pendingProviders)
|
||||||
@@ -449,7 +502,17 @@ function ModelProviderConfigPageContent() {
|
|||||||
setAutoSaving(true)
|
setAutoSaving(true)
|
||||||
// 清理 providers 数据:将 null 值转换为默认值
|
// 清理 providers 数据:将 null 值转换为默认值
|
||||||
const cleanedProviders = newProviders.map(cleanProviderData)
|
const cleanedProviders = newProviders.map(cleanProviderData)
|
||||||
await updateModelConfigSection('api_providers', cleanedProviders)
|
const result = await updateModelConfigSection('api_providers', cleanedProviders)
|
||||||
|
if (!result.success) {
|
||||||
|
console.error('自动保存失败:', result.error)
|
||||||
|
toast({
|
||||||
|
title: '自动保存失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setHasUnsavedChanges(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('自动保存失败:', error)
|
console.error('自动保存失败:', error)
|
||||||
@@ -509,7 +572,17 @@ function ModelProviderConfigPageContent() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = await getModelConfig()
|
const resultGet = await getModelConfig()
|
||||||
|
if (!resultGet.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultGet.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const config = resultGet.data
|
||||||
|
|
||||||
// 获取所有有效的 provider 名称
|
// 获取所有有效的 provider 名称
|
||||||
const validProviderNames = new Set(cleanedProviders.map(p => p.name))
|
const validProviderNames = new Set(cleanedProviders.map(p => p.name))
|
||||||
@@ -519,12 +592,12 @@ function ModelProviderConfigPageContent() {
|
|||||||
const filteredModels = originalModels.filter((model: any) => {
|
const filteredModels = originalModels.filter((model: any) => {
|
||||||
const isValid = validProviderNames.has(model.api_provider)
|
const isValid = validProviderNames.has(model.api_provider)
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
console.warn(`模型 "${model.name}" 引用了已删除的提供商 "${model.api_provider}",将被移除`)
|
console.warn(`模型 "${model.name}" 引用了已删除的提供商 "${model.api_provider}"、将被移除`)
|
||||||
}
|
}
|
||||||
return isValid
|
return isValid
|
||||||
})
|
})
|
||||||
|
|
||||||
// 如果有模型被移除,显示警告
|
// 如果有模型被移除、显示警告
|
||||||
if (originalModels.length !== filteredModels.length) {
|
if (originalModels.length !== filteredModels.length) {
|
||||||
const removedCount = originalModels.length - filteredModels.length
|
const removedCount = originalModels.length - filteredModels.length
|
||||||
toast({
|
toast({
|
||||||
@@ -539,7 +612,16 @@ function ModelProviderConfigPageContent() {
|
|||||||
config.models = filteredModels
|
config.models = filteredModels
|
||||||
console.log('完整配置数据:', config)
|
console.log('完整配置数据:', config)
|
||||||
|
|
||||||
await updateModelConfig(config)
|
const resultUpdate = await updateModelConfig(config)
|
||||||
|
if (!resultUpdate.success) {
|
||||||
|
toast({
|
||||||
|
title: '保存失败',
|
||||||
|
description: resultUpdate.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
setSaving(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
setHasUnsavedChanges(false)
|
setHasUnsavedChanges(false)
|
||||||
toast({
|
toast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
@@ -804,31 +886,40 @@ function ModelProviderConfigPageContent() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await testProviderConnection(providerName)
|
const result = await testProviderConnection(providerName)
|
||||||
setTestResults(prev => new Map(prev).set(providerName, result))
|
if (!result.success) {
|
||||||
|
toast({
|
||||||
|
title: '测试失败',
|
||||||
|
description: result.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const testResult = result.data
|
||||||
|
setTestResults(prev => new Map(prev).set(providerName, testResult))
|
||||||
|
|
||||||
// 显示结果 toast
|
// 显示结果 toast
|
||||||
if (result.network_ok) {
|
if (testResult.network_ok) {
|
||||||
if (result.api_key_valid === true) {
|
if (testResult.api_key_valid === true) {
|
||||||
toast({
|
toast({
|
||||||
title: '连接正常',
|
title: '连接正常',
|
||||||
description: `${providerName} 网络连接正常,API Key 有效 (${result.latency_ms}ms)`,
|
description: `${providerName} 网络连接正常、API Key 有效 (${testResult.latency_ms}ms)`,
|
||||||
})
|
})
|
||||||
} else if (result.api_key_valid === false) {
|
} else if (testResult.api_key_valid === false) {
|
||||||
toast({
|
toast({
|
||||||
title: '连接正常但 Key 无效',
|
title: '连接正常但 Key 无效',
|
||||||
description: `${providerName} 网络连接正常,但 API Key 无效或已过期`,
|
description: `${providerName} 网络连接正常、但 API Key 无效或已过期`,
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: '网络连接正常',
|
title: '网络连接正常',
|
||||||
description: `${providerName} 可以访问 (${result.latency_ms}ms)`,
|
description: `${providerName} 可以访问 (${testResult.latency_ms}ms)`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: '连接失败',
|
title: '连接失败',
|
||||||
description: result.error || '无法连接到提供商',
|
description: testResult.error || '无法连接到提供商',
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user