server.js 5.6 KB

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