Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

107 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const axios = require('axios');
const router = express.Router();
const { MODELS, DEFAULT_MODEL } = require('../config/models');
router.get('/test', (req, res) => {
res.json({
status: 'ok',
message: '模型路由正常 ❤️ (支持主备自动切换)',
available_models: Object.keys(MODELS),
default_model: DEFAULT_MODEL
});
});
// 带 fallback 的 API 调用函数
async function callAPIWithFallback(model, message) {
// 第一次尝试:主密钥
try {
console.log(`[API] 尝试使用主密钥调用 ${model.name}`);
const response = await axios.post(
model.apiUrl,
{
model: model.model,
messages: [{ role: 'user', content: message }],
max_tokens: model.maxTokens
},
{
headers: {
'Authorization': 'Bearer ' + model.primaryApiKey,
'Content-Type': 'application/json'
}
}
);
return { success: true, data: response.data };
} catch (primaryErr) {
// 判断是否需要 fallback余额不足/超时/401/403
const shouldFallback =
primaryErr.response?.status === 401 ||
primaryErr.response?.status === 403 ||
primaryErr.response?.data?.error?.type === 'insufficient_quota' ||
primaryErr.code === 'ECONNABORTED' ||
primaryErr.message.includes('timeout');
if (!shouldFallback || !model.fallbackApiKey) {
// 不需要 fallback 或没有备用密钥,直接抛出错误
throw primaryErr;
}
// 需要 fallback尝试备用密钥
console.log(`[API Fallback] 从主密钥切换到备用密钥 (${model.name})`);
try {
const fallbackResponse = await axios.post(
model.apiUrl,
{
model: model.model,
messages: [{ role: 'user', content: message }],
max_tokens: model.maxTokens
},
{
headers: {
'Authorization': 'Bearer ' + model.fallbackApiKey,
'Content-Type': 'application/json'
}
}
);
return { success: true, data: fallbackResponse.data, fallback: true };
} catch (fallbackErr) {
// 备用也失败了,抛出备用错误
throw fallbackErr;
}
}
}
router.post('/chat', async (req, res) => {
try {
const { message, model_name } = req.body;
const model = MODELS[model_name || DEFAULT_MODEL];
if (!model) {
return res.status(400).json({ error: '未知模型: ' + model_name });
}
if (!model.primaryApiKey) {
return res.status(503).json({
error: '模型 ' + model.name + ' 的主 API Key 尚未配置',
hint: '请检查 .env 文件中的 PRIMARY_API_KEY'
});
}
const result = await callAPIWithFallback(model, message);
res.json({
model: model.name,
reply: result.data.choices[0].message.content,
fallback_used: result.fallback || false
});
} catch (err) {
console.error('[API Error]', err.message);
res.status(500).json({
error: err.message,
detail: err.response ? err.response.data : null
});
}
});
module.exports = router;