server.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import express from "express";
  2. import cors from "cors";
  3. import logger from "./logger";
  4. import config from "./config";
  5. const app = express();
  6. // 中间件
  7. app.use(cors());
  8. app.use(express.json());
  9. const api_url = config["api-url"];
  10. // POST 路由处理OpenAI请求
  11. app.post("/api/openai", async (req, res) => {
  12. try {
  13. const { model_id, open_ai_url, api_key, payload } = req.body;
  14. let requestUrl = open_ai_url;
  15. let apiKey = api_key;
  16. let aiBody = payload;
  17. // 验证必需的参数
  18. if (!model_id) {
  19. if (!open_ai_url || !api_key || !payload) {
  20. return res.status(400).json({
  21. error:
  22. "Missing required parameters: open_ai_url, api_key, or payload",
  23. });
  24. }
  25. } else {
  26. //get model info from api server
  27. try {
  28. const url = api_url + `/v2/ai-model/${model_id}`;
  29. logger.info("get model info from api server " + url);
  30. const response = await fetch(url, {
  31. method: "GET",
  32. headers: {
  33. "Content-Type": "application/json",
  34. },
  35. });
  36. // 获取响应数据
  37. const model = await response.json();
  38. if (model.ok) {
  39. requestUrl = model.data.url;
  40. apiKey = model.data.key;
  41. aiBody.model = model.data.model;
  42. } else {
  43. return res.status(400).json({
  44. error:
  45. "Missing required parameters: open_ai_url, api_key, or payload",
  46. });
  47. }
  48. } catch (error) {
  49. logger.error(error.message);
  50. if (!res.headersSent) {
  51. res.status(500).json({
  52. error: "Proxy server error",
  53. message: error.message,
  54. type: "proxy_error",
  55. });
  56. }
  57. }
  58. }
  59. // 检测不同的 AI 服务提供商
  60. const isClaudeAPI =
  61. requestUrl.includes("anthropic.com") || requestUrl.includes("claude");
  62. const isStreaming = aiBody.stream === true;
  63. // 构建请求URL和headers
  64. let headers = {
  65. "Content-Type": "application/json",
  66. Authorization: `Bearer ${apiKey}`,
  67. };
  68. if (isClaudeAPI) {
  69. // Claude API使用特殊的header格式
  70. headers["x-api-key"] = apiKey;
  71. headers["anthropic-version"] = "2023-06-01";
  72. }
  73. logger.info("request " + requestUrl);
  74. if (isStreaming) {
  75. // 流式响应处理
  76. res.setHeader("Content-Type", "text/event-stream");
  77. res.setHeader("Cache-Control", "no-cache");
  78. res.setHeader("Connection", "keep-alive");
  79. res.setHeader("Access-Control-Allow-Origin", "*");
  80. try {
  81. const response = await fetch(requestUrl, {
  82. method: "POST",
  83. headers: headers,
  84. body: JSON.stringify(aiBody),
  85. });
  86. // 复制响应头到客户端
  87. response.headers.forEach((value, key) => {
  88. // 跳过一些不需要的头部
  89. if (
  90. ![
  91. "content-encoding",
  92. "content-length",
  93. "transfer-encoding",
  94. ].includes(key.toLowerCase())
  95. ) {
  96. res.setHeader(key, value);
  97. }
  98. });
  99. // 设置响应状态码(直接使用大模型返回的状态码)
  100. res.status(response.status);
  101. if (response.status !== 200) {
  102. logger.error(response.status);
  103. logger.error(JSON.stringify(response.body));
  104. } else {
  105. logger.info(response.status);
  106. }
  107. if (!response.ok) {
  108. // 对于错误响应,也要透传原始数据
  109. const reader = response.body.getReader();
  110. const decoder = new TextDecoder();
  111. while (true) {
  112. const { done, value } = await reader.read();
  113. if (done) break;
  114. const chunk = decoder.decode(value, { stream: true });
  115. res.write(chunk);
  116. }
  117. res.end();
  118. return;
  119. }
  120. // 处理成功的流式响应
  121. const reader = response.body.getReader();
  122. const decoder = new TextDecoder();
  123. while (true) {
  124. const { done, value } = await reader.read();
  125. if (done) break;
  126. const chunk = decoder.decode(value, { stream: true });
  127. res.write(chunk);
  128. }
  129. res.end();
  130. } catch (streamError) {
  131. logger.error("Streaming error:" + streamError.message);
  132. // 网络错误或其他系统错误
  133. res.status(500);
  134. res.setHeader("Content-Type", "application/json");
  135. res.json({
  136. error: "Proxy server error",
  137. message: streamError.message,
  138. type: "proxy_error",
  139. });
  140. }
  141. } else {
  142. // 非流式响应处理
  143. const response = await fetch(requestUrl, {
  144. method: "POST",
  145. headers: headers,
  146. body: JSON.stringify(aiBody),
  147. });
  148. // 复制响应头到客户端
  149. response.headers.forEach((value, key) => {
  150. // 跳过一些不需要的头部
  151. if (
  152. !["content-encoding", "content-length", "transfer-encoding"].includes(
  153. key.toLowerCase()
  154. )
  155. ) {
  156. res.setHeader(key, value);
  157. }
  158. });
  159. // 设置响应状态码(直接使用大模型返回的状态码)
  160. res.status(response.status);
  161. // 获取响应数据
  162. const responseData = await response.json();
  163. // 直接返回原始响应数据,不进行任何修改
  164. res.json(responseData);
  165. }
  166. } catch (error) {
  167. logger.error("Proxy Error:" + error.message);
  168. // 只有在系统级错误时才返回代理服务器的错误信息
  169. // 比如网络错误、JSON解析错误等
  170. if (!res.headersSent) {
  171. res.status(500).json({
  172. error: "Proxy server error",
  173. message: error.message,
  174. type: "proxy_error",
  175. });
  176. }
  177. }
  178. });
  179. // 健康检查端点
  180. app.get("/health", (req, res) => {
  181. res.json({ status: "OK", timestamp: new Date().toISOString() });
  182. });
  183. export default app;