Files
easynode/server/app/controller/chat.js
T

104 lines
3.4 KiB
JavaScript

/**
* AI 接口配置
*
* 旧的 AI Chat 已下线,会话历史由 agent 的 AgentSessionDB 承担
* (见 controller/agent-session.js),这里只保留接口配置相关能力。
*/
import { AIConfigDB } from '../utils/db-class.js'
import { mergeAIProviderConfig, mergeAIPreferences, normalizeAIConfig } from '../ai/config.js'
import { deriveBaseURL } from '../ai/provider.js'
const aiConfigDB = new AIConfigDB().getInstance()
const MODEL_DISCOVERY_TIMEOUT_MS = 15 * 1000
async function getAIConfig({ res }) {
try {
const config = await aiConfigDB.findOneAsync({})
res.success({ data: normalizeAIConfig(config) })
} catch {
res.fail({ msg: '获取配置失败' })
}
}
async function getAIModels({ res, request }) {
const { apiUrl, apiKey } = request.body
if (!apiUrl || !apiKey) return res.fail({ msg: 'param error' })
const baseURL = deriveBaseURL(apiUrl)
if (!baseURL) return res.fail({ msg: 'invalid Base URL' })
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), MODEL_DISCOVERY_TIMEOUT_MS)
try {
const response = await fetch(`${ baseURL }/models`, {
method: 'GET',
headers: { authorization: `Bearer ${ apiKey }` },
signal: controller.signal
})
const body = await response.json().catch(() => null)
if (!response.ok) {
const message = body?.error?.message || body?.message || `HTTP ${ response.status }`
return res.fail({ msg: 'get AI models failed', data: { message } })
}
if (!Array.isArray(body?.data)) {
return res.fail({ msg: 'get AI models failed', data: { message: 'invalid models response' } })
}
res.success({ data: body.data })
} catch (error) {
const message = error?.name === 'AbortError' ? 'request timeout' : error.message
res.fail({ msg: 'get AI models failed', data: { message } })
} finally {
clearTimeout(timeout)
}
}
async function saveAIConfig({ res, request }) {
const { body } = request
if (!body.apiUrl || !body.apiKey || !Array.isArray(body.models) || !body.models.length) {
return res.fail({ msg: 'param error' })
}
try {
const existConfig = await aiConfigDB.findOneAsync({})
const nextConfig = mergeAIProviderConfig(existConfig, body)
if (existConfig) {
await aiConfigDB.updateAsync({ _id: existConfig._id }, nextConfig)
} else {
await aiConfigDB.insertAsync(nextConfig)
}
res.success({ msg: 'save success', data: { success: true } })
} catch {
res.fail({ msg: 'save AI config failed', data: { success: false } })
}
}
async function updateAIPreferences({ res, request }) {
const body = request.body || {}
const preferences = {}
if (typeof body.petEnabled === 'boolean') preferences.petEnabled = body.petEnabled
if (typeof body.nativeAgentEnabled === 'boolean') {
preferences.nativeAgentEnabled = body.nativeAgentEnabled
}
if (!Object.keys(preferences).length) return res.fail({ msg: 'param error' })
try {
const existConfig = await aiConfigDB.findOneAsync({})
const ui = mergeAIPreferences(existConfig, preferences)
if (existConfig) {
await aiConfigDB.updateAsync({ _id: existConfig._id }, { $set: { ui } })
} else {
await aiConfigDB.insertAsync({ ui })
}
res.success({ msg: 'save success', data: preferences })
} catch {
res.fail({ msg: 'save AI preferences failed', data: { success: false } })
}
}
export {
getAIConfig,
saveAIConfig,
getAIModels,
updateAIPreferences
}