245 lines
9.0 KiB
HTML
245 lines
9.0 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}API设置 - 有音个性化教学系统{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-key"></i> AI API 配置</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle"></i> 配置AI生成练习报告所需的API参数。
|
|
</div>
|
|
|
|
<form id="apiConfigForm">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">提供商</label>
|
|
<select class="form-select" id="apiProvider" onchange="onProviderChange()">
|
|
<option value="minimax">MiniMax (Token Plan)</option>
|
|
<option value="volcengine">火山引擎 (Volcengine)</option>
|
|
<option value="deepseek">DeepSeek</option>
|
|
<option value="openai">OpenAI</option>
|
|
<option value="openrouter">OpenRouter</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">模型</label>
|
|
<select class="form-select" id="apiModel">
|
|
<option value="MiniMax-M2.7-highspeed">MiniMax-M2.7-highspeed</option>
|
|
<option value="doubao-seed-2.0-pro">doubao-seed-2.0-pro</option>
|
|
<option value="doubao-seed-code">doubao-seed-code</option>
|
|
<option value="doubao-seed-2.0-lite">doubao-seed-2.0-lite</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">API Key</label>
|
|
<div class="input-group">
|
|
<input type="password" class="form-control" id="apiKey" placeholder="请输入API Key">
|
|
<button class="btn btn-outline-secondary" type="button" onclick="toggleApiKey()">
|
|
<i class="bi bi-eye" id="apiKeyToggleIcon"></i>
|
|
</button>
|
|
</div>
|
|
<small class="text-muted" id="apiKeyPreview"></small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">API Endpoint</label>
|
|
<input type="text" class="form-control" id="apiEndpoint" placeholder="https://ark.cn-beijing.volces.com/api/coding/v3">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Temperature</label>
|
|
<input type="number" class="form-control" id="apiTemperature" min="0" max="2" step="0.1" value="0.7">
|
|
<small class="text-muted">控制输出的随机性,值越大越有创造性</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">PDF水印文本</label>
|
|
<input type="text" class="form-control" id="watermarkText" placeholder="留空则不显示水印">
|
|
<small class="text-muted">PDF每页中央显示的斜向水印文字</small>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-primary" onclick="saveApiConfig()">
|
|
<i class="bi bi-save"></i> 保存配置
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="testApiConnection()">
|
|
<i class="bi bi-plug"></i> 测试连接
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-3" id="apiTestResult"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h6 class="mb-0"><i class="bi bi-lightbulb"></i> 推荐配置</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>提供商</th>
|
|
<th>推荐模型</th>
|
|
<th>Endpoint</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>MiniMax</td>
|
|
<td>MiniMax-M2.7-highspeed</td>
|
|
<td><code>https://api.minimaxi.com/anthropic/v1</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>火山方舟</td>
|
|
<td>doubao-seed-2.0-pro</td>
|
|
<td><code>https://ark.cn-beijing.volces.com/api/coding/v3</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>DeepSeek</td>
|
|
<td>deepseek-chat</td>
|
|
<td><code>https://api.deepseek.com</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>OpenRouter</td>
|
|
<td>deepseek/deepseek-r1</td>
|
|
<td><code>https://openrouter.ai/api/v1</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
window.pageInit = function(data) {
|
|
if (data.role !== 'admin') {
|
|
window.location.href = '/';
|
|
return;
|
|
}
|
|
loadApiConfig();
|
|
};
|
|
|
|
const providerDefaults = {
|
|
'minimax': {
|
|
endpoint: 'https://api.minimaxi.com/anthropic/v1',
|
|
model: 'MiniMax-M2.7-highspeed',
|
|
temperature: 0.7,
|
|
api_key: ''
|
|
},
|
|
'volcengine': {
|
|
endpoint: 'https://ark.cn-beijing.volces.com/api/coding/v3',
|
|
model: 'doubao-seed-2.0-pro',
|
|
temperature: 0.7,
|
|
api_key: ''
|
|
},
|
|
'deepseek': {
|
|
endpoint: 'https://api.deepseek.com',
|
|
model: 'deepseek-chat',
|
|
temperature: 0.7,
|
|
api_key: ''
|
|
},
|
|
'openai': {
|
|
endpoint: 'https://api.openai.com/v1',
|
|
model: 'gpt-4o-mini',
|
|
temperature: 0.7,
|
|
api_key: ''
|
|
},
|
|
'openrouter': {
|
|
endpoint: 'https://openrouter.ai/api/v1',
|
|
model: 'anthropic/claude-3-haiku',
|
|
temperature: 0.7,
|
|
api_key: ''
|
|
}
|
|
};
|
|
|
|
async function onProviderChange() {
|
|
const provider = document.getElementById('apiProvider').value;
|
|
const defaults = providerDefaults[provider] || providerDefaults['volcengine'];
|
|
|
|
document.getElementById('apiModel').value = defaults.model;
|
|
document.getElementById('apiEndpoint').value = defaults.endpoint;
|
|
document.getElementById('apiTemperature').value = defaults.temperature;
|
|
document.getElementById('apiKey').value = defaults.api_key || '';
|
|
}
|
|
|
|
async function saveApiConfig(silent = false, overrideProvider = null) {
|
|
const provider = overrideProvider || document.getElementById('apiProvider').value;
|
|
const config = {
|
|
provider: provider,
|
|
model: document.getElementById('apiModel').value,
|
|
api_key: document.getElementById('apiKey').value,
|
|
base_url: document.getElementById('apiEndpoint').value,
|
|
temperature: parseFloat(document.getElementById('apiTemperature').value),
|
|
watermark_text: document.getElementById('watermarkText').value.trim()
|
|
};
|
|
|
|
const r = await fetch('/api/config', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(config)
|
|
});
|
|
const data = await r.json();
|
|
if (!silent) {
|
|
if (data.error) {
|
|
alert(data.error);
|
|
} else {
|
|
alert('保存成功');
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
async function loadApiConfig() {
|
|
try {
|
|
const r = await fetch('/api/config');
|
|
const config = await r.json();
|
|
document.getElementById('apiProvider').value = config.provider || 'volcengine';
|
|
document.getElementById('apiModel').value = config.model || 'doubao-seed-2.0-pro';
|
|
document.getElementById('apiKey').value = config.api_key || '';
|
|
document.getElementById('apiEndpoint').value = config.base_url || '';
|
|
document.getElementById('apiTemperature').value = config.temperature || 0.7;
|
|
document.getElementById('watermarkText').value = config.watermark_text || '';
|
|
|
|
if (config.api_key_preview) {
|
|
document.getElementById('apiKeyPreview').textContent = '当前: ' + config.api_key_preview;
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
|
|
async function testApiConnection() {
|
|
const resultDiv = document.getElementById('apiTestResult');
|
|
resultDiv.innerHTML = '<div class="alert alert-info">测试中...</div>';
|
|
|
|
const r = await fetch('/api/config/test', { method: 'POST' });
|
|
const data = await r.json();
|
|
|
|
if (data.success) {
|
|
resultDiv.innerHTML = '<div class="alert alert-success"><i class="bi bi-check-circle"></i> 连接成功</div>';
|
|
} else {
|
|
resultDiv.innerHTML = '<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 连接失败: ' + (data.error || '未知错误') + '</div>';
|
|
}
|
|
}
|
|
|
|
function toggleApiKey() {
|
|
const input = document.getElementById('apiKey');
|
|
const icon = document.getElementById('apiKeyToggleIcon');
|
|
if (input.type === 'password') {
|
|
input.type = 'text';
|
|
icon.className = 'bi bi-eye-slash';
|
|
} else {
|
|
input.type = 'password';
|
|
icon.className = 'bi bi-eye';
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %} |