feat: add DeepSeek Chat API streaming wrapper

This commit is contained in:
z2_cc 2026-05-13 11:38:08 +08:00
parent 6a9b9e2cc6
commit bec48dca72
1 changed files with 87 additions and 0 deletions

87
api/llm/deepseek.js Normal file
View File

@ -0,0 +1,87 @@
// api/llm/deepseek.js
const https = require('https');
const http = require('http');
function createStream(messages, options = {}) {
const apiKey = process.env.DEEPSEEK_API_KEY;
const baseUrl = process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com';
const model = process.env.DEEPSEEK_CHAT_MODEL || 'deepseek-chat';
if (!apiKey) {
throw new Error('DEEPSEEK_API_KEY environment variable is required');
}
const body = JSON.stringify({
model,
messages,
stream: true,
temperature: options.temperature || 0.7,
max_tokens: options.maxTokens || 2048,
});
const url = new URL('/chat/completions', baseUrl);
const transport = url.protocol === 'https:' ? https : http;
const reqOptions = {
hostname: url.hostname,
port: url.port || (url.protocol === 'https:' ? 443 : 80),
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'Content-Length': Buffer.byteLength(body),
},
};
return { reqOptions, transport, body };
}
/**
* Stream DeepSeek chat completions as an async iterator of content strings.
* @param {Array<{role: string, content: string}>} messages
* @param {object} options
* @yields {string} content delta
*/
async function* streamChat(messages, options = {}) {
const { reqOptions, transport, body } = createStream(messages, options);
const res = await new Promise((resolve, reject) => {
const req = transport.request(reqOptions, resolve);
req.on('error', reject);
req.write(body);
req.end();
});
if (res.statusCode !== 200) {
const errBody = await new Promise(r => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => r(data));
});
throw new Error(`DeepSeek API error ${res.statusCode}: ${errBody}`);
}
let buffer = '';
for await (const chunk of res) {
buffer += chunk.toString();
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith('data: ')) continue;
const data = trimmed.slice(6);
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) yield content;
} catch {
// skip malformed lines
}
}
}
}
module.exports = { streamChat };