mirror of
https://github.com/foxhui/WebAI2API.git
synced 2026-06-16 21:03:59 +08:00
148 lines
4.4 KiB
JavaScript
148 lines
4.4 KiB
JavaScript
/**
|
|
* @fileoverview 统一响应写出模块
|
|
* @description 封装 JSON、SSE 响应和错误响应的统一处理函数
|
|
*/
|
|
|
|
import { getErrorDetails } from '../errors.js';
|
|
|
|
/**
|
|
* 发送 JSON 响应
|
|
* @param {import('http').ServerResponse} res - HTTP 响应对象
|
|
* @param {number} status - HTTP 状态码
|
|
* @param {object} payload - 响应数据
|
|
*/
|
|
export function sendJson(res, status, payload) {
|
|
if (res.writableEnded) return;
|
|
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
res.end(JSON.stringify(payload));
|
|
}
|
|
|
|
/**
|
|
* 发送 SSE 事件
|
|
* @param {import('http').ServerResponse} res - HTTP 响应对象
|
|
* @param {object} payload - 事件数据
|
|
*/
|
|
export function sendSse(res, payload) {
|
|
if (res.writableEnded) return;
|
|
res.write(`data: ${JSON.stringify(payload)}\n\n`);
|
|
}
|
|
|
|
/**
|
|
* 发送 SSE 结束标记
|
|
* @param {import('http').ServerResponse} res - HTTP 响应对象
|
|
*/
|
|
export function sendSseDone(res) {
|
|
if (res.writableEnded) return;
|
|
res.write(`data: [DONE]\n\n`);
|
|
res.end();
|
|
}
|
|
|
|
/**
|
|
* 发送 SSE 心跳包
|
|
* @param {import('http').ServerResponse} res - HTTP 响应对象
|
|
* @param {string} mode - 心跳模式 ('comment' | 'content')
|
|
* @param {string} [modelName] - 模型名称(content 模式需要)
|
|
*/
|
|
export function sendHeartbeat(res, mode, modelName) {
|
|
if (res.writableEnded) return;
|
|
|
|
if (mode === 'comment') {
|
|
res.write(`:keepalive\n\n`);
|
|
} else {
|
|
// content 模式:发送空 delta
|
|
const chunk = {
|
|
id: 'chatcmpl-' + Date.now(),
|
|
object: 'chat.completion.chunk',
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: modelName || 'default-model',
|
|
choices: [{
|
|
index: 0,
|
|
delta: { content: '' },
|
|
finish_reason: null
|
|
}]
|
|
};
|
|
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送统一 API 错误响应
|
|
* @param {import('http').ServerResponse} res - HTTP 响应对象
|
|
* @param {object} options - 错误选项
|
|
* @param {string} [options.code] - 错误码(使用 ERROR_CODES 枚举)
|
|
* @param {string} [options.error] - 自定义错误消息(如提供则覆盖 code 对应的消息)
|
|
* @param {number} [options.status] - 自定义 HTTP 状态码
|
|
* @param {boolean} [options.isStreaming=false] - 是否为流式响应
|
|
* @param {string} [options.raw] - 原始错误信息(用于调试)
|
|
*/
|
|
export function sendApiError(res, options) {
|
|
const { code, error, status, isStreaming = false, raw } = options;
|
|
|
|
// 获取错误详情
|
|
const details = code ? getErrorDetails(code) : null;
|
|
const errorMessage = error || (details ? details.message : '未知错误');
|
|
const httpStatus = status || (details ? details.status : 500);
|
|
|
|
// 构造错误响应体
|
|
const payload = {
|
|
error: errorMessage,
|
|
code: code || 'INTERNAL_ERROR',
|
|
};
|
|
if (raw) {
|
|
payload.raw = raw;
|
|
}
|
|
|
|
if (isStreaming) {
|
|
// 流式响应
|
|
sendSse(res, payload);
|
|
sendSseDone(res);
|
|
} else {
|
|
// 非流式响应
|
|
sendJson(res, httpStatus, payload);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 构造 OpenAI 格式的聊天完成响应(非流式)
|
|
* @param {string} content - 响应内容
|
|
* @param {string} [modelName] - 模型名称
|
|
* @returns {object} OpenAI 格式的响应对象
|
|
*/
|
|
export function buildChatCompletion(content, modelName) {
|
|
return {
|
|
id: 'chatcmpl-' + Date.now(),
|
|
object: 'chat.completion',
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: modelName || 'default-model',
|
|
choices: [{
|
|
index: 0,
|
|
message: {
|
|
role: 'assistant',
|
|
content: content
|
|
},
|
|
finish_reason: 'stop'
|
|
}]
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 构造 OpenAI 格式的流式聊天完成响应块
|
|
* @param {string} content - 响应内容
|
|
* @param {string} [modelName] - 模型名称
|
|
* @param {string|null} [finishReason='stop'] - 完成原因
|
|
* @returns {object} OpenAI 格式的流式响应块
|
|
*/
|
|
export function buildChatCompletionChunk(content, modelName, finishReason = 'stop') {
|
|
return {
|
|
id: 'chatcmpl-' + Date.now(),
|
|
object: 'chat.completion.chunk',
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: modelName || 'default-model',
|
|
choices: [{
|
|
index: 0,
|
|
delta: { content },
|
|
finish_reason: finishReason
|
|
}]
|
|
};
|
|
}
|