mirror of
				https://github.com/Yidadaa/ChatGPT-Next-Web.git
				synced 2025-11-04 08:26:12 +08:00 
			
		
		
		
	Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web
This commit is contained in:
		@@ -4,12 +4,13 @@ import {
 | 
				
			|||||||
  Anthropic,
 | 
					  Anthropic,
 | 
				
			||||||
  ApiPath,
 | 
					  ApiPath,
 | 
				
			||||||
  DEFAULT_MODELS,
 | 
					  DEFAULT_MODELS,
 | 
				
			||||||
 | 
					  ServiceProvider,
 | 
				
			||||||
  ModelProvider,
 | 
					  ModelProvider,
 | 
				
			||||||
} from "@/app/constant";
 | 
					} from "@/app/constant";
 | 
				
			||||||
import { prettyObject } from "@/app/utils/format";
 | 
					import { prettyObject } from "@/app/utils/format";
 | 
				
			||||||
import { NextRequest, NextResponse } from "next/server";
 | 
					import { NextRequest, NextResponse } from "next/server";
 | 
				
			||||||
import { auth } from "../../auth";
 | 
					import { auth } from "../../auth";
 | 
				
			||||||
import { collectModelTable } from "@/app/utils/model";
 | 
					import { isModelAvailableInServer } from "@/app/utils/model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
 | 
					const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -136,17 +137,19 @@ async function request(req: NextRequest) {
 | 
				
			|||||||
  // #1815 try to refuse some request to some models
 | 
					  // #1815 try to refuse some request to some models
 | 
				
			||||||
  if (serverConfig.customModels && req.body) {
 | 
					  if (serverConfig.customModels && req.body) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const modelTable = collectModelTable(
 | 
					 | 
				
			||||||
        DEFAULT_MODELS,
 | 
					 | 
				
			||||||
        serverConfig.customModels,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      const clonedBody = await req.text();
 | 
					      const clonedBody = await req.text();
 | 
				
			||||||
      fetchOptions.body = clonedBody;
 | 
					      fetchOptions.body = clonedBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const jsonBody = JSON.parse(clonedBody) as { model?: string };
 | 
					      const jsonBody = JSON.parse(clonedBody) as { model?: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // not undefined and is false
 | 
					      // not undefined and is false
 | 
				
			||||||
      if (modelTable[jsonBody?.model ?? ""].available === false) {
 | 
					      if (
 | 
				
			||||||
 | 
					        isModelAvailableInServer(
 | 
				
			||||||
 | 
					          serverConfig.customModels,
 | 
				
			||||||
 | 
					          jsonBody?.model as string,
 | 
				
			||||||
 | 
					          ServiceProvider.Anthropic as string,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        return NextResponse.json(
 | 
					        return NextResponse.json(
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            error: true,
 | 
					            error: true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,12 @@
 | 
				
			|||||||
import { NextRequest, NextResponse } from "next/server";
 | 
					import { NextRequest, NextResponse } from "next/server";
 | 
				
			||||||
import { getServerSideConfig } from "../config/server";
 | 
					import { getServerSideConfig } from "../config/server";
 | 
				
			||||||
import { DEFAULT_MODELS, OPENAI_BASE_URL, GEMINI_BASE_URL } from "../constant";
 | 
					import {
 | 
				
			||||||
import { collectModelTable } from "../utils/model";
 | 
					  DEFAULT_MODELS,
 | 
				
			||||||
 | 
					  OPENAI_BASE_URL,
 | 
				
			||||||
 | 
					  GEMINI_BASE_URL,
 | 
				
			||||||
 | 
					  ServiceProvider,
 | 
				
			||||||
 | 
					} from "../constant";
 | 
				
			||||||
 | 
					import { isModelAvailableInServer } from "../utils/model";
 | 
				
			||||||
import { makeAzurePath } from "../azure";
 | 
					import { makeAzurePath } from "../azure";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const serverConfig = getServerSideConfig();
 | 
					const serverConfig = getServerSideConfig();
 | 
				
			||||||
@@ -83,17 +88,24 @@ export async function requestOpenai(req: NextRequest) {
 | 
				
			|||||||
  // #1815 try to refuse gpt4 request
 | 
					  // #1815 try to refuse gpt4 request
 | 
				
			||||||
  if (serverConfig.customModels && req.body) {
 | 
					  if (serverConfig.customModels && req.body) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const modelTable = collectModelTable(
 | 
					 | 
				
			||||||
        DEFAULT_MODELS,
 | 
					 | 
				
			||||||
        serverConfig.customModels,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      const clonedBody = await req.text();
 | 
					      const clonedBody = await req.text();
 | 
				
			||||||
      fetchOptions.body = clonedBody;
 | 
					      fetchOptions.body = clonedBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const jsonBody = JSON.parse(clonedBody) as { model?: string };
 | 
					      const jsonBody = JSON.parse(clonedBody) as { model?: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // not undefined and is false
 | 
					      // not undefined and is false
 | 
				
			||||||
      if (modelTable[jsonBody?.model ?? ""].available === false) {
 | 
					      if (
 | 
				
			||||||
 | 
					        isModelAvailableInServer(
 | 
				
			||||||
 | 
					          serverConfig.customModels,
 | 
				
			||||||
 | 
					          jsonBody?.model as string,
 | 
				
			||||||
 | 
					          ServiceProvider.OpenAI as string,
 | 
				
			||||||
 | 
					        ) ||
 | 
				
			||||||
 | 
					        isModelAvailableInServer(
 | 
				
			||||||
 | 
					          serverConfig.customModels,
 | 
				
			||||||
 | 
					          jsonBody?.model as string,
 | 
				
			||||||
 | 
					          ServiceProvider.Azure as string,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        return NextResponse.json(
 | 
					        return NextResponse.json(
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            error: true,
 | 
					            error: true,
 | 
				
			||||||
@@ -112,16 +124,16 @@ export async function requestOpenai(req: NextRequest) {
 | 
				
			|||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const res = await fetch(fetchUrl, fetchOptions);
 | 
					    const res = await fetch(fetchUrl, fetchOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Extract the OpenAI-Organization header from the response
 | 
					    // Extract the OpenAI-Organization header from the response
 | 
				
			||||||
  const openaiOrganizationHeader = res.headers.get("OpenAI-Organization");
 | 
					    const openaiOrganizationHeader = res.headers.get("OpenAI-Organization");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check if serverConfig.openaiOrgId is defined and not an empty string
 | 
					    // Check if serverConfig.openaiOrgId is defined and not an empty string
 | 
				
			||||||
  if (serverConfig.openaiOrgId && serverConfig.openaiOrgId.trim() !== "") {
 | 
					    if (serverConfig.openaiOrgId && serverConfig.openaiOrgId.trim() !== "") {
 | 
				
			||||||
    // If openaiOrganizationHeader is present, log it; otherwise, log that the header is not present
 | 
					      // If openaiOrganizationHeader is present, log it; otherwise, log that the header is not present
 | 
				
			||||||
    console.log("[Org ID]", openaiOrganizationHeader);
 | 
					      console.log("[Org ID]", openaiOrganizationHeader);
 | 
				
			||||||
  } else {
 | 
					    } else {
 | 
				
			||||||
    console.log("[Org ID] is not set up.");
 | 
					      console.log("[Org ID] is not set up.");
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // to prevent browser prompt for credentials
 | 
					    // to prevent browser prompt for credentials
 | 
				
			||||||
    const newHeaders = new Headers(res.headers);
 | 
					    const newHeaders = new Headers(res.headers);
 | 
				
			||||||
@@ -129,7 +141,6 @@ export async function requestOpenai(req: NextRequest) {
 | 
				
			|||||||
    // to disable nginx buffering
 | 
					    // to disable nginx buffering
 | 
				
			||||||
    newHeaders.set("X-Accel-Buffering", "no");
 | 
					    newHeaders.set("X-Accel-Buffering", "no");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Conditionally delete the OpenAI-Organization header from the response if [Org ID] is undefined or empty (not setup in ENV)
 | 
					    // Conditionally delete the OpenAI-Organization header from the response if [Org ID] is undefined or empty (not setup in ENV)
 | 
				
			||||||
    // Also, this is to prevent the header from being sent to the client
 | 
					    // Also, this is to prevent the header from being sent to the client
 | 
				
			||||||
    if (!serverConfig.openaiOrgId || serverConfig.openaiOrgId.trim() === "") {
 | 
					    if (!serverConfig.openaiOrgId || serverConfig.openaiOrgId.trim() === "") {
 | 
				
			||||||
@@ -142,7 +153,6 @@ export async function requestOpenai(req: NextRequest) {
 | 
				
			|||||||
    // The browser will try to decode the response with brotli and fail
 | 
					    // The browser will try to decode the response with brotli and fail
 | 
				
			||||||
    newHeaders.delete("content-encoding");
 | 
					    newHeaders.delete("content-encoding");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return new Response(res.body, {
 | 
					    return new Response(res.body, {
 | 
				
			||||||
      status: res.status,
 | 
					      status: res.status,
 | 
				
			||||||
      statusText: res.statusText,
 | 
					      statusText: res.statusText,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,7 +97,17 @@ function setItem(key: string, value: string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function getLanguage() {
 | 
					function getLanguage() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    return navigator.language.toLowerCase();
 | 
					    const locale = new Intl.Locale(navigator.language).maximize();
 | 
				
			||||||
 | 
					    const region = locale?.region?.toLowerCase();
 | 
				
			||||||
 | 
					    // 1. check region code in ALL_LANGS
 | 
				
			||||||
 | 
					    if (AllLangs.includes(region as Lang)) {
 | 
				
			||||||
 | 
					      return region as Lang;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 2. check language code in ALL_LANGS
 | 
				
			||||||
 | 
					    if (AllLangs.includes(locale.language as Lang)) {
 | 
				
			||||||
 | 
					      return locale.language as Lang;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return DEFAULT_LANG;
 | 
				
			||||||
  } catch {
 | 
					  } catch {
 | 
				
			||||||
    return DEFAULT_LANG;
 | 
					    return DEFAULT_LANG;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -110,15 +120,7 @@ export function getLang(): Lang {
 | 
				
			|||||||
    return savedLang as Lang;
 | 
					    return savedLang as Lang;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const lang = getLanguage();
 | 
					  return getLanguage();
 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (const option of AllLangs) {
 | 
					 | 
				
			||||||
    if (lang.includes(option)) {
 | 
					 | 
				
			||||||
      return option;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return DEFAULT_LANG;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function changeLang(lang: Lang) {
 | 
					export function changeLang(lang: Lang) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,12 +116,12 @@ export const useAppConfig = createPersistStore(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      for (const model of oldModels) {
 | 
					      for (const model of oldModels) {
 | 
				
			||||||
        model.available = false;
 | 
					        model.available = false;
 | 
				
			||||||
        modelMap[model.name] = model;
 | 
					        modelMap[`${model.name}@${model?.provider?.id}`] = model;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      for (const model of newModels) {
 | 
					      for (const model of newModels) {
 | 
				
			||||||
        model.available = true;
 | 
					        model.available = true;
 | 
				
			||||||
        modelMap[model.name] = model;
 | 
					        modelMap[`${model.name}@${model?.provider?.id}`] = model;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      set(() => ({
 | 
					      set(() => ({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
 | 
					import { DEFAULT_MODELS } from "../constant";
 | 
				
			||||||
import { LLMModel } from "../client/api";
 | 
					import { LLMModel } from "../client/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const customProvider = (modelName: string) => ({
 | 
					const customProvider = (modelName: string) => ({
 | 
				
			||||||
  id: modelName,
 | 
					  id: modelName,
 | 
				
			||||||
  providerName: "",
 | 
					  providerName: "Custom",
 | 
				
			||||||
  providerType: "custom",
 | 
					  providerType: "custom",
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,7 +24,8 @@ export function collectModelTable(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // default models
 | 
					  // default models
 | 
				
			||||||
  models.forEach((m) => {
 | 
					  models.forEach((m) => {
 | 
				
			||||||
    modelTable[m.name] = {
 | 
					    // using <modelName>@<providerId> as fullName
 | 
				
			||||||
 | 
					    modelTable[`${m.name}@${m?.provider?.id}`] = {
 | 
				
			||||||
      ...m,
 | 
					      ...m,
 | 
				
			||||||
      displayName: m.name, // 'provider' is copied over if it exists
 | 
					      displayName: m.name, // 'provider' is copied over if it exists
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -45,12 +47,27 @@ export function collectModelTable(
 | 
				
			|||||||
          (model) => (model.available = available),
 | 
					          (model) => (model.available = available),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        modelTable[name] = {
 | 
					        // 1. find model by name(), and set available value
 | 
				
			||||||
          name,
 | 
					        let count = 0;
 | 
				
			||||||
          displayName: displayName || name,
 | 
					        for (const fullName in modelTable) {
 | 
				
			||||||
          available,
 | 
					          if (fullName.split("@").shift() == name) {
 | 
				
			||||||
          provider: modelTable[name]?.provider ?? customProvider(name), // Use optional chaining
 | 
					            count += 1;
 | 
				
			||||||
        };
 | 
					            modelTable[fullName]["available"] = available;
 | 
				
			||||||
 | 
					            if (displayName) {
 | 
				
			||||||
 | 
					              modelTable[fullName]["displayName"] = displayName;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // 2. if model not exists, create new model with available value
 | 
				
			||||||
 | 
					        if (count === 0) {
 | 
				
			||||||
 | 
					          const provider = customProvider(name);
 | 
				
			||||||
 | 
					          modelTable[`${name}@${provider?.id}`] = {
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            displayName: displayName || name,
 | 
				
			||||||
 | 
					            available,
 | 
				
			||||||
 | 
					            provider, // Use optional chaining
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,3 +117,13 @@ export function collectModelsWithDefaultModel(
 | 
				
			|||||||
  const allModels = Object.values(modelTable);
 | 
					  const allModels = Object.values(modelTable);
 | 
				
			||||||
  return allModels;
 | 
					  return allModels;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isModelAvailableInServer(
 | 
				
			||||||
 | 
					  customModels: string,
 | 
				
			||||||
 | 
					  modelName: string,
 | 
				
			||||||
 | 
					  providerName: string,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const fullName = `${modelName}@${providerName}`;
 | 
				
			||||||
 | 
					  const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
 | 
				
			||||||
 | 
					  return modelTable[fullName]?.available === false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user