浏览代码

no message

Ryuiso 3 月之前
父节点
当前提交
19a11aa8ed

+ 1 - 0
.gitignore

@@ -42,6 +42,7 @@ dev
 
 # docker-compose env files
 .env
+.env.local
 
 *.key
 *.key.pub

+ 0 - 12
app/api/[provider]/[...path]/route.ts

@@ -2,13 +2,9 @@ import { ApiPath } from "@/app/constant";
 import { NextRequest, NextResponse } from "next/server";
 import { handle as openaiHandler } from "../../openai";
 import { handle as azureHandler } from "../../azure";
-import { handle as googleHandler } from "../../google";
-import { handle as anthropicHandler } from "../../anthropic";
 import { handle as baiduHandler } from "../../baidu";
 import { handle as bytedanceHandler } from "../../bytedance";
 import { handle as alibabaHandler } from "../../alibaba";
-import { handle as moonshotHandler } from "../../moonshot";
-import { handle as stabilityHandler } from "../../stability";
 import { handle as iflytekHandler } from "../../iflytek";
 async function handle(
   req: NextRequest,
@@ -19,10 +15,6 @@ async function handle(
   switch (apiPath) {
     case ApiPath.Azure:
       return azureHandler(req, { params });
-    case ApiPath.Google:
-      return googleHandler(req, { params });
-    case ApiPath.Anthropic:
-      return anthropicHandler(req, { params });
     case ApiPath.Baidu:
       return baiduHandler(req, { params });
     case ApiPath.ByteDance:
@@ -30,10 +22,6 @@ async function handle(
     case ApiPath.Alibaba:
       return alibabaHandler(req, { params });
     // case ApiPath.Tencent: using "/api/tencent"
-    case ApiPath.Moonshot:
-      return moonshotHandler(req, { params });
-    case ApiPath.Stability:
-      return stabilityHandler(req, { params });
     case ApiPath.Iflytek:
       return iflytekHandler(req, { params });
     default:

+ 0 - 170
app/api/anthropic.ts

@@ -1,170 +0,0 @@
-import { getServerSideConfig } from "@/app/config/server";
-import {
-  ANTHROPIC_BASE_URL,
-  Anthropic,
-  ApiPath,
-  DEFAULT_MODELS,
-  ServiceProvider,
-  ModelProvider,
-} from "@/app/constant";
-import { prettyObject } from "@/app/utils/format";
-import { NextRequest, NextResponse } from "next/server";
-import { auth } from "./auth";
-import { isModelAvailableInServer } from "@/app/utils/model";
-import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
-
-const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
-
-export async function handle(
-  req: NextRequest,
-  { params }: { params: { path: string[] } },
-) {
-  console.log("[Anthropic Route] params ", params);
-
-  if (req.method === "OPTIONS") {
-    return NextResponse.json({ body: "OK" }, { status: 200 });
-  }
-
-  const subpath = params.path.join("/");
-
-  if (!ALLOWD_PATH.has(subpath)) {
-    console.log("[Anthropic Route] forbidden path ", subpath);
-    return NextResponse.json(
-      {
-        error: true,
-        msg: "you are not allowed to request " + subpath,
-      },
-      {
-        status: 403,
-      },
-    );
-  }
-
-  const authResult = auth(req, ModelProvider.Claude);
-  if (authResult.error) {
-    return NextResponse.json(authResult, {
-      status: 401,
-    });
-  }
-
-  try {
-    const response = await request(req);
-    return response;
-  } catch (e) {
-    console.error("[Anthropic] ", e);
-    return NextResponse.json(prettyObject(e));
-  }
-}
-
-const serverConfig = getServerSideConfig();
-
-async function request(req: NextRequest) {
-  const controller = new AbortController();
-
-  let authHeaderName = "x-api-key";
-  let authValue =
-    req.headers.get(authHeaderName) ||
-    req.headers.get("Authorization")?.replaceAll("Bearer ", "").trim() ||
-    serverConfig.anthropicApiKey ||
-    "";
-
-  let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Anthropic, "");
-
-  let baseUrl =
-    serverConfig.anthropicUrl || serverConfig.baseUrl || ANTHROPIC_BASE_URL;
-
-  if (!baseUrl.startsWith("http")) {
-    baseUrl = `https://${baseUrl}`;
-  }
-
-  if (baseUrl.endsWith("/")) {
-    baseUrl = baseUrl.slice(0, -1);
-  }
-
-  console.log("[Proxy] ", path);
-  console.log("[Base Url]", baseUrl);
-
-  const timeoutId = setTimeout(
-    () => {
-      controller.abort();
-    },
-    10 * 60 * 1000,
-  );
-
-  // try rebuild url, when using cloudflare ai gateway in server
-  const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}${path}`);
-
-  const fetchOptions: RequestInit = {
-    headers: {
-      "Content-Type": "application/json",
-      "Cache-Control": "no-store",
-      [authHeaderName]: authValue,
-      "anthropic-version":
-        req.headers.get("anthropic-version") ||
-        serverConfig.anthropicApiVersion ||
-        Anthropic.Vision,
-    },
-    method: req.method,
-    body: req.body,
-    redirect: "manual",
-    // @ts-ignore
-    duplex: "half",
-    signal: controller.signal,
-  };
-
-  // #1815 try to refuse some request to some models
-  if (serverConfig.customModels && req.body) {
-    try {
-      const clonedBody = await req.text();
-      fetchOptions.body = clonedBody;
-
-      const jsonBody = JSON.parse(clonedBody) as { model?: string };
-
-      // not undefined and is false
-      if (
-        isModelAvailableInServer(
-          serverConfig.customModels,
-          jsonBody?.model as string,
-          ServiceProvider.Anthropic as string,
-        )
-      ) {
-        return NextResponse.json(
-          {
-            error: true,
-            message: `you are not allowed to use ${jsonBody?.model} model`,
-          },
-          {
-            status: 403,
-          },
-        );
-      }
-    } catch (e) {
-      console.error(`[Anthropic] filter`, e);
-    }
-  }
-  // console.log("[Anthropic request]", fetchOptions.headers, req.method);
-  try {
-    const res = await fetch(fetchUrl, fetchOptions);
-
-    // console.log(
-    //   "[Anthropic response]",
-    //   res.status,
-    //   "   ",
-    //   res.headers,
-    //   res.url,
-    // );
-    // to prevent browser prompt for credentials
-    const newHeaders = new Headers(res.headers);
-    newHeaders.delete("www-authenticate");
-    // to disable nginx buffering
-    newHeaders.set("X-Accel-Buffering", "no");
-
-    return new Response(res.body, {
-      status: res.status,
-      statusText: res.statusText,
-      headers: newHeaders,
-    });
-  } finally {
-    clearTimeout(timeoutId);
-  }
-}

+ 0 - 12
app/api/auth.ts

@@ -67,15 +67,6 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
     let systemApiKey: string | undefined;
 
     switch (modelProvider) {
-      case ModelProvider.Stability:
-        systemApiKey = serverConfig.stabilityApiKey;
-        break;
-      case ModelProvider.GeminiPro:
-        systemApiKey = serverConfig.googleApiKey;
-        break;
-      case ModelProvider.Claude:
-        systemApiKey = serverConfig.anthropicApiKey;
-        break;
       case ModelProvider.Doubao:
         systemApiKey = serverConfig.bytedanceApiKey;
         break;
@@ -85,9 +76,6 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
       case ModelProvider.Qwen:
         systemApiKey = serverConfig.alibabaApiKey;
         break;
-      case ModelProvider.Moonshot:
-        systemApiKey = serverConfig.moonshotApiKey;
-        break;
       case ModelProvider.Iflytek:
         systemApiKey =
           serverConfig.iflytekApiKey + ":" + serverConfig.iflytekApiSecret;

+ 0 - 1
app/api/common.ts

@@ -3,7 +3,6 @@ import { getServerSideConfig } from "../config/server";
 import {
   DEFAULT_MODELS,
   OPENAI_BASE_URL,
-  GEMINI_BASE_URL,
   ServiceProvider,
 } from "../constant";
 import { isModelAvailableInServer } from "../utils/model";

+ 0 - 134
app/api/google.ts

@@ -1,134 +0,0 @@
-import { NextRequest, NextResponse } from "next/server";
-import { auth } from "./auth";
-import { getServerSideConfig } from "@/app/config/server";
-import {
-  ApiPath,
-  GEMINI_BASE_URL,
-  Google,
-  ModelProvider,
-} from "@/app/constant";
-import { prettyObject } from "@/app/utils/format";
-
-const serverConfig = getServerSideConfig();
-
-export async function handle(
-  req: NextRequest,
-  { params }: { params: { provider: string; path: string[] } },
-) {
-  console.log("[Google Route] params ", params);
-
-  if (req.method === "OPTIONS") {
-    return NextResponse.json({ body: "OK" }, { status: 200 });
-  }
-
-  const authResult = auth(req, ModelProvider.GeminiPro);
-  if (authResult.error) {
-    return NextResponse.json(authResult, {
-      status: 401,
-    });
-  }
-
-  const bearToken = req.headers.get("Authorization") ?? "";
-  const token = bearToken.trim().replaceAll("Bearer ", "").trim();
-
-  const apiKey = token ? token : serverConfig.googleApiKey;
-
-  if (!apiKey) {
-    return NextResponse.json(
-      {
-        error: true,
-        message: `missing GOOGLE_API_KEY in server env vars`,
-      },
-      {
-        status: 401,
-      },
-    );
-  }
-  try {
-    const response = await request(req, apiKey);
-    return response;
-  } catch (e) {
-    console.error("[Google] ", e);
-    return NextResponse.json(prettyObject(e));
-  }
-}
-
-export const GET = handle;
-export const POST = handle;
-
-export const runtime = "edge";
-export const preferredRegion = [
-  "bom1",
-  "cle1",
-  "cpt1",
-  "gru1",
-  "hnd1",
-  "iad1",
-  "icn1",
-  "kix1",
-  "pdx1",
-  "sfo1",
-  "sin1",
-  "syd1",
-];
-
-async function request(req: NextRequest, apiKey: string) {
-  const controller = new AbortController();
-
-  let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL;
-
-  let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Google, "");
-
-  if (!baseUrl.startsWith("http")) {
-    baseUrl = `https://${baseUrl}`;
-  }
-
-  if (baseUrl.endsWith("/")) {
-    baseUrl = baseUrl.slice(0, -1);
-  }
-
-  console.log("[Proxy] ", path);
-  console.log("[Base Url]", baseUrl);
-
-  const timeoutId = setTimeout(
-    () => {
-      controller.abort();
-    },
-    10 * 60 * 1000,
-  );
-  const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
-    req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
-  }`;
-
-  console.log("[Fetch Url] ", fetchUrl);
-  const fetchOptions: RequestInit = {
-    headers: {
-      "Content-Type": "application/json",
-      "Cache-Control": "no-store",
-    },
-    method: req.method,
-    body: req.body,
-    // to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
-    redirect: "manual",
-    // @ts-ignore
-    duplex: "half",
-    signal: controller.signal,
-  };
-
-  try {
-    const res = await fetch(fetchUrl, fetchOptions);
-    // to prevent browser prompt for credentials
-    const newHeaders = new Headers(res.headers);
-    newHeaders.delete("www-authenticate");
-    // to disable nginx buffering
-    newHeaders.set("X-Accel-Buffering", "no");
-
-    return new Response(res.body, {
-      status: res.status,
-      statusText: res.statusText,
-      headers: newHeaders,
-    });
-  } finally {
-    clearTimeout(timeoutId);
-  }
-}

+ 0 - 130
app/api/moonshot.ts

@@ -1,130 +0,0 @@
-import { getServerSideConfig } from "@/app/config/server";
-import {
-  Moonshot,
-  MOONSHOT_BASE_URL,
-  ApiPath,
-  ModelProvider,
-  ServiceProvider,
-} from "@/app/constant";
-import { prettyObject } from "@/app/utils/format";
-import { NextRequest, NextResponse } from "next/server";
-import { auth } from "@/app/api/auth";
-import { isModelAvailableInServer } from "@/app/utils/model";
-import type { RequestPayload } from "@/app/client/platforms/openai";
-
-const serverConfig = getServerSideConfig();
-
-export async function handle(
-  req: NextRequest,
-  { params }: { params: { path: string[] } },
-) {
-  console.log("[Moonshot Route] params ", params);
-
-  if (req.method === "OPTIONS") {
-    return NextResponse.json({ body: "OK" }, { status: 200 });
-  }
-
-  const authResult = auth(req, ModelProvider.Moonshot);
-  if (authResult.error) {
-    return NextResponse.json(authResult, {
-      status: 401,
-    });
-  }
-
-  try {
-    const response = await request(req);
-    return response;
-  } catch (e) {
-    console.error("[Moonshot] ", e);
-    return NextResponse.json(prettyObject(e));
-  }
-}
-
-async function request(req: NextRequest) {
-  const controller = new AbortController();
-
-  // alibaba use base url or just remove the path
-  let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Moonshot, "");
-
-  let baseUrl = serverConfig.moonshotUrl || MOONSHOT_BASE_URL;
-
-  if (!baseUrl.startsWith("http")) {
-    baseUrl = `https://${baseUrl}`;
-  }
-
-  if (baseUrl.endsWith("/")) {
-    baseUrl = baseUrl.slice(0, -1);
-  }
-
-  console.log("[Proxy] ", path);
-  console.log("[Base Url]", baseUrl);
-
-  const timeoutId = setTimeout(
-    () => {
-      controller.abort();
-    },
-    10 * 60 * 1000,
-  );
-
-  const fetchUrl = `${baseUrl}${path}`;
-  const fetchOptions: RequestInit = {
-    headers: {
-      "Content-Type": "application/json",
-      Authorization: req.headers.get("Authorization") ?? "",
-    },
-    method: req.method,
-    body: req.body,
-    redirect: "manual",
-    // @ts-ignore
-    duplex: "half",
-    signal: controller.signal,
-  };
-
-  // #1815 try to refuse some request to some models
-  if (serverConfig.customModels && req.body) {
-    try {
-      const clonedBody = await req.text();
-      fetchOptions.body = clonedBody;
-
-      const jsonBody = JSON.parse(clonedBody) as { model?: string };
-
-      // not undefined and is false
-      if (
-        isModelAvailableInServer(
-          serverConfig.customModels,
-          jsonBody?.model as string,
-          ServiceProvider.Moonshot as string,
-        )
-      ) {
-        return NextResponse.json(
-          {
-            error: true,
-            message: `you are not allowed to use ${jsonBody?.model} model`,
-          },
-          {
-            status: 403,
-          },
-        );
-      }
-    } catch (e) {
-      console.error(`[Moonshot] filter`, e);
-    }
-  }
-  try {
-    const res = await fetch(fetchUrl, fetchOptions);
-
-    // to prevent browser prompt for credentials
-    const newHeaders = new Headers(res.headers);
-    newHeaders.delete("www-authenticate");
-    // to disable nginx buffering
-    newHeaders.set("X-Accel-Buffering", "no");
-
-    return new Response(res.body, {
-      status: res.status,
-      statusText: res.statusText,
-      headers: newHeaders,
-    });
-  } finally {
-    clearTimeout(timeoutId);
-  }
-}

+ 0 - 99
app/api/stability.ts

@@ -1,99 +0,0 @@
-import { NextRequest, NextResponse } from "next/server";
-import { getServerSideConfig } from "@/app/config/server";
-import { ModelProvider, STABILITY_BASE_URL } from "@/app/constant";
-import { auth } from "@/app/api/auth";
-
-export async function handle(
-  req: NextRequest,
-  { params }: { params: { path: string[] } },
-) {
-  console.log("[Stability] params ", params);
-
-  if (req.method === "OPTIONS") {
-    return NextResponse.json({ body: "OK" }, { status: 200 });
-  }
-
-  const controller = new AbortController();
-
-  const serverConfig = getServerSideConfig();
-
-  let baseUrl = serverConfig.stabilityUrl || STABILITY_BASE_URL;
-
-  if (!baseUrl.startsWith("http")) {
-    baseUrl = `https://${baseUrl}`;
-  }
-
-  if (baseUrl.endsWith("/")) {
-    baseUrl = baseUrl.slice(0, -1);
-  }
-
-  let path = `${req.nextUrl.pathname}`.replaceAll("/api/stability/", "");
-
-  console.log("[Stability Proxy] ", path);
-  console.log("[Stability Base Url]", baseUrl);
-
-  const timeoutId = setTimeout(
-    () => {
-      controller.abort();
-    },
-    10 * 60 * 1000,
-  );
-
-  const authResult = auth(req, ModelProvider.Stability);
-
-  if (authResult.error) {
-    return NextResponse.json(authResult, {
-      status: 401,
-    });
-  }
-
-  const bearToken = req.headers.get("Authorization") ?? "";
-  const token = bearToken.trim().replaceAll("Bearer ", "").trim();
-
-  const key = token ? token : serverConfig.stabilityApiKey;
-
-  if (!key) {
-    return NextResponse.json(
-      {
-        error: true,
-        message: `missing STABILITY_API_KEY in server env vars`,
-      },
-      {
-        status: 401,
-      },
-    );
-  }
-
-  const fetchUrl = `${baseUrl}/${path}`;
-  console.log("[Stability Url] ", fetchUrl);
-  const fetchOptions: RequestInit = {
-    headers: {
-      "Content-Type": req.headers.get("Content-Type") || "multipart/form-data",
-      Accept: req.headers.get("Accept") || "application/json",
-      Authorization: `Bearer ${key}`,
-    },
-    method: req.method,
-    body: req.body,
-    // to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
-    redirect: "manual",
-    // @ts-ignore
-    duplex: "half",
-    signal: controller.signal,
-  };
-
-  try {
-    const res = await fetch(fetchUrl, fetchOptions);
-    // to prevent browser prompt for credentials
-    const newHeaders = new Headers(res.headers);
-    newHeaders.delete("www-authenticate");
-    // to disable nginx buffering
-    newHeaders.set("X-Accel-Buffering", "no");
-    return new Response(res.body, {
-      status: res.status,
-      statusText: res.statusText,
-      headers: newHeaders,
-    });
-  } finally {
-    clearTimeout(timeoutId);
-  }
-}

+ 17 - 50
app/client/api.ts

@@ -8,13 +8,10 @@ import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
 import { BigModelApi } from "./platforms/bigModel";
 import { DeepSeekApi } from "./platforms/deepSeek";
 import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
-import { GeminiProApi } from "./platforms/google";
-import { ClaudeApi } from "./platforms/anthropic";
 import { ErnieApi } from "./platforms/baidu";
 import { DoubaoApi } from "./platforms/bytedance";
 import { QwenApi } from "./platforms/alibaba";
 import { HunyuanApi } from "./platforms/tencent";
-import { MoonshotApi } from "./platforms/moonshot";
 import { SparkApi } from "./platforms/iflytek";
 
 export const ROLES = ["system", "user", "assistant"] as const;
@@ -122,12 +119,6 @@ export class ClientApi {
       case ModelProvider.DeepSeek:
         this.llm = new DeepSeekApi();
         break;
-      case ModelProvider.GeminiPro:
-        this.llm = new GeminiProApi();
-        break;
-      case ModelProvider.Claude:
-        this.llm = new ClaudeApi();
-        break;
       case ModelProvider.Ernie:
         this.llm = new ErnieApi();
         break;
@@ -140,9 +131,6 @@ export class ClientApi {
       case ModelProvider.Hunyuan:
         this.llm = new HunyuanApi();
         break;
-      case ModelProvider.Moonshot:
-        this.llm = new MoonshotApi();
-        break;
       case ModelProvider.Iflytek:
         this.llm = new SparkApi();
         break;
@@ -222,66 +210,51 @@ export function getHeaders() {
 
   function getConfig() {
     const modelConfig = chatStore.currentSession().mask.modelConfig;
-    const isGoogle = modelConfig.providerName == ServiceProvider.Google;
     const isAzure = modelConfig.providerName === ServiceProvider.Azure;
-    const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
     const isBaidu = modelConfig.providerName == ServiceProvider.Baidu;
     const isByteDance = modelConfig.providerName === ServiceProvider.ByteDance;
     const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba;
-    const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot;
     const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek;
     const isEnabledAccessControl = accessStore.enabledAccessControl();
-    const apiKey = isGoogle
-      ? accessStore.googleApiKey
-      : isAzure
-        ? accessStore.azureApiKey
-        : isAnthropic
-          ? accessStore.anthropicApiKey
-          : isByteDance
-            ? accessStore.bytedanceApiKey
-            : isAlibaba
-              ? accessStore.alibabaApiKey
-              : isMoonshot
-                ? accessStore.moonshotApiKey
-                : isIflytek
-                  ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
-                    ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
-                    : ""
-                  : accessStore.openaiApiKey;
+    const apiKey = isAzure
+      ? accessStore.azureApiKey
+      : isByteDance
+        ? accessStore.bytedanceApiKey
+        : isAlibaba
+          ? accessStore.alibabaApiKey
+          : isIflytek
+            ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
+              ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
+              : ""
+            : accessStore.openaiApiKey;
     return {
-      isGoogle,
       isAzure,
-      isAnthropic,
       isBaidu,
       isByteDance,
       isAlibaba,
-      isMoonshot,
       isIflytek,
       apiKey,
       isEnabledAccessControl,
     };
   }
 
-  function getAuthHeader(): string {
-    return isAzure ? "api-key" : isAnthropic ? "x-api-key" : "Authorization";
-  }
-
   const {
-    isGoogle,
     isAzure,
-    isAnthropic,
     isBaidu,
     apiKey,
     isEnabledAccessControl,
   } = getConfig();
-  // when using google api in app, not set auth header
-  if (isGoogle && clientConfig?.isApp) return headers;
+  
+  function getAuthHeader(): string {
+    return isAzure ? "api-key" : "Authorization";
+  }
+  
   // when using baidu api in app, not set auth header
   if (isBaidu && clientConfig?.isApp) return headers;
 
   const authHeader = getAuthHeader();
 
-  const bearerToken = getBearerToken(apiKey, isAzure || isAnthropic);
+  const bearerToken = getBearerToken(apiKey, isAzure);
 
   if (bearerToken) {
     headers[authHeader] = bearerToken;
@@ -300,10 +273,6 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
       return new ClientApi(ModelProvider.BigModel);
     case ServiceProvider.DeepSeek:
       return new ClientApi(ModelProvider.DeepSeek);
-    case ServiceProvider.Google:
-      return new ClientApi(ModelProvider.GeminiPro);
-    case ServiceProvider.Anthropic:
-      return new ClientApi(ModelProvider.Claude);
     case ServiceProvider.Baidu:
       return new ClientApi(ModelProvider.Ernie);
     case ServiceProvider.ByteDance:
@@ -312,8 +281,6 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
       return new ClientApi(ModelProvider.Qwen);
     case ServiceProvider.Tencent:
       return new ClientApi(ModelProvider.Hunyuan);
-    case ServiceProvider.Moonshot:
-      return new ClientApi(ModelProvider.Moonshot);
     case ServiceProvider.Iflytek:
       return new ClientApi(ModelProvider.Iflytek);
     default:

+ 0 - 397
app/client/platforms/anthropic.ts

@@ -1,397 +0,0 @@
-import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant";
-import { ChatOptions, getHeaders, LLMApi, MultimodalContent } from "../api";
-import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
-import { getClientConfig } from "@/app/config/client";
-import { DEFAULT_API_HOST } from "@/app/constant";
-import {
-  EventStreamContentType,
-  fetchEventSource,
-} from "@fortaine/fetch-event-source";
-
-import Locale from "../../locales";
-import { prettyObject } from "@/app/utils/format";
-import { getMessageTextContent, isVisionModel } from "@/app/utils";
-import { preProcessImageContent } from "@/app/utils/chat";
-import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
-
-export type MultiBlockContent = {
-  type: "image" | "text";
-  source?: {
-    type: string;
-    media_type: string;
-    data: string;
-  };
-  text?: string;
-};
-
-export type AnthropicMessage = {
-  role: (typeof ClaudeMapper)[keyof typeof ClaudeMapper];
-  content: string | MultiBlockContent[];
-};
-
-export interface AnthropicChatRequest {
-  model: string; // The model that will complete your prompt.
-  messages: AnthropicMessage[]; // The prompt that you want Claude to complete.
-  max_tokens: number; // The maximum number of tokens to generate before stopping.
-  stop_sequences?: string[]; // Sequences that will cause the model to stop generating completion text.
-  temperature?: number; // Amount of randomness injected into the response.
-  top_p?: number; // Use nucleus sampling.
-  top_k?: number; // Only sample from the top K options for each subsequent token.
-  metadata?: object; // An object describing metadata about the request.
-  stream?: boolean; // Whether to incrementally stream the response using server-sent events.
-}
-
-export interface ChatRequest {
-  model: string; // The model that will complete your prompt.
-  prompt: string; // The prompt that you want Claude to complete.
-  max_tokens_to_sample: number; // The maximum number of tokens to generate before stopping.
-  stop_sequences?: string[]; // Sequences that will cause the model to stop generating completion text.
-  temperature?: number; // Amount of randomness injected into the response.
-  top_p?: number; // Use nucleus sampling.
-  top_k?: number; // Only sample from the top K options for each subsequent token.
-  metadata?: object; // An object describing metadata about the request.
-  stream?: boolean; // Whether to incrementally stream the response using server-sent events.
-}
-
-export interface ChatResponse {
-  completion: string;
-  stop_reason: "stop_sequence" | "max_tokens";
-  model: string;
-}
-
-export type ChatStreamResponse = ChatResponse & {
-  stop?: string;
-  log_id: string;
-};
-
-const ClaudeMapper = {
-  assistant: "assistant",
-  user: "user",
-  system: "user",
-} as const;
-
-const keys = ["claude-2, claude-instant-1"];
-
-export class ClaudeApi implements LLMApi {
-  extractMessage(res: any) {
-    console.log("[Response] claude response: ", res);
-
-    return res?.content?.[0]?.text;
-  }
-  async chat(options: ChatOptions): Promise<void> {
-    const visionModel = isVisionModel(options.config.model);
-
-    const accessStore = useAccessStore.getState();
-
-    const shouldStream = !!options.config.stream;
-
-    const modelConfig = {
-      ...useAppConfig.getState().modelConfig,
-      ...useChatStore.getState().currentSession().mask.modelConfig,
-      ...{
-        model: options.config.model,
-      },
-    };
-
-    // try get base64image from local cache image_url
-    const messages: ChatOptions["messages"] = [];
-    for (const v of options.messages) {
-      const content = await preProcessImageContent(v.content);
-      messages.push({ role: v.role, content });
-    }
-
-    const keys = ["system", "user"];
-
-    // roles must alternate between "user" and "assistant" in claude, so add a fake assistant message between two user messages
-    for (let i = 0; i < messages.length - 1; i++) {
-      const message = messages[i];
-      const nextMessage = messages[i + 1];
-
-      if (keys.includes(message.role) && keys.includes(nextMessage.role)) {
-        messages[i] = [
-          message,
-          {
-            role: "assistant",
-            content: ";",
-          },
-        ] as any;
-      }
-    }
-
-    const prompt = messages
-      .flat()
-      .filter((v) => {
-        if (!v.content) return false;
-        if (typeof v.content === "string" && !v.content.trim()) return false;
-        return true;
-      })
-      .map((v) => {
-        const { role, content } = v;
-        const insideRole = ClaudeMapper[role] ?? "user";
-
-        if (!visionModel || typeof content === "string") {
-          return {
-            role: insideRole,
-            content: getMessageTextContent(v),
-          };
-        }
-        return {
-          role: insideRole,
-          content: content
-            .filter((v) => v.image_url || v.text)
-            .map(({ type, text, image_url }) => {
-              if (type === "text") {
-                return {
-                  type,
-                  text: text!,
-                };
-              }
-              const { url = "" } = image_url || {};
-              const colonIndex = url.indexOf(":");
-              const semicolonIndex = url.indexOf(";");
-              const comma = url.indexOf(",");
-
-              const mimeType = url.slice(colonIndex + 1, semicolonIndex);
-              const encodeType = url.slice(semicolonIndex + 1, comma);
-              const data = url.slice(comma + 1);
-
-              return {
-                type: "image" as const,
-                source: {
-                  type: encodeType,
-                  media_type: mimeType,
-                  data,
-                },
-              };
-            }),
-        };
-      });
-
-    if (prompt[0]?.role === "assistant") {
-      prompt.unshift({
-        role: "user",
-        content: ";",
-      });
-    }
-
-    const requestBody: AnthropicChatRequest = {
-      messages: prompt,
-      stream: shouldStream,
-
-      model: modelConfig.model,
-      max_tokens: modelConfig.max_tokens,
-      temperature: modelConfig.temperature,
-      top_p: modelConfig.top_p,
-      // top_k: modelConfig.top_k,
-      top_k: 5,
-    };
-
-    const path = this.path(Anthropic.ChatPath);
-
-    const controller = new AbortController();
-    options.onController?.(controller);
-
-    const payload = {
-      method: "POST",
-      body: JSON.stringify(requestBody),
-      signal: controller.signal,
-      headers: {
-        ...getHeaders(), // get common headers
-        "anthropic-version": accessStore.anthropicApiVersion,
-        // do not send `anthropicApiKey` in browser!!!
-        // Authorization: getAuthKey(accessStore.anthropicApiKey),
-      },
-    };
-
-    if (shouldStream) {
-      try {
-        const context = {
-          text: "",
-          finished: false,
-        };
-
-        const finish = () => {
-          if (!context.finished) {
-            options.onFinish(context.text);
-            context.finished = true;
-          }
-        };
-
-        controller.signal.onabort = finish;
-        fetchEventSource(path, {
-          ...payload,
-          async onopen(res) {
-            const contentType = res.headers.get("content-type");
-            console.log("response content type: ", contentType);
-
-            if (contentType?.startsWith("text/plain")) {
-              context.text = await res.clone().text();
-              return finish();
-            }
-
-            if (
-              !res.ok ||
-              !res.headers
-                .get("content-type")
-                ?.startsWith(EventStreamContentType) ||
-              res.status !== 200
-            ) {
-              const responseTexts = [context.text];
-              let extraInfo = await res.clone().text();
-              try {
-                const resJson = await res.clone().json();
-                extraInfo = prettyObject(resJson);
-              } catch {}
-
-              if (res.status === 401) {
-                responseTexts.push(Locale.Error.Unauthorized);
-              }
-
-              if (extraInfo) {
-                responseTexts.push(extraInfo);
-              }
-
-              context.text = responseTexts.join("\n\n");
-
-              return finish();
-            }
-          },
-          onmessage(msg) {
-            let chunkJson:
-              | undefined
-              | {
-                  type: "content_block_delta" | "content_block_stop";
-                  delta?: {
-                    type: "text_delta";
-                    text: string;
-                  };
-                  index: number;
-                };
-            try {
-              chunkJson = JSON.parse(msg.data);
-            } catch (e) {
-              console.error("[Response] parse error", msg.data);
-            }
-
-            if (!chunkJson || chunkJson.type === "content_block_stop") {
-              return finish();
-            }
-
-            const { delta } = chunkJson;
-            if (delta?.text) {
-              context.text += delta.text;
-              options.onUpdate?.(context.text, delta.text);
-            }
-          },
-          onclose() {
-            finish();
-          },
-          onerror(e) {
-            options.onError?.(e);
-            throw e;
-          },
-          openWhenHidden: true,
-        });
-      } catch (e) {
-        console.error("failed to chat", e);
-        options.onError?.(e as Error);
-      }
-    } else {
-      try {
-        controller.signal.onabort = () => options.onFinish("");
-
-        const res = await fetch(path, payload);
-        const resJson = await res.json();
-
-        const message = this.extractMessage(resJson);
-        options.onFinish(message);
-      } catch (e) {
-        console.error("failed to chat", e);
-        options.onError?.(e as Error);
-      }
-    }
-  }
-  async usage() {
-    return {
-      used: 0,
-      total: 0,
-    };
-  }
-  async models() {
-    // const provider = {
-    //   id: "anthropic",
-    //   providerName: "Anthropic",
-    //   providerType: "anthropic",
-    // };
-
-    return [
-      // {
-      //   name: "claude-instant-1.2",
-      //   available: true,
-      //   provider,
-      // },
-      // {
-      //   name: "claude-2.0",
-      //   available: true,
-      //   provider,
-      // },
-      // {
-      //   name: "claude-2.1",
-      //   available: true,
-      //   provider,
-      // },
-      // {
-      //   name: "claude-3-opus-20240229",
-      //   available: true,
-      //   provider,
-      // },
-      // {
-      //   name: "claude-3-sonnet-20240229",
-      //   available: true,
-      //   provider,
-      // },
-      // {
-      //   name: "claude-3-haiku-20240307",
-      //   available: true,
-      //   provider,
-      // },
-    ];
-  }
-  path(path: string): string {
-    const accessStore = useAccessStore.getState();
-
-    let baseUrl: string = "";
-
-    if (accessStore.useCustomConfig) {
-      baseUrl = accessStore.anthropicUrl;
-    }
-
-    // if endpoint is empty, use default endpoint
-    if (baseUrl.trim().length === 0) {
-      const isApp = !!getClientConfig()?.isApp;
-
-      baseUrl = isApp
-        ? DEFAULT_API_HOST + "/api/proxy/anthropic"
-        : ApiPath.Anthropic;
-    }
-
-    if (!baseUrl.startsWith("http") && !baseUrl.startsWith("/api")) {
-      baseUrl = "https://" + baseUrl;
-    }
-
-    baseUrl = trimEnd(baseUrl, "/");
-
-    // try rebuild url, when using cloudflare ai gateway in client
-    return cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
-  }
-}
-
-function trimEnd(s: string, end = " ") {
-  if (end.length === 0) return s;
-
-  while (s.endsWith(end)) {
-    s = s.slice(0, -end.length);
-  }
-
-  return s;
-}

+ 0 - 308
app/client/platforms/google.ts

@@ -1,308 +0,0 @@
-import { ApiPath, Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
-import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
-import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
-import { getClientConfig } from "@/app/config/client";
-import { DEFAULT_API_HOST } from "@/app/constant";
-import Locale from "../../locales";
-import {
-  EventStreamContentType,
-  fetchEventSource,
-} from "@fortaine/fetch-event-source";
-import { prettyObject } from "@/app/utils/format";
-import {
-  getMessageTextContent,
-  getMessageImages,
-  isVisionModel,
-} from "@/app/utils";
-import { preProcessImageContent } from "@/app/utils/chat";
-
-export class GeminiProApi implements LLMApi {
-  path(path: string): string {
-    const accessStore = useAccessStore.getState();
-
-    let baseUrl = "";
-    if (accessStore.useCustomConfig) {
-      baseUrl = accessStore.googleUrl;
-    }
-
-    const isApp = !!getClientConfig()?.isApp;
-    if (baseUrl.length === 0) {
-      baseUrl = isApp ? DEFAULT_API_HOST + `/api/proxy/google` : ApiPath.Google;
-    }
-    if (baseUrl.endsWith("/")) {
-      baseUrl = baseUrl.slice(0, baseUrl.length - 1);
-    }
-    if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.Google)) {
-      baseUrl = "https://" + baseUrl;
-    }
-
-    console.log("[Proxy Endpoint] ", baseUrl, path);
-
-    let chatPath = [baseUrl, path].join("/");
-
-    chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
-    // if chatPath.startsWith('http') then add key in query string
-    if (chatPath.startsWith("http") && accessStore.googleApiKey) {
-      chatPath += `&key=${accessStore.googleApiKey}`;
-    }
-    return chatPath;
-  }
-  extractMessage(res: any) {
-    console.log("[Response] gemini-pro response: ", res);
-
-    return (
-      res?.candidates?.at(0)?.content?.parts.at(0)?.text ||
-      res?.error?.message ||
-      ""
-    );
-  }
-  async chat(options: ChatOptions): Promise<void> {
-    const apiClient = this;
-    let multimodal = false;
-
-    // try get base64image from local cache image_url
-    const _messages: ChatOptions["messages"] = [];
-    for (const v of options.messages) {
-      const content = await preProcessImageContent(v.content);
-      _messages.push({ role: v.role, content });
-    }
-    const messages = _messages.map((v) => {
-      let parts: any[] = [{ text: getMessageTextContent(v) }];
-      if (isVisionModel(options.config.model)) {
-        const images = getMessageImages(v);
-        if (images.length > 0) {
-          multimodal = true;
-          parts = parts.concat(
-            images.map((image) => {
-              const imageType = image.split(";")[0].split(":")[1];
-              const imageData = image.split(",")[1];
-              return {
-                inline_data: {
-                  mime_type: imageType,
-                  data: imageData,
-                },
-              };
-            }),
-          );
-        }
-      }
-      return {
-        role: v.role.replace("assistant", "model").replace("system", "user"),
-        parts: parts,
-      };
-    });
-
-    // google requires that role in neighboring messages must not be the same
-    for (let i = 0; i < messages.length - 1; ) {
-      // Check if current and next item both have the role "model"
-      if (messages[i].role === messages[i + 1].role) {
-        // Concatenate the 'parts' of the current and next item
-        messages[i].parts = messages[i].parts.concat(messages[i + 1].parts);
-        // Remove the next item
-        messages.splice(i + 1, 1);
-      } else {
-        // Move to the next item
-        i++;
-      }
-    }
-    // if (visionModel && messages.length > 1) {
-    //   options.onError?.(new Error("Multiturn chat is not enabled for models/gemini-pro-vision"));
-    // }
-
-    const accessStore = useAccessStore.getState();
-
-    const modelConfig = {
-      ...useAppConfig.getState().modelConfig,
-      ...useChatStore.getState().currentSession().mask.modelConfig,
-      ...{
-        model: options.config.model,
-      },
-    };
-    const requestPayload = {
-      contents: messages,
-      generationConfig: {
-        // stopSequences: [
-        //   "Title"
-        // ],
-        temperature: modelConfig.temperature,
-        maxOutputTokens: modelConfig.max_tokens,
-        topP: modelConfig.top_p,
-        // "topK": modelConfig.top_k,
-      },
-      safetySettings: [
-        {
-          category: "HARM_CATEGORY_HARASSMENT",
-          threshold: accessStore.googleSafetySettings,
-        },
-        {
-          category: "HARM_CATEGORY_HATE_SPEECH",
-          threshold: accessStore.googleSafetySettings,
-        },
-        {
-          category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
-          threshold: accessStore.googleSafetySettings,
-        },
-        {
-          category: "HARM_CATEGORY_DANGEROUS_CONTENT",
-          threshold: accessStore.googleSafetySettings,
-        },
-      ],
-    };
-
-    let shouldStream = !!options.config.stream;
-    const controller = new AbortController();
-    options.onController?.(controller);
-    try {
-      // https://github.com/google-gemini/cookbook/blob/main/quickstarts/rest/Streaming_REST.ipynb
-      const chatPath = this.path(Google.ChatPath(modelConfig.model));
-
-      const chatPayload = {
-        method: "POST",
-        body: JSON.stringify(requestPayload),
-        signal: controller.signal,
-        headers: getHeaders(),
-      };
-
-      // make a fetch request
-      const requestTimeoutId = setTimeout(
-        () => controller.abort(),
-        REQUEST_TIMEOUT_MS,
-      );
-
-      if (shouldStream) {
-        let responseText = "";
-        let remainText = "";
-        let finished = false;
-
-        const finish = () => {
-          if (!finished) {
-            finished = true;
-            options.onFinish(responseText + remainText);
-          }
-        };
-
-        // animate response to make it looks smooth
-        function animateResponseText() {
-          if (finished || controller.signal.aborted) {
-            responseText += remainText;
-            finish();
-            return;
-          }
-
-          if (remainText.length > 0) {
-            const fetchCount = Math.max(1, Math.round(remainText.length / 60));
-            const fetchText = remainText.slice(0, fetchCount);
-            responseText += fetchText;
-            remainText = remainText.slice(fetchCount);
-            options.onUpdate?.(responseText, fetchText);
-          }
-
-          requestAnimationFrame(animateResponseText);
-        }
-
-        // start animaion
-        animateResponseText();
-
-        controller.signal.onabort = finish;
-
-        fetchEventSource(chatPath, {
-          ...chatPayload,
-          async onopen(res) {
-            clearTimeout(requestTimeoutId);
-            const contentType = res.headers.get("content-type");
-            console.log(
-              "[Gemini] request response content type: ",
-              contentType,
-            );
-
-            if (contentType?.startsWith("text/plain")) {
-              responseText = await res.clone().text();
-              return finish();
-            }
-
-            if (
-              !res.ok ||
-              !res.headers
-                .get("content-type")
-                ?.startsWith(EventStreamContentType) ||
-              res.status !== 200
-            ) {
-              const responseTexts = [responseText];
-              let extraInfo = await res.clone().text();
-              try {
-                const resJson = await res.clone().json();
-                extraInfo = prettyObject(resJson);
-              } catch {}
-
-              if (res.status === 401) {
-                responseTexts.push(Locale.Error.Unauthorized);
-              }
-
-              if (extraInfo) {
-                responseTexts.push(extraInfo);
-              }
-
-              responseText = responseTexts.join("\n\n");
-
-              return finish();
-            }
-          },
-          onmessage(msg) {
-            if (msg.data === "[DONE]" || finished) {
-              return finish();
-            }
-            const text = msg.data;
-            try {
-              const json = JSON.parse(text);
-              const delta = apiClient.extractMessage(json);
-
-              if (delta) {
-                remainText += delta;
-              }
-
-              const blockReason = json?.promptFeedback?.blockReason;
-              if (blockReason) {
-                // being blocked
-                console.log(`[Google] [Safety Ratings] result:`, blockReason);
-              }
-            } catch (e) {
-              console.error("[Request] parse error", text, msg);
-            }
-          },
-          onclose() {
-            finish();
-          },
-          onerror(e) {
-            options.onError?.(e);
-            throw e;
-          },
-          openWhenHidden: true,
-        });
-      } else {
-        const res = await fetch(chatPath, chatPayload);
-        clearTimeout(requestTimeoutId);
-        const resJson = await res.json();
-        if (resJson?.promptFeedback?.blockReason) {
-          // being blocked
-          options.onError?.(
-            new Error(
-              "Message is being blocked for reason: " +
-                resJson.promptFeedback.blockReason,
-            ),
-          );
-        }
-        const message = apiClient.extractMessage(resJson);
-        options.onFinish(message);
-      }
-    } catch (e) {
-      console.log("[Request] failed to make a chat request", e);
-      options.onError?.(e as Error);
-    }
-  }
-  usage(): Promise<LLMUsage> {
-    throw new Error("Method not implemented.");
-  }
-  async models(): Promise<LLMModel[]> {
-    return [];
-  }
-}

+ 0 - 251
app/client/platforms/moonshot.ts

@@ -1,251 +0,0 @@
-"use client";
-// azure and openai, using same models. so using same LLMApi.
-import {
-  ApiPath,
-  DEFAULT_API_HOST,
-  DEFAULT_MODELS,
-  Moonshot,
-  REQUEST_TIMEOUT_MS,
-  ServiceProvider,
-} from "@/app/constant";
-import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
-import { collectModelsWithDefaultModel } from "@/app/utils/model";
-import { preProcessImageContent } from "@/app/utils/chat";
-import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
-
-import {
-  ChatOptions,
-  getHeaders,
-  LLMApi,
-  LLMModel,
-  LLMUsage,
-  MultimodalContent,
-} from "../api";
-import Locale from "../../locales";
-import {
-  EventStreamContentType,
-  fetchEventSource,
-} from "@fortaine/fetch-event-source";
-import { prettyObject } from "@/app/utils/format";
-import { getClientConfig } from "@/app/config/client";
-import { getMessageTextContent } from "@/app/utils";
-
-import { OpenAIListModelResponse, RequestPayload } from "./openai";
-
-export class MoonshotApi implements LLMApi {
-  private disableListModels = true;
-
-  path(path: string): string {
-    const accessStore = useAccessStore.getState();
-
-    let baseUrl = "";
-
-    if (accessStore.useCustomConfig) {
-      baseUrl = accessStore.moonshotUrl;
-    }
-
-    if (baseUrl.length === 0) {
-      const isApp = !!getClientConfig()?.isApp;
-      const apiPath = ApiPath.Moonshot;
-      baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
-    }
-
-    if (baseUrl.endsWith("/")) {
-      baseUrl = baseUrl.slice(0, baseUrl.length - 1);
-    }
-    if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.Moonshot)) {
-      baseUrl = "https://" + baseUrl;
-    }
-
-    console.log("[Proxy Endpoint] ", baseUrl, path);
-
-    return [baseUrl, path].join("/");
-  }
-
-  extractMessage(res: any) {
-    return res.choices?.at(0)?.message?.content ?? "";
-  }
-
-  async chat(options: ChatOptions) {
-    const messages: ChatOptions["messages"] = [];
-    for (const v of options.messages) {
-      const content = getMessageTextContent(v);
-      messages.push({ role: v.role, content });
-    }
-
-    const modelConfig = {
-      ...useAppConfig.getState().modelConfig,
-      ...useChatStore.getState().currentSession().mask.modelConfig,
-      ...{
-        model: options.config.model,
-        providerName: options.config.providerName,
-      },
-    };
-
-    const requestPayload: RequestPayload = {
-      messages,
-      stream: options.config.stream,
-      model: modelConfig.model,
-      temperature: modelConfig.temperature,
-      presence_penalty: modelConfig.presence_penalty,
-      frequency_penalty: modelConfig.frequency_penalty,
-      top_p: modelConfig.top_p,
-      // max_tokens: Math.max(modelConfig.max_tokens, 1024),
-      // Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
-    };
-
-    console.log("[Request] openai payload: ", requestPayload);
-
-    const shouldStream = !!options.config.stream;
-    const controller = new AbortController();
-    options.onController?.(controller);
-
-    try {
-      const chatPath = this.path(Moonshot.ChatPath);
-      const chatPayload = {
-        method: "POST",
-        body: JSON.stringify(requestPayload),
-        signal: controller.signal,
-        headers: getHeaders(),
-      };
-
-      // make a fetch request
-      const requestTimeoutId = setTimeout(
-        () => controller.abort(),
-        REQUEST_TIMEOUT_MS,
-      );
-
-      if (shouldStream) {
-        let responseText = "";
-        let remainText = "";
-        let finished = false;
-
-        // animate response to make it looks smooth
-        function animateResponseText() {
-          if (finished || controller.signal.aborted) {
-            responseText += remainText;
-            console.log("[Response Animation] finished");
-            if (responseText?.length === 0) {
-              options.onError?.(new Error("empty response from server"));
-            }
-            return;
-          }
-
-          if (remainText.length > 0) {
-            const fetchCount = Math.max(1, Math.round(remainText.length / 60));
-            const fetchText = remainText.slice(0, fetchCount);
-            responseText += fetchText;
-            remainText = remainText.slice(fetchCount);
-            options.onUpdate?.(responseText, fetchText);
-          }
-
-          requestAnimationFrame(animateResponseText);
-        }
-
-        // start animaion
-        animateResponseText();
-
-        const finish = () => {
-          if (!finished) {
-            finished = true;
-            options.onFinish(responseText + remainText);
-          }
-        };
-
-        controller.signal.onabort = finish;
-
-        fetchEventSource(chatPath, {
-          ...chatPayload,
-          async onopen(res) {
-            clearTimeout(requestTimeoutId);
-            const contentType = res.headers.get("content-type");
-            console.log(
-              "[OpenAI] request response content type: ",
-              contentType,
-            );
-
-            if (contentType?.startsWith("text/plain")) {
-              responseText = await res.clone().text();
-              return finish();
-            }
-
-            if (
-              !res.ok ||
-              !res.headers
-                .get("content-type")
-                ?.startsWith(EventStreamContentType) ||
-              res.status !== 200
-            ) {
-              const responseTexts = [responseText];
-              let extraInfo = await res.clone().text();
-              try {
-                const resJson = await res.clone().json();
-                extraInfo = prettyObject(resJson);
-              } catch {}
-
-              if (res.status === 401) {
-                responseTexts.push(Locale.Error.Unauthorized);
-              }
-
-              if (extraInfo) {
-                responseTexts.push(extraInfo);
-              }
-
-              responseText = responseTexts.join("\n\n");
-
-              return finish();
-            }
-          },
-          onmessage(msg) {
-            if (msg.data === "[DONE]" || finished) {
-              return finish();
-            }
-            const text = msg.data;
-            try {
-              const json = JSON.parse(text);
-              const choices = json.choices as Array<{
-                delta: { content: string };
-              }>;
-              const delta = choices[0]?.delta?.content;
-              const textmoderation = json?.prompt_filter_results;
-
-              if (delta) {
-                remainText += delta;
-              }
-            } catch (e) {
-              console.error("[Request] parse error", text, msg);
-            }
-          },
-          onclose() {
-            finish();
-          },
-          onerror(e) {
-            options.onError?.(e);
-            throw e;
-          },
-          openWhenHidden: true,
-        });
-      } else {
-        const res = await fetch(chatPath, chatPayload);
-        clearTimeout(requestTimeoutId);
-
-        const resJson = await res.json();
-        const message = this.extractMessage(resJson);
-        options.onFinish(message);
-      }
-    } catch (e) {
-      console.log("[Request] failed to make a chat request", e);
-      options.onError?.(e as Error);
-    }
-  }
-  async usage() {
-    return {
-      used: 0,
-      total: 0,
-    };
-  }
-
-  async models(): Promise<LLMModel[]> {
-    return [];
-  }
-}

+ 1 - 1
app/components/DeepSeekChat.tsx

@@ -867,7 +867,7 @@ function _Chat() {
   // chat commands shortcuts
   const chatCommands = useChatCommand({
     new: () => chatStore.newSession(),
-    newm: () => navigate(Path.NewChat),
+    // newm: () => navigate(Path.MaskChat),  // 关闭mask入口 ,后续有需求再二开
     prev: () => chatStore.nextSession(-1),
     next: () => chatStore.nextSession(1),
     clear: () =>

+ 1 - 1
app/components/DeepSeekHomeChat.tsx

@@ -829,7 +829,7 @@ function _Chat() {
   // chat commands shortcuts
   const chatCommands = useChatCommand({
     new: () => chatStore.newSession(),
-    newm: () => navigate(Path.NewChat),
+    // newm: () => navigate(Path.MaskChat),  // 关闭mask入口 ,后续有需求再二开
     prev: () => chatStore.nextSession(-1),
     next: () => chatStore.nextSession(1),
     clear: () =>

+ 1 - 11
app/components/auth.tsx

@@ -64,17 +64,7 @@ export function AuthPage() {
               );
             }}
           />
-          <input
-            className={styles["auth-input"]}
-            type="password"
-            placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
-            value={accessStore.googleApiKey}
-            onChange={(e) => {
-              accessStore.update(
-                (access) => (access.googleApiKey = e.currentTarget.value),
-              );
-            }}
-          />
+
         </>
       ) : null}
 

+ 1 - 1
app/components/chat.tsx

@@ -1153,7 +1153,7 @@ function _Chat() {
   // chat commands shortcuts
   const chatCommands = useChatCommand({
     new: () => chatStore.newSession(),
-    newm: () => navigate(Path.NewChat),
+    newm: () => navigate(Path.MaskChat),
     prev: () => chatStore.nextSession(-1),
     next: () => chatStore.nextSession(1),
     clear: () =>

+ 6 - 2
app/components/home.tsx

@@ -143,10 +143,10 @@ const HomeApp = dynamic(
   }
 );
 
-const NewChat = dynamic(
+const MaskChat = dynamic(
   async () => {
     await delayer();
-    return (await import("./new-chat")).NewChat
+    return (await import("./mask-chat")).MaskChat
   },
   {
     loading: () => <Loading />,
@@ -275,6 +275,10 @@ function Screen() {
             <Route path='/newChat' element={<Chat />} />
             <Route path='/deepseekChat' element={<DeepSeekChat />} />
             <Route path='/newDeepseekChat' element={<DeepSeekChat />} />
+            {/* 关闭以下入口  后续有需求再开启*/}
+            {/* <Route path='/settings' element={<Settings />} /> */}
+            {/* <Route path='/mask-chat' element={<MaskChat />} /> */}
+            {/* <Route path='/masks' element={<MaskPage />} /> */}
           </Routes>
         </WindowContent>
       </>

+ 2 - 2
app/components/new-chat.module.scss → app/components/mask-chat.module.scss

@@ -1,6 +1,6 @@
 @import "../styles/animation.scss";
 
-.new-chat {
+.mask-chat {
   height: 100%;
   width: 100%;
   display: flex;
@@ -98,6 +98,7 @@
       .mask {
         display: flex;
         align-items: center;
+        justify-content: center;
         padding: 10px 14px;
         border: var(--border-in-light);
         box-shadow: var(--card-shadow);
@@ -116,7 +117,6 @@
         }
 
         .mask-name {
-          margin-left: 10px;
           font-size: 14px;
         }
       }

+ 5 - 9
app/components/new-chat.tsx → app/components/mask-chat.tsx

@@ -15,7 +15,7 @@ function EmojiAvatar(props: { avatar: string; size?: number }) {
   }
   return <BotIcon className="user-avatar" />;
 }
-import styles from "./new-chat.module.scss";
+import styles from "./mask-chat.module.scss";
 
 import LeftIcon from "../icons/left.svg";
 import LightningIcon from "../icons/lightning.svg";
@@ -25,7 +25,7 @@ import { useLocation, useNavigate } from "react-router-dom";
 import { Mask, useMaskStore } from "../store/mask";
 import Locale from "../locales";
 import { useAppConfig, useChatStore } from "../store";
-import { MaskAvatar } from "./mask";
+
 import { useCommand } from "../command";
 import { showConfirm } from "./ui-lib";
 import { BUILTIN_MASK_STORE } from "../masks";
@@ -33,10 +33,6 @@ import { BUILTIN_MASK_STORE } from "../masks";
 function MaskItem(props: { mask: Mask; onClick?: () => void }) {
   return (
     <div className={styles["mask"]} onClick={props.onClick}>
-      <MaskAvatar
-        avatar={props.mask.avatar}
-        model={props.mask.modelConfig.model}
-      />
       <div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
     </div>
   );
@@ -84,7 +80,7 @@ function useMaskGroup(masks: Mask[]) {
   return groups;
 }
 
-export function NewChat() {
+export function MaskChat() {
   const chatStore = useChatStore();
   const maskStore = useMaskStore();
 
@@ -111,7 +107,7 @@ export function NewChat() {
         const mask = maskStore.get(id) ?? BUILTIN_MASK_STORE.get(id);
         startChat(mask ?? undefined);
       } catch {
-        console.error("[New Chat] failed to create chat from mask id=", id);
+        console.error("[Mask Chat] failed to create chat from mask id=", id);
       }
     },
   });
@@ -124,7 +120,7 @@ export function NewChat() {
   }, [groups]);
 
   return (
-    <div className={styles["new-chat"]}>
+    <div className={styles["mask-chat"]}>
       <div className={styles["mask-header"]}>
         <IconButton
           icon={<LeftIcon />}

+ 1 - 1
app/components/mask.tsx

@@ -144,7 +144,7 @@ export function MaskConfig(props: {
   };
 
   const copyMaskLink = () => {
-    const maskLink = `${location.protocol}//${location.host}/#${Path.NewChat}?mask=${props.mask.id}`;
+    const maskLink = `${location.protocol}//${location.host}/#${Path.MaskChat}?mask=${props.mask.id}`;
     copyToClipboard(maskLink);
   };
 

+ 2 - 4
app/components/model-config.tsx

@@ -97,8 +97,7 @@ export function ModelConfigList(props: {
         ></input>
       </ListItem>
 
-      {props.modelConfig?.providerName == ServiceProvider.Google ? null : (
-        <>
+
           <ListItem
             title={Locale.Settings.PresencePenalty.Title}
             subTitle={Locale.Settings.PresencePenalty.SubTitle}
@@ -176,8 +175,7 @@ export function ModelConfigList(props: {
               }
             ></input>
           </ListItem>
-        </>
-      )}
+
       <ListItem
         title={Locale.Settings.HistoryCount.Title}
         subTitle={Locale.Settings.HistoryCount.SubTitle}

+ 8 - 312
app/components/settings.tsx

@@ -37,7 +37,6 @@ import {
   SubmitKey,
   useChatStore,
   Theme,
-  useUpdateStore,
   useAccessStore,
   useAppConfig,
 } from "../store";
@@ -51,23 +50,16 @@ import Locale, {
 import { copyToClipboard } from "../utils";
 import Link from "next/link";
 import {
-  Anthropic,
   Azure,
   Baidu,
   Tencent,
   ByteDance,
   Alibaba,
-  Moonshot,
-  Google,
-  GoogleSafetySettingsThreshold,
   OPENAI_BASE_URL,
   Path,
-  RELEASE_URL,
   STORAGE_KEY,
   ServiceProvider,
   SlotID,
-  UPDATE_URL,
-  Stability,
   Iflytek,
 } from "../constant";
 import { Prompt, SearchService, usePromptStore } from "../store/prompt";
@@ -627,22 +619,7 @@ export function Settings() {
   const config = useAppConfig();
   const updateConfig = config.update;
 
-  const updateStore = useUpdateStore();
-  const [checkingUpdate, setCheckingUpdate] = useState(false);
-  const currentVersion = updateStore.formatVersion(updateStore.version);
-  const remoteId = updateStore.formatVersion(updateStore.remoteVersion);
-  const hasNewVersion = currentVersion !== remoteId;
-  const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL;
-
-  function checkUpdate(force = false) {
-    setCheckingUpdate(true);
-    updateStore.getLatestVersion(force).then(() => {
-      setCheckingUpdate(false);
-    });
-
-    console.log("[Update] local version ", updateStore.version);
-    console.log("[Update] remote version ", updateStore.remoteVersion);
-  }
+
 
   const accessStore = useAccessStore();
   const shouldHideBalanceQuery = useMemo(() => {
@@ -659,21 +636,7 @@ export function Settings() {
     accessStore.provider,
   ]);
 
-  const usage = {
-    used: updateStore.used,
-    subscription: updateStore.subscription,
-  };
-  const [loadingUsage, setLoadingUsage] = useState(false);
-  function checkUsage(force = false) {
-    if (shouldHideBalanceQuery) {
-      return;
-    }
 
-    setLoadingUsage(true);
-    updateStore.updateUsage(force).finally(() => {
-      setLoadingUsage(false);
-    });
-  }
 
   const enabledAccessControl = useMemo(
     () => accessStore.enabledAccessControl(),
@@ -687,13 +650,7 @@ export function Settings() {
   const customCount = promptStore.getUserPrompts().length ?? 0;
   const [shouldShowPromptModal, setShowPromptModal] = useState(false);
 
-  const showUsage = accessStore.isAuthorized();
-  useEffect(() => {
-    // checks per minutes
-    checkUpdate();
-    showUsage && checkUsage();
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
+
 
   useEffect(() => {
     const keydownEvent = (e: KeyboardEvent) => {
@@ -849,141 +806,9 @@ export function Settings() {
     </>
   );
 
-  const googleConfigComponent = accessStore.provider ===
-    ServiceProvider.Google && (
-    <>
-      <ListItem
-        title={Locale.Settings.Access.Google.Endpoint.Title}
-        subTitle={
-          Locale.Settings.Access.Google.Endpoint.SubTitle +
-          Google.ExampleEndpoint
-        }
-      >
-        <input
-          aria-label={Locale.Settings.Access.Google.Endpoint.Title}
-          type="text"
-          value={accessStore.googleUrl}
-          placeholder={Google.ExampleEndpoint}
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.googleUrl = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Google.ApiKey.Title}
-        subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
-      >
-        <PasswordInput
-          aria-label={Locale.Settings.Access.Google.ApiKey.Title}
-          value={accessStore.googleApiKey}
-          type="text"
-          placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
-          onChange={(e) => {
-            accessStore.update(
-              (access) => (access.googleApiKey = e.currentTarget.value),
-            );
-          }}
-        />
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Google.ApiVersion.Title}
-        subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle}
-      >
-        <input
-          aria-label={Locale.Settings.Access.Google.ApiVersion.Title}
-          type="text"
-          value={accessStore.googleApiVersion}
-          placeholder="2023-08-01-preview"
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.googleApiVersion = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Google.GoogleSafetySettings.Title}
-        subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle}
-      >
-        <Select
-          aria-label={Locale.Settings.Access.Google.GoogleSafetySettings.Title}
-          value={accessStore.googleSafetySettings}
-          onChange={(e) => {
-            accessStore.update(
-              (access) =>
-                (access.googleSafetySettings = e.target
-                  .value as GoogleSafetySettingsThreshold),
-            );
-          }}
-        >
-          {Object.entries(GoogleSafetySettingsThreshold).map(([k, v]) => (
-            <option value={v} key={k}>
-              {k}
-            </option>
-          ))}
-        </Select>
-      </ListItem>
-    </>
-  );
 
-  const anthropicConfigComponent = accessStore.provider ===
-    ServiceProvider.Anthropic && (
-    <>
-      <ListItem
-        title={Locale.Settings.Access.Anthropic.Endpoint.Title}
-        subTitle={
-          Locale.Settings.Access.Anthropic.Endpoint.SubTitle +
-          Anthropic.ExampleEndpoint
-        }
-      >
-        <input
-          aria-label={Locale.Settings.Access.Anthropic.Endpoint.Title}
-          type="text"
-          value={accessStore.anthropicUrl}
-          placeholder={Anthropic.ExampleEndpoint}
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.anthropicUrl = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Anthropic.ApiKey.Title}
-        subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle}
-      >
-        <PasswordInput
-          aria-label={Locale.Settings.Access.Anthropic.ApiKey.Title}
-          value={accessStore.anthropicApiKey}
-          type="text"
-          placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder}
-          onChange={(e) => {
-            accessStore.update(
-              (access) => (access.anthropicApiKey = e.currentTarget.value),
-            );
-          }}
-        />
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Anthropic.ApiVerion.Title}
-        subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle}
-      >
-        <input
-          aria-label={Locale.Settings.Access.Anthropic.ApiVerion.Title}
-          type="text"
-          value={accessStore.anthropicApiVersion}
-          placeholder={Anthropic.Vision}
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.anthropicApiVersion = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-    </>
-  );
+
+  const anthropicConfigComponent = null;
 
   const baiduConfigComponent = accessStore.provider ===
     ServiceProvider.Baidu && (
@@ -1175,87 +1000,9 @@ export function Settings() {
     </>
   );
 
-  const moonshotConfigComponent = accessStore.provider ===
-    ServiceProvider.Moonshot && (
-    <>
-      <ListItem
-        title={Locale.Settings.Access.Moonshot.Endpoint.Title}
-        subTitle={
-          Locale.Settings.Access.Moonshot.Endpoint.SubTitle +
-          Moonshot.ExampleEndpoint
-        }
-      >
-        <input
-          aria-label={Locale.Settings.Access.Moonshot.Endpoint.Title}
-          type="text"
-          value={accessStore.moonshotUrl}
-          placeholder={Moonshot.ExampleEndpoint}
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.moonshotUrl = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Moonshot.ApiKey.Title}
-        subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle}
-      >
-        <PasswordInput
-          aria-label={Locale.Settings.Access.Moonshot.ApiKey.Title}
-          value={accessStore.moonshotApiKey}
-          type="text"
-          placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder}
-          onChange={(e) => {
-            accessStore.update(
-              (access) => (access.moonshotApiKey = e.currentTarget.value),
-            );
-          }}
-        />
-      </ListItem>
-    </>
-  );
+  const moonshotConfigComponent = null;
 
-  const stabilityConfigComponent = accessStore.provider ===
-    ServiceProvider.Stability && (
-    <>
-      <ListItem
-        title={Locale.Settings.Access.Stability.Endpoint.Title}
-        subTitle={
-          Locale.Settings.Access.Stability.Endpoint.SubTitle +
-          Stability.ExampleEndpoint
-        }
-      >
-        <input
-          aria-label={Locale.Settings.Access.Stability.Endpoint.Title}
-          type="text"
-          value={accessStore.stabilityUrl}
-          placeholder={Stability.ExampleEndpoint}
-          onChange={(e) =>
-            accessStore.update(
-              (access) => (access.stabilityUrl = e.currentTarget.value),
-            )
-          }
-        ></input>
-      </ListItem>
-      <ListItem
-        title={Locale.Settings.Access.Stability.ApiKey.Title}
-        subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle}
-      >
-        <PasswordInput
-          aria-label={Locale.Settings.Access.Stability.ApiKey.Title}
-          value={accessStore.stabilityApiKey}
-          type="text"
-          placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder}
-          onChange={(e) => {
-            accessStore.update(
-              (access) => (access.stabilityApiKey = e.currentTarget.value),
-            );
-          }}
-        />
-      </ListItem>
-    </>
-  );
+  const stabilityConfigComponent = null;
   const lflytekConfigComponent = accessStore.provider ===
     ServiceProvider.Iflytek && (
     <>
@@ -1366,30 +1113,7 @@ export function Settings() {
             </Popover>
           </ListItem>
 
-          <ListItem
-            title={Locale.Settings.Update.Version(currentVersion ?? "unknown")}
-            subTitle={
-              checkingUpdate
-                ? Locale.Settings.Update.IsChecking
-                : hasNewVersion
-                ? Locale.Settings.Update.FoundUpdate(remoteId ?? "ERROR")
-                : Locale.Settings.Update.IsLatest
-            }
-          >
-            {checkingUpdate ? (
-              <LoadingIcon />
-            ) : hasNewVersion ? (
-              <Link href={updateUrl} target="_blank" className="link">
-                {Locale.Settings.Update.GoToUpdate}
-              </Link>
-            ) : (
-              <IconButton
-                icon={<ResetIcon></ResetIcon>}
-                text={Locale.Settings.Update.CheckUpdate}
-                onClick={() => checkUpdate(true)}
-              />
-            )}
-          </ListItem>
+
 
           <ListItem title={Locale.Settings.SendKey}>
             <Select
@@ -1623,45 +1347,17 @@ export function Settings() {
 
                   {openAIConfigComponent}
                   {azureConfigComponent}
-                  {googleConfigComponent}
-                  {anthropicConfigComponent}
                   {baiduConfigComponent}
                   {byteDanceConfigComponent}
                   {alibabaConfigComponent}
                   {tencentConfigComponent}
-                  {moonshotConfigComponent}
-                  {stabilityConfigComponent}
                   {lflytekConfigComponent}
                 </>
               )}
             </>
           )}
 
-          {!shouldHideBalanceQuery && !clientConfig?.isApp ? (
-            <ListItem
-              title={Locale.Settings.Usage.Title}
-              subTitle={
-                showUsage
-                  ? loadingUsage
-                    ? Locale.Settings.Usage.IsChecking
-                    : Locale.Settings.Usage.SubTitle(
-                        usage?.used ?? "[?]",
-                        usage?.subscription ?? "[?]",
-                      )
-                  : Locale.Settings.Usage.NoAccess
-              }
-            >
-              {!showUsage || loadingUsage ? (
-                <div />
-              ) : (
-                <IconButton
-                  icon={<ResetIcon></ResetIcon>}
-                  text={Locale.Settings.Usage.Check}
-                  onClick={() => checkUsage(true)}
-                />
-              )}
-            </ListItem>
-          ) : null}
+
 
           <ListItem
             title={Locale.Settings.Access.CustomModel.Title}

+ 11 - 93
app/constant.ts

@@ -12,9 +12,7 @@ export const STABILITY_BASE_URL = "https://api.stability.ai";
 
 export const DEFAULT_API_HOST = "https://api.nextchat.dev";
 export const OPENAI_BASE_URL = "https://api.openai.com";
-export const ANTHROPIC_BASE_URL = "https://api.anthropic.com";
 
-export const GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/";
 
 export const BAIDU_BASE_URL = "https://aip.baidubce.com";
 export const BAIDU_OATUH_URL = `${BAIDU_BASE_URL}/oauth/2.0/token`;
@@ -25,7 +23,7 @@ export const ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/api/";
 
 export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
 
-export const MOONSHOT_BASE_URL = "https://api.moonshot.cn";
+
 export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com";
 
 export const CACHE_URL_PREFIX = "/api/cache";
@@ -35,7 +33,7 @@ export enum Path {
   Home = "/",
   Chat = "/chat",
   Settings = "/settings",
-  NewChat = "/new-chat",
+  MaskChat = "/mask-chat",
   Masks = "/masks",
   Auth = "/auth",
   Artifacts = "/artifacts",
@@ -45,15 +43,11 @@ export enum ApiPath {
   Cors = "",
   Azure = "/api/azure",
   OpenAI = "/api/openai",
-  Anthropic = "/api/anthropic",
-  Google = "/api/google",
   Baidu = "/api/baidu",
   ByteDance = "/api/bytedance",
   Alibaba = "/api/alibaba",
   Tencent = "/api/tencent",
-  Moonshot = "/api/moonshot",
   Iflytek = "/api/iflytek",
-  Stability = "/api/stability",
   Artifacts = "/api/artifacts",
   BigModel = "/api/bigModel",
 }
@@ -107,50 +101,29 @@ export enum ServiceProvider {
   DeepSeek = "DeepSeek",
   OpenAI = "OpenAI",
   Azure = "Azure",
-  Google = "Google",
-  Anthropic = "Anthropic",
   Baidu = "Baidu",
   ByteDance = "ByteDance",
   Alibaba = "Alibaba",
   Tencent = "Tencent",
-  Moonshot = "Moonshot",
-  Stability = "Stability",
   Iflytek = "Iflytek",
 }
 
-export enum GoogleSafetySettingsThreshold {
-  BLOCK_NONE = "BLOCK_NONE",
-  BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH",
-  BLOCK_MEDIUM_AND_ABOVE = "BLOCK_MEDIUM_AND_ABOVE",
-  BLOCK_LOW_AND_ABOVE = "BLOCK_LOW_AND_ABOVE",
-}
+
 
 export enum ModelProvider {
   BigModel = "bigModel",
   DeepSeek = "deepSeek",
-  Stability = "Stability",
   GPT = "GPT",
-  GeminiPro = "GeminiPro",
-  Claude = "Claude",
   Ernie = "Ernie",
   Doubao = "Doubao",
   Qwen = "Qwen",
   Hunyuan = "Hunyuan",
-  Moonshot = "Moonshot",
   Iflytek = "Iflytek",
 }
 
-export const Stability = {
-  GeneratePath: "v2beta/stable-image/generate",
-  ExampleEndpoint: "https://api.stability.ai",
-};
 
-export const Anthropic = {
-  ChatPath: "v1/messages",
-  ChatPath1: "v1/complete",
-  ExampleEndpoint: "https://api.anthropic.com",
-  Vision: "2023-06-01",
-};
+
+
 
 export const OpenaiPath = {
   ChatPath: "v1/chat/completions",
@@ -169,11 +142,7 @@ export const Azure = {
   ExampleEndpoint: "https://{resource-url}/openai",
 };
 
-export const Google = {
-  ExampleEndpoint: "https://generativelanguage.googleapis.com/",
-  ChatPath: (modelName: string) =>
-    `v1beta/models/${modelName}:streamGenerateContent`,
-};
+
 
 export const Baidu = {
   ExampleEndpoint: BAIDU_BASE_URL,
@@ -209,10 +178,7 @@ export const Tencent = {
   ExampleEndpoint: TENCENT_BASE_URL,
 };
 
-export const Moonshot = {
-  ExampleEndpoint: MOONSHOT_BASE_URL,
-  ChatPath: "v1/chat/completions",
-};
+
 
 export const Iflytek = {
   ExampleEndpoint: IFLYTEK_BASE_URL,
@@ -239,7 +205,7 @@ export const DEFAULT_SYSTEM_TEMPLATE: any = null;
 // `;
 
 export const SUMMARIZE_MODEL = "gpt-4o-mini";
-export const GEMINI_SUMMARIZE_MODEL = "gemini-pro";
+
 
 export const KnowledgeCutOffDate: Record<string, string> = {
   default: "2021-09",
@@ -254,8 +220,6 @@ export const KnowledgeCutOffDate: Record<string, string> = {
   "gpt-4-vision-preview": "2023-04",
   // After improvements,
   // it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
-  "gemini-pro": "2023-12",
-  "gemini-pro-vision": "2023-12",
 };
 
 const openaiModels = [
@@ -279,22 +243,7 @@ const openaiModels = [
   "dall-e-3",
 ];
 
-const googleModels = [
-  "gemini-1.0-pro",
-  "gemini-1.5-pro-latest",
-  "gemini-1.5-flash-latest",
-  "gemini-pro-vision",
-];
 
-const anthropicModels = [
-  "claude-instant-1.2",
-  "claude-2.0",
-  "claude-2.1",
-  "claude-3-sonnet-20240229",
-  "claude-3-opus-20240229",
-  "claude-3-haiku-20240307",
-  "claude-3-5-sonnet-20240620",
-];
 
 const baiduModels = [
   "ernie-4.0-turbo-8k",
@@ -339,7 +288,7 @@ const tencentModels = [
   "hunyuan-vision",
 ];
 
-const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"];
+
 
 const iflytekModels = [
   "general",
@@ -373,28 +322,7 @@ export const DEFAULT_MODELS = [
       sorted: 2,
     },
   })),
-  ...googleModels.map((name) => ({
-    name,
-    available: true,
-    sorted: seq++,
-    provider: {
-      id: "google",
-      providerName: "Google",
-      providerType: "google",
-      sorted: 3,
-    },
-  })),
-  ...anthropicModels.map((name) => ({
-    name,
-    available: true,
-    sorted: seq++,
-    provider: {
-      id: "anthropic",
-      providerName: "Anthropic",
-      providerType: "anthropic",
-      sorted: 4,
-    },
-  })),
+
   ...baiduModels.map((name) => ({
     name,
     available: true,
@@ -439,17 +367,7 @@ export const DEFAULT_MODELS = [
       sorted: 8,
     },
   })),
-  ...moonshotModes.map((name) => ({
-    name,
-    available: true,
-    sorted: seq++,
-    provider: {
-      id: "moonshot",
-      providerName: "Moonshot",
-      providerType: "moonshot",
-      sorted: 9,
-    },
-  })),
+
   ...iflytekModels.map((name) => ({
     name,
     available: true,

+ 1 - 40
app/store/access.ts

@@ -1,7 +1,6 @@
 import {
   ApiPath,
   DEFAULT_API_HOST,
-  GoogleSafetySettingsThreshold,
   ServiceProvider,
   StoreKey,
 } from "../constant";
@@ -19,13 +18,7 @@ const DEFAULT_OPENAI_URL = isApp
   ? DEFAULT_API_HOST + "/api/proxy/openai"
   : ApiPath.OpenAI;
 
-const DEFAULT_GOOGLE_URL = isApp
-  ? DEFAULT_API_HOST + "/api/proxy/google"
-  : ApiPath.Google;
 
-const DEFAULT_ANTHROPIC_URL = isApp
-  ? DEFAULT_API_HOST + "/api/proxy/anthropic"
-  : ApiPath.Anthropic;
 
 const DEFAULT_BAIDU_URL = isApp
   ? DEFAULT_API_HOST + "/api/proxy/baidu"
@@ -43,13 +36,7 @@ const DEFAULT_TENCENT_URL = isApp
   ? DEFAULT_API_HOST + "/api/proxy/tencent"
   : ApiPath.Tencent;
 
-const DEFAULT_MOONSHOT_URL = isApp
-  ? DEFAULT_API_HOST + "/api/proxy/moonshot"
-  : ApiPath.Moonshot;
 
-const DEFAULT_STABILITY_URL = isApp
-  ? DEFAULT_API_HOST + "/api/proxy/stability"
-  : ApiPath.Stability;
 
 const DEFAULT_IFLYTEK_URL = isApp
   ? DEFAULT_API_HOST + "/api/proxy/iflytek"
@@ -70,16 +57,7 @@ const DEFAULT_ACCESS_STATE = {
   azureApiKey: "",
   azureApiVersion: "2023-08-01-preview",
 
-  // google ai studio
-  googleUrl: DEFAULT_GOOGLE_URL,
-  googleApiKey: "",
-  googleApiVersion: "v1",
-  googleSafetySettings: GoogleSafetySettingsThreshold.BLOCK_ONLY_HIGH,
 
-  // anthropic
-  anthropicUrl: DEFAULT_ANTHROPIC_URL,
-  anthropicApiKey: "",
-  anthropicApiVersion: "2023-06-01",
 
   // baidu
   baiduUrl: DEFAULT_BAIDU_URL,
@@ -94,13 +72,7 @@ const DEFAULT_ACCESS_STATE = {
   alibabaUrl: DEFAULT_ALIBABA_URL,
   alibabaApiKey: "",
 
-  // moonshot
-  moonshotUrl: DEFAULT_MOONSHOT_URL,
-  moonshotApiKey: "",
 
-  //stability
-  stabilityUrl: DEFAULT_STABILITY_URL,
-  stabilityApiKey: "",
 
   // tencent
   tencentUrl: DEFAULT_TENCENT_URL,
@@ -140,13 +112,7 @@ export const useAccessStore = createPersistStore(
       return ensure(get(), ["azureUrl", "azureApiKey", "azureApiVersion"]);
     },
 
-    isValidGoogle() {
-      return ensure(get(), ["googleApiKey"]);
-    },
 
-    isValidAnthropic() {
-      return ensure(get(), ["anthropicApiKey"]);
-    },
 
     isValidBaidu() {
       return ensure(get(), ["baiduApiKey", "baiduSecretKey"]);
@@ -164,9 +130,7 @@ export const useAccessStore = createPersistStore(
       return ensure(get(), ["tencentSecretKey", "tencentSecretId"]);
     },
 
-    isValidMoonshot() {
-      return ensure(get(), ["moonshotApiKey"]);
-    },
+
     isValidIflytek() {
       return ensure(get(), ["iflytekApiKey"]);
     },
@@ -178,13 +142,10 @@ export const useAccessStore = createPersistStore(
       return (
         this.isValidOpenAI() ||
         this.isValidAzure() ||
-        this.isValidGoogle() ||
-        this.isValidAnthropic() ||
         this.isValidBaidu() ||
         this.isValidByteDance() ||
         this.isValidAlibaba() ||
         this.isValidTencent ||
-        this.isValidMoonshot() ||
         this.isValidIflytek() ||
         !this.enabledAccessControl() ||
         (this.enabledAccessControl() && ensure(get(), ["accessCode"]))

+ 1 - 2
app/store/chat.ts

@@ -11,7 +11,6 @@ import {
   KnowledgeCutOffDate,
   StoreKey,
   SUMMARIZE_MODEL,
-  GEMINI_SUMMARIZE_MODEL,
   ServiceProvider,
 } from "../constant";
 import { getClientApi } from "../client/api";
@@ -122,7 +121,7 @@ function getSummarizeModel(currentModel: string) {
     return summarizeModel?.name ?? currentModel;
   }
   if (currentModel.startsWith("gemini")) {
-    return GEMINI_SUMMARIZE_MODEL;
+    return SUMMARIZE_MODEL;
   }
   return currentModel;
 }

+ 20 - 4
package-lock.json

@@ -11,6 +11,7 @@
       "dependencies": {
         "@fortaine/fetch-event-source": "^3.0.6",
         "@hello-pangea/dnd": "^16.5.0",
+        "@next/swc-darwin-arm64": "^15.4.6",
         "@next/third-parties": "^14.1.0",
         "@svgr/webpack": "^6.5.1",
         "@vercel/analytics": "^0.1.11",
@@ -2684,14 +2685,13 @@
       "license": "MIT"
     },
     "node_modules/@next/swc-darwin-arm64": {
-      "version": "14.2.31",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.31.tgz",
-      "integrity": "sha512-dTHKfaFO/xMJ3kzhXYgf64VtV6MMwDs2viedDOdP+ezd0zWMOQZkxcwOfdcQeQCpouTr9b+xOqMCUXxgLizl8Q==",
+      "version": "15.4.6",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz",
+      "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==",
       "cpu": [
         "arm64"
       ],
       "license": "MIT",
-      "optional": true,
       "os": [
         "darwin"
       ],
@@ -8194,6 +8194,22 @@
         }
       }
     },
+    "node_modules/next/node_modules/@next/swc-darwin-arm64": {
+      "version": "14.2.31",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.31.tgz",
+      "integrity": "sha512-dTHKfaFO/xMJ3kzhXYgf64VtV6MMwDs2viedDOdP+ezd0zWMOQZkxcwOfdcQeQCpouTr9b+xOqMCUXxgLizl8Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "node_modules/node-addon-api": {
       "version": "7.1.1",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",

+ 2 - 2
package.json

@@ -23,6 +23,7 @@
   "dependencies": {
     "@fortaine/fetch-event-source": "^3.0.6",
     "@hello-pangea/dnd": "^16.5.0",
+    "@next/swc-darwin-arm64": "^15.4.6",
     "@next/third-parties": "^14.1.0",
     "@svgr/webpack": "^6.5.1",
     "@vercel/analytics": "^0.1.11",
@@ -30,7 +31,6 @@
     "antd": "^5.25.0",
     "axios": "^1.7.7",
     "dayjs": "^1.11.12",
-
     "fuse.js": "^7.0.0",
     "heic2any": "^0.0.4",
     "html-to-image": "^1.11.11",
@@ -76,4 +76,4 @@
     "lint-staged/yaml": "^2.2.2"
   },
   "packageManager": "yarn@1.22.19"
-}
+}