common.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { NextRequest, NextResponse } from "next/server";
  2. import { getServerSideConfig } from "../config/server";
  3. import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
  4. import { collectModelTable } from "../utils/model";
  5. import { makeAzurePath } from "../azure";
  6. const serverConfig = getServerSideConfig();
  7. export async function requestOpenai(req: NextRequest) {
  8. const controller = new AbortController();
  9. const authValue = req.headers.get("Authorization") ?? "";
  10. const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization";
  11. let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
  12. "/api/openai/",
  13. "",
  14. );
  15. let baseUrl =
  16. serverConfig.azureUrl ?? serverConfig.baseUrl ?? OPENAI_BASE_URL;
  17. if (!baseUrl.startsWith("http")) {
  18. baseUrl = `https://${baseUrl}`;
  19. }
  20. if (baseUrl.endsWith("/")) {
  21. baseUrl = baseUrl.slice(0, -1);
  22. }
  23. console.log("[Proxy] ", path);
  24. console.log("[Base Url]", baseUrl);
  25. console.log("[Org ID]", serverConfig.openaiOrgId);
  26. const timeoutId = setTimeout(
  27. () => {
  28. controller.abort();
  29. },
  30. 10 * 60 * 1000,
  31. );
  32. if (serverConfig.isAzure) {
  33. if (!serverConfig.azureApiVersion) {
  34. return NextResponse.json({
  35. error: true,
  36. message: `missing AZURE_API_VERSION in server env vars`,
  37. });
  38. }
  39. path = makeAzurePath(path, serverConfig.azureApiVersion);
  40. }
  41. const fetchUrl = `${baseUrl}/${path}`;
  42. const fetchOptions: RequestInit = {
  43. headers: {
  44. "Content-Type": "application/json",
  45. "Cache-Control": "no-store",
  46. [authHeaderName]: authValue,
  47. ...(serverConfig.openaiOrgId && {
  48. "OpenAI-Organization": serverConfig.openaiOrgId,
  49. }),
  50. },
  51. method: req.method,
  52. body: req.body,
  53. // to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
  54. redirect: "manual",
  55. // @ts-ignore
  56. duplex: "half",
  57. signal: controller.signal,
  58. };
  59. // #1815 try to refuse gpt4 request
  60. if (serverConfig.customModels && req.body) {
  61. try {
  62. const modelTable = collectModelTable(
  63. DEFAULT_MODELS,
  64. serverConfig.customModels,
  65. );
  66. const clonedBody = await req.text();
  67. fetchOptions.body = clonedBody;
  68. const jsonBody = JSON.parse(clonedBody) as { model?: string };
  69. // not undefined and is false
  70. if (modelTable[jsonBody?.model ?? ""] === false) {
  71. return NextResponse.json(
  72. {
  73. error: true,
  74. message: `you are not allowed to use ${jsonBody?.model} model`,
  75. },
  76. {
  77. status: 403,
  78. },
  79. );
  80. }
  81. } catch (e) {
  82. console.error("[OpenAI] gpt4 filter", e);
  83. }
  84. }
  85. try {
  86. const res = await fetch(fetchUrl, fetchOptions);
  87. // to prevent browser prompt for credentials
  88. const newHeaders = new Headers(res.headers);
  89. newHeaders.delete("www-authenticate");
  90. // to disable nginx buffering
  91. newHeaders.set("X-Accel-Buffering", "no");
  92. return new Response(res.body, {
  93. status: res.status,
  94. statusText: res.statusText,
  95. headers: newHeaders,
  96. });
  97. } finally {
  98. clearTimeout(timeoutId);
  99. }
  100. }