feat: 备用大模型接口管理功能

- 新增 BackupLLMConfig 数据模型存储备用大模型配置
- 支持手动新增、编辑、删除备用大模型接口
- 支持测试连接功能
- 大模型配置页面静态表格改为动态管理的备用接口链接
- 默认初始化5个常用大模型服务商配置
This commit is contained in:
2026-04-16 14:36:13 +08:00
parent 9a36b9245a
commit dcddcbf626
5 changed files with 613 additions and 15 deletions

206
admin.py
View File

@@ -10,7 +10,7 @@ import json
from models import (db, User, Translation, TranslationCache, GuestTranslation, from models import (db, User, Translation, TranslationCache, GuestTranslation,
SystemConfig, OperationLog, DataPackage, UserPackage, DynamicConfig, SystemConfig, OperationLog, DataPackage, UserPackage, DynamicConfig,
UserTypeConfig, MembershipPlanConfig) UserTypeConfig, MembershipPlanConfig, BackupLLMConfig)
from config import USER_LIMITS, MEMBERSHIP_PLANS from config import USER_LIMITS, MEMBERSHIP_PLANS
admin_bp = Blueprint('admin', __name__, url_prefix='/admin') admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
@@ -1217,4 +1217,206 @@ def get_membership_plan(plan_key):
def get_all_membership_plans(): def get_all_membership_plans():
"""获取所有会员套餐配置""" """获取所有会员套餐配置"""
plans = MembershipPlanConfig.query.filter_by(is_active=True).order_by(MembershipPlanConfig.sort_order).all() plans = MembershipPlanConfig.query.filter_by(is_active=True).order_by(MembershipPlanConfig.sort_order).all()
return [p.to_dict() for p in plans] return [p.to_dict() for p in plans]
# ==================== 备用大模型接口管理 ====================
@admin_bp.route('/backup-llm')
@admin_required
def backup_llm_list():
"""备用大模型接口列表"""
configs = BackupLLMConfig.query.order_by(BackupLLMConfig.sort_order).all()
# 如果数据库中没有数据,初始化默认配置
if not configs:
init_default_backup_llm()
configs = BackupLLMConfig.query.order_by(BackupLLMConfig.sort_order).all()
return render_template('admin/backup_llm.html', configs=configs)
@admin_bp.route('/backup-llm/add', methods=['GET', 'POST'])
@admin_required
def add_backup_llm():
"""添加备用大模型接口"""
if request.method == 'POST':
data = request.json if request.is_json else request.form
config = BackupLLMConfig(
provider_name=data.get('provider_name'),
api_base=data.get('api_base'),
api_key=data.get('api_key'),
model=data.get('model'),
is_active=data.get('is_active', True) if isinstance(data.get('is_active'), bool) else data.get('is_active') == 'true',
sort_order=int(data.get('sort_order', 0)),
description=data.get('description'),
)
db.session.add(config)
db.session.commit()
# 记录日志
log = OperationLog(
user_id=session.get('user_id'),
username='admin',
action='add_backup_llm',
target=config.provider_name,
detail=json.dumps(config.to_dict(), ensure_ascii=False)
)
db.session.add(log)
db.session.commit()
if request.is_json:
return jsonify({'success': True, 'config': config.to_dict()})
flash('备用大模型接口已添加', 'success')
return redirect(url_for('admin.backup_llm_list'))
return render_template('admin/backup_llm_form.html', config=None)
@admin_bp.route('/backup-llm/<int:config_id>/edit', methods=['GET', 'POST'])
@admin_required
def edit_backup_llm(config_id):
"""编辑备用大模型接口"""
config = BackupLLMConfig.query.get_or_404(config_id)
if request.method == 'POST':
data = request.json if request.is_json else request.form
config.provider_name = data.get('provider_name', config.provider_name)
config.api_base = data.get('api_base', config.api_base)
config.api_key = data.get('api_key', config.api_key)
config.model = data.get('model', config.model)
config.is_active = data.get('is_active', True) if isinstance(data.get('is_active'), bool) else data.get('is_active') == 'true'
config.sort_order = int(data.get('sort_order', config.sort_order))
config.description = data.get('description', config.description)
db.session.commit()
# 记录日志
log = OperationLog(
user_id=session.get('user_id'),
username='admin',
action='edit_backup_llm',
target=config.provider_name,
detail=json.dumps(config.to_dict(), ensure_ascii=False)
)
db.session.add(log)
db.session.commit()
if request.is_json:
return jsonify({'success': True, 'config': config.to_dict()})
flash('备用大模型接口已更新', 'success')
return redirect(url_for('admin.backup_llm_list'))
return render_template('admin/backup_llm_form.html', config=config)
@admin_bp.route('/backup-llm/<int:config_id>/delete', methods=['POST'])
@admin_required
def delete_backup_llm(config_id):
"""删除备用大模型接口"""
config = BackupLLMConfig.query.get_or_404(config_id)
provider_name = config.provider_name
db.session.delete(config)
db.session.commit()
# 记录日志
log = OperationLog(
user_id=session.get('user_id'),
username='admin',
action='delete_backup_llm',
target=provider_name
)
db.session.add(log)
db.session.commit()
return jsonify({'success': True})
@admin_bp.route('/backup-llm/<int:config_id>/toggle', methods=['POST'])
@admin_required
def toggle_backup_llm(config_id):
"""切换备用大模型接口状态"""
config = BackupLLMConfig.query.get_or_404(config_id)
config.is_active = not config.is_active
db.session.commit()
return jsonify({'success': True, 'is_active': config.is_active})
@admin_bp.route('/backup-llm/<int:config_id>/test', methods=['POST'])
@admin_required
def test_backup_llm(config_id):
"""测试备用大模型接口"""
config = BackupLLMConfig.query.get_or_404(config_id)
try:
from openai import OpenAI
client = OpenAI(
api_key=config.api_key or 'sk-test',
base_url=config.api_base,
)
model = config.model or 'default'
# 发送简单测试请求
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Hello"}],
max_tokens=10,
timeout=10,
)
return jsonify({
'success': True,
'provider': config.provider_name,
'model': model,
'response': response.choices[0].message.content[:50] if response.choices else 'OK'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@admin_bp.route('/backup-llm/init', methods=['POST'])
@admin_required
def init_backup_llm():
"""初始化默认备用大模型"""
init_default_backup_llm()
return jsonify({'success': True})
def init_default_backup_llm():
"""初始化默认备用大模型接口配置"""
defaults = [
('本地LM Studio', 'http://localhost:1234/v1', None, None, 0),
('OpenAI', 'https://api.openai.com/v1', None, 'gpt-4', 1),
('DeepSeek', 'https://api.deepseek.com/v1', None, 'deepseek-chat', 2),
('阿里百炼', 'https://dashscope.aliyuncs.com/compatible-mode/v1', None, 'qwen-turbo', 3),
('SiliconFlow', 'https://api.siliconflow.cn/v1', None, 'Qwen/Qwen2.5-72B-Instruct', 4),
]
for provider_name, api_base, api_key, model, sort_order in defaults:
existing = BackupLLMConfig.query.filter_by(provider_name=provider_name).first()
if not existing:
config = BackupLLMConfig(
provider_name=provider_name,
api_base=api_base,
api_key=api_key,
model=model,
is_active=True,
sort_order=sort_order,
description=f'{provider_name} 默认接口',
)
db.session.add(config)
db.session.commit()
def get_backup_llm_configs():
"""获取所有备用大模型配置(供其他模块使用)"""
configs = BackupLLMConfig.query.filter_by(is_active=True).order_by(BackupLLMConfig.sort_order).all()
return [c.to_dict() for c in configs]

View File

@@ -869,6 +869,44 @@ class EmailNotification(db.Model):
} }
# ==================== 备用大模型接口配置 ====================
class BackupLLMConfig(db.Model):
"""备用大模型接口配置"""
__tablename__ = 'backup_llm_config'
id = db.Column(db.Integer, primary_key=True)
# 服务商信息
provider_name = db.Column(db.String(100), nullable=False) # 服务商名称: OpenAI, DeepSeek, 阿里百炼, etc.
api_base = db.Column(db.String(255), nullable=False) # API地址
api_key = db.Column(db.String(255), nullable=True) # API Key可选
model = db.Column(db.String(100), nullable=True) # 默认模型
# 状态
is_active = db.Column(db.Boolean, default=True) # 是否启用
sort_order = db.Column(db.Integer, default=0) # 排序
# 备注
description = db.Column(db.String(255), nullable=True) # 备注
# 时间
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'provider_name': self.provider_name,
'api_base': self.api_base,
'api_key': self.api_key,
'model': self.model,
'is_active': self.is_active,
'sort_order': self.sort_order,
'description': self.description,
'created_at': self.created_at.isoformat() if self.created_at else None,
}
# ==================== 邮件模板配置 ==================== # ==================== 邮件模板配置 ====================
class EmailTemplateConfig(db.Model): class EmailTemplateConfig(db.Model):
"""邮件模板配置""" """邮件模板配置"""

View File

@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>备用大模型接口 - 后台管理</title>
<link rel="icon" href="/static/img/favicon.svg" type="image/svg+xml">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body { background-color: #f5f5f5; }
.sidebar { position: fixed; top: 0; left: 0; height: 100vh; width: 250px; background: #343a40; padding-top: 60px; }
.sidebar .nav-link { color: #adb5bd; padding: 12px 20px; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: #fff; background: rgba(255,255,255,0.1); }
.main-content { margin-left: 250px; padding: 20px; }
.status-badge { font-size: 0.75rem; }
</style>
</head>
<body>
<nav class="sidebar">
<div class="position-absolute top-0 w-100 p-3 border-bottom border-secondary">
<h5 class="text-white mb-0"><i class="bi bi-gear-fill"></i> 后台管理</h5>
</div>
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.dashboard') }}"><i class="bi bi-speedometer2"></i> 数据概览</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.users') }}"><i class="bi bi-people"></i> 用户管理</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.translations') }}"><i class="bi bi-file-text"></i> 翻译记录</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.backup_llm_list') }}"><i class="bi bi-cloud"></i> 备用大模型</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
</ul>
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
<a href="/" class="btn btn-outline-light btn-sm w-100"><i class="bi bi-house"></i> 返回前台</a>
</div>
</nav>
<main class="main-content">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5><i class="bi bi-cloud"></i> 备用大模型接口</h5>
<div>
<a href="{{ url_for('admin.add_backup_llm') }}" class="btn btn-primary"><i class="bi bi-plus-lg"></i> 新增接口</a>
<button class="btn btn-outline-secondary" onclick="initDefaults()"><i class="bi bi-arrow-counterclockwise"></i> 初始化默认</button>
</div>
</div>
<div class="card">
<div class="card-body">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th style="width: 50px;">#</th>
<th>服务商</th>
<th>API地址</th>
<th>模型</th>
<th style="width: 80px;">状态</th>
<th style="width: 150px;">操作</th>
</tr>
</thead>
<tbody>
{% for config in configs %}
<tr id="row-{{ config.id }}">
<td>{{ config.sort_order }}</td>
<td>
<strong>{{ config.provider_name }}</strong>
{% if config.description %}
<br><small class="text-muted">{{ config.description }}</small>
{% endif %}
</td>
<td><code>{{ config.api_base }}</code></td>
<td><code>{{ config.model or '默认' }}</code></td>
<td>
{% if config.is_active %}
<span class="badge bg-success status-badge">启用</span>
{% else %}
<span class="badge bg-secondary status-badge">禁用</span>
{% endif %}
</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="testConfig({{ config.id }})" title="测试连接">
<i class="bi bi-plug"></i>
</button>
<a href="{{ url_for('admin.edit_backup_llm', config_id=config.id) }}" class="btn btn-sm btn-outline-secondary" title="编辑">
<i class="bi bi-pencil"></i>
</a>
<button class="btn btn-sm btn-outline-warning" onclick="toggleConfig({{ config.id }})" title="切换状态">
<i class="bi bi-toggle2"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteConfig({{ config.id }})" title="删除">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not configs %}
<div class="text-center text-muted py-4">
<i class="bi bi-cloud-slash" style="font-size: 2rem;"></i>
<p>暂无备用大模型配置</p>
<button class="btn btn-outline-primary" onclick="initDefaults()">点击初始化默认配置</button>
</div>
{% endif %}
</div>
</div>
<div id="testResult" class="mt-3" style="display:none;"></div>
</main>
<script>
function testConfig(id) {
const resultDiv = document.getElementById('testResult');
resultDiv.style.display = 'block';
resultDiv.innerHTML = '<div class="alert alert-info"><i class="bi bi-hourglass-split"></i> 正在测试连接...</div>';
fetch(`/admin/backup-llm/${id}/test`, {method: 'POST'})
.then(r => r.json())
.then(res => {
if (res.success) {
resultDiv.innerHTML = `<div class="alert alert-success"><i class="bi bi-check-circle"></i> ${res.provider} 连接成功!模型: ${res.model}</div>`;
} else {
resultDiv.innerHTML = `<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 连接失败: ${res.error}</div>`;
}
})
.catch(err => {
resultDiv.innerHTML = `<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 请求失败: ${err}</div>`;
});
}
function toggleConfig(id) {
fetch(`/admin/backup-llm/${id}/toggle`, {method: 'POST'})
.then(r => r.json())
.then(res => {
if (res.success) {
location.reload();
} else {
alert('操作失败');
}
});
}
function deleteConfig(id) {
if (confirm('确定删除此备用大模型接口吗?')) {
fetch(`/admin/backup-llm/${id}/delete`, {method: 'POST'})
.then(r => r.json())
.then(res => {
if (res.success) {
document.getElementById(`row-${id}`).remove();
} else {
alert('删除失败');
}
});
}
}
function initDefaults() {
if (confirm('初始化默认备用大模型配置?')) {
fetch('/admin/backup-llm/init', {method: 'POST'})
.then(r => r.json())
.then(res => {
if (res.success) {
location.reload();
} else {
alert('初始化失败');
}
});
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{ config ? '编辑' : '添加' }}备用大模型接口 - 后台管理</title>
<link rel="icon" href="/static/img/favicon.svg" type="image/svg+xml">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body { background-color: #f5f5f5; }
.sidebar { position: fixed; top: 0; left: 0; height: 100vh; width: 250px; background: #343a40; padding-top: 60px; }
.sidebar .nav-link { color: #adb5bd; padding: 12px 20px; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: #fff; background: rgba(255,255,255,0.1); }
.main-content { margin-left: 250px; padding: 20px; }
</style>
</head>
<body>
<nav class="sidebar">
<div class="position-absolute top-0 w-100 p-3 border-bottom border-secondary">
<h5 class="text-white mb-0"><i class="bi bi-gear-fill"></i> 后台管理</h5>
</div>
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.dashboard') }}"><i class="bi bi-speedometer2"></i> 数据概览</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.users') }}"><i class="bi bi-people"></i> 用户管理</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.translations') }}"><i class="bi bi-file-text"></i> 翻译记录</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.backup_llm_list') }}"><i class="bi bi-cloud"></i> 备用大模型</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
</ul>
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
<a href="/" class="btn btn-outline-light btn-sm w-100"><i class="bi bi-house"></i> 返回前台</a>
</div>
</nav>
<main class="main-content">
<div class="card">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-cloud-plus"></i> {{ config ? '编辑备用大模型接口' : '添加备用大模型接口' }}</h6>
</div>
<div class="card-body">
<form id="configForm" onsubmit="return saveConfig(event)">
<div class="mb-3">
<label class="form-label">服务商名称 <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="provider_name"
value="{{ config.provider_name if config else '' }}"
placeholder="例如: OpenAI, DeepSeek, 阿里百炼" required>
<small class="text-muted">显示名称,方便识别</small>
</div>
<div class="mb-3">
<label class="form-label">API地址 <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="api_base"
value="{{ config.api_base if config else '' }}"
placeholder="https://api.openai.com/v1" required>
<small class="text-muted">LLM服务的API endpoint</small>
</div>
<div class="mb-3">
<label class="form-label">API Key</label>
<input type="text" class="form-control" name="api_key"
value="{{ config.api_key if config else '' }}"
placeholder="sk-xxx可选">
<small class="text-muted">如果不需要可留空</small>
</div>
<div class="mb-3">
<label class="form-label">默认模型</label>
<input type="text" class="form-control" name="model"
value="{{ config.model if config else '' }}"
placeholder="gpt-4, deepseek-chat">
<small class="text-muted">推荐使用的模型ID</small>
</div>
<div class="mb-3">
<label class="form-label">备注</label>
<input type="text" class="form-control" name="description"
value="{{ config.description if config else '' }}"
placeholder="接口说明">
</div>
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">排序</label>
<input type="number" class="form-control" name="sort_order"
value="{{ config.sort_order if config else 0 }}">
</div>
<div class="col-md-6">
<label class="form-label">状态</label>
<select class="form-select" name="is_active">
<option value="true" {% if not config or config.is_active %}selected{% endif %}>启用</option>
<option value="false" {% if config and not config.is_active %}selected{% endif %}>禁用</option>
</select>
</div>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-primary"><i class="bi bi-check-lg"></i> 保存</button>
<button type="button" class="btn btn-outline-secondary" onclick="testConfig()"><i class="bi bi-plug"></i> 测试连接</button>
<a href="{{ url_for('admin.backup_llm_list') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> 返回</a>
</div>
</form>
<div id="testResult" class="mt-3" style="display:none;"></div>
</div>
</div>
</main>
<script>
function saveConfig(e) {
e.preventDefault();
const formData = new FormData(document.getElementById('configForm'));
const data = {};
formData.forEach((value, key) => {
if (key === 'sort_order') {
data[key] = parseInt(value) || 0;
} else if (key === 'is_active') {
data[key] = value === 'true';
} else {
data[key] = value;
}
});
{% if config %}
const url = `/admin/backup-llm/{{ config.id }}/edit`;
{% else %}
const url = '/admin/backup-llm/add';
{% endif %}
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
})
.then(r => r.json())
.then(res => {
if (res.success) {
alert('保存成功!');
window.location.href = '/admin/backup-llm';
} else {
alert('保存失败: ' + (res.error || '未知错误'));
}
})
.catch(err => alert('请求失败: ' + err));
return false;
}
function testConfig() {
const formData = new FormData(document.getElementById('configForm'));
const resultDiv = document.getElementById('testResult');
resultDiv.style.display = 'block';
resultDiv.innerHTML = '<div class="alert alert-info"><i class="bi bi-hourglass-split"></i> 正在测试连接...</div>';
// 先临时保存,然后测试
{% if config %}
// 已有配置,直接测试
fetch(`/admin/backup-llm/{{ config.id }}/test`, {method: 'POST'})
{% else %}
// 新配置,需要先创建
const data = {};
formData.forEach((value, key) => {
if (key === 'sort_order') data[key] = parseInt(value) || 0;
else if (key === 'is_active') data[key] = value === 'true';
else data[key] = value;
});
// 直接用输入的数据测试(不保存)
resultDiv.innerHTML = '<div class="alert alert-warning"><i class="bi bi-info-circle"></i> 新配置请先保存后再测试</div>';
return;
{% endif %}
.then(r => r.json())
.then(res => {
if (res.success) {
resultDiv.innerHTML = `<div class="alert alert-success"><i class="bi bi-check-circle"></i> ${res.provider} 连接成功!模型: ${res.model}</div>`;
} else {
resultDiv.innerHTML = `<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 连接失败: ${res.error}</div>`;
}
})
.catch(err => {
resultDiv.innerHTML = `<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 请求失败: ${err}</div>`;
});
}
</script>
</body>
</html>

View File

@@ -90,20 +90,17 @@
</div> </div>
<div class="card mt-3"> <div class="card mt-3">
<div class="card-header"><h6 class="mb-0"><i class="bi bi-info-circle"></i> 常用模型配置参考</h6></div> <div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0"><i class="bi bi-cloud"></i> 备用大模型接口</h6>
<a href="{{ url_for('admin.backup_llm_list') }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-gear"></i> 管理备用接口
</a>
</div>
<div class="card-body"> <div class="card-body">
<table class="table table-sm"> <p class="text-muted">备用大模型接口用于主接口不可用时切换备用服务。支持手动新增、编辑、测试连接。</p>
<thead class="table-light"> <a href="{{ url_for('admin.backup_llm_list') }}" class="btn btn-outline-secondary">
<tr><th>服务商</th><th>API地址</th><th>模型示例</th></tr> <i class="bi bi-list"></i> 查看所有备用接口
</thead> </a>
<tbody>
<tr><td>本地LM Studio</td><td>http://localhost:1234/v1</td><td>根据加载的模型</td></tr>
<tr><td>OpenAI</td><td>https://api.openai.com/v1</td><td>gpt-4, gpt-3.5-turbo</td></tr>
<tr><td>DeepSeek</td><td>https://api.deepseek.com/v1</td><td>deepseek-chat</td></tr>
<tr><td>阿里百炼</td><td>https://dashscope.aliyuncs.com/compatible-mode/v1</td><td>qwen-turbo, qwen-plus</td></tr>
<tr><td>SiliconFlow</td><td>https://api.siliconflow.cn/v1</td><td>Qwen/Qwen2.5-72B-Instruct</td></tr>
</tbody>
</table>
</div> </div>
</div> </div>
</main> </main>