57 lines
2.5 KiB
JavaScript
57 lines
2.5 KiB
JavaScript
// DeepSeek API 客户端
|
||
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || '';
|
||
|
||
function sanitizeUserId(username) {
|
||
return username.replace(/[^a-zA-Z0-9\-_]/g, '_').substring(0, 64) || 'anon';
|
||
}
|
||
|
||
function getErrorMsg(status, data) {
|
||
var msg = data && data.error && data.error.message ? data.error.message : '';
|
||
switch (status) {
|
||
case 400: return 'AI 请求格式错误,请联系管理员(400)' + (msg ? ':' + msg : '');
|
||
case 401: return 'AI 认证失败,请检查 API Key(401)' + (msg ? ':' + msg : '');
|
||
case 402: return 'AI 账户余额不足,请联系管理员充值(402)';
|
||
case 422: return 'AI 参数错误(422)' + (msg ? ':' + msg : '');
|
||
case 429: return 'AI 请求太频繁,请稍后重试(429)';
|
||
case 500: return 'AI 服务器故障,请稍后重试(500)';
|
||
case 503: return 'AI 服务器繁忙,请稍后重试(503)';
|
||
default: return 'AI 响应异常(' + status + ')' + (msg ? ':' + msg : '');
|
||
}
|
||
}
|
||
|
||
async function callDeepSeek(messages, userId, temperature = 0.1, jsonMode = true) {
|
||
console.log('调用 DeepSeek...');
|
||
var body = { model: 'deepseek-v4-flash', messages: messages, temperature: temperature, stream: false, thinking: { type: 'disabled' } };
|
||
if (jsonMode) body.response_format = { type: 'json_object' };
|
||
if (userId) body.user_id = sanitizeUserId(userId);
|
||
var res = await fetch('https://api.deepseek.com/chat/completions', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + DEEPSEEK_API_KEY },
|
||
body: JSON.stringify(body)
|
||
});
|
||
var data = await res.json();
|
||
if (!res.ok) {
|
||
var errMsg = getErrorMsg(res.status, data);
|
||
console.error('DeepSeek API 错误:', errMsg);
|
||
throw new Error(errMsg);
|
||
}
|
||
if (!data.choices || !data.choices[0]) {
|
||
console.error('DeepSeek 返回异常:', JSON.stringify(data));
|
||
throw new Error('AI 返回数据异常');
|
||
}
|
||
// 缓存命中日志
|
||
if (data.usage) {
|
||
var hit = data.usage.prompt_cache_hit_tokens || 0;
|
||
var miss = data.usage.prompt_cache_miss_tokens || 0;
|
||
var total = data.usage.prompt_tokens || 0;
|
||
var rate = total > 0 ? (hit / total * 100).toFixed(1) : 0;
|
||
console.log('缓存: hit=' + hit + ' miss=' + miss + ' total=' + total + ' 命中率=' + rate + '%');
|
||
}
|
||
// 输入消息大小日志
|
||
var inputChars = JSON.stringify(messages).length;
|
||
console.log('输入大小: ' + inputChars + ' 字符, ' + messages.length + ' 条消息');
|
||
return data.choices[0].message.content;
|
||
}
|
||
|
||
module.exports = { callDeepSeek };
|