Skip to content

Commit 377480b

Browse files
committed
2 parents 8bd0d6a + 12863f5 commit 377480b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1327
-338
lines changed

.env.template

+6-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ ANTHROPIC_API_VERSION=
7373
### anthropic claude Api url (optional)
7474
ANTHROPIC_URL=
7575

76-
7776
### (optional)
7877
WHITE_WEBDAV_ENDPOINTS=
78+
79+
### siliconflow Api key (optional)
80+
SILICONFLOW_API_KEY=
81+
82+
### siliconflow Api url (optional)
83+
SILICONFLOW_URL=

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023-2024 Zhang Yifei
3+
Copyright (c) 2023-2025 NextChat
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
<div align="center">
22

33
<a href='https://nextchat.club'>
4-
<img src="https://github.com/user-attachments/assets/287c510f-f508-478e-ade3-54d30453dc18" width="1000" alt="icon"/>
4+
<img src="https://github.com/user-attachments/assets/83bdcc07-ae5e-4954-a53a-ac151ba6ccf3" width="1000" alt="icon"/>
55
</a>
66

77

88

9+
910
<h1 align="center">NextChat (ChatGPT Next Web)</h1>
1011

1112
English / [简体中文](./README_CN.md)
1213

1314
<a href="https://trendshift.io/repositories/5973" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5973" alt="ChatGPTNextWeb%2FChatGPT-Next-Web | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
1415

1516

16-
17-
One-Click to get a well-designed cross-platform ChatGPT web UI, with Claude, GPT4 & Gemini Pro support.
17+
✨ Light and Fast AI Assistant,with Claude, DeepSeek, GPT4 & Gemini Pro support.
1818

1919
[![Saas][Saas-image]][saas-url]
2020
[![Web][Web-image]][web-url]
2121
[![Windows][Windows-image]][download-url]
2222
[![MacOS][MacOS-image]][download-url]
2323
[![Linux][Linux-image]][download-url]
2424

25+
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App Demo](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases)
2526
[NextChatAI](https://nextchat.club?utm_source=readme) / [Web App Demo](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
2627

2728

@@ -34,9 +35,9 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with Claude, GPT
3435
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
3536
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
3637

37-
[<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://img.shields.io/badge/BT_Deploy-Install-20a53a" alt="BT Deply Install" height="30">](https://www.bt.cn/new/download.html)
38+
[<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/ChatGPTNextWeb/NextChat)
3839

39-
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="60" width="288" >](https://monica.im/?utm=nxcrp)
40+
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="50" width="" >](https://monica.im/?utm=nxcrp)
4041

4142
</div>
4243

@@ -352,6 +353,13 @@ Customize Stability API url.
352353

353354
Enable MCP(Model Context Protocol)Feature
354355

356+
### `SILICONFLOW_API_KEY` (optional)
357+
358+
SiliconFlow API Key.
359+
360+
### `SILICONFLOW_URL` (optional)
361+
362+
SiliconFlow API URL.
355363

356364
## Requirements
357365

README_CN.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ code1,code2,code3
8989
9090
### `OPENAI_API_KEY` (必填项)
9191

92-
OpanAI 密钥,你在 openai 账户页面申请的 api key,使用英文逗号隔开多个 key,这样可以随机轮询这些 key。
92+
OpenAI 密钥,你在 openai 账户页面申请的 api key,使用英文逗号隔开多个 key,这样可以随机轮询这些 key。
9393

9494
### `CODE` (可选)
9595

@@ -267,6 +267,13 @@ Stability API密钥
267267

268268
启用MCP(Model Context Protocol)功能
269269

270+
### `SILICONFLOW_API_KEY` (optional)
271+
272+
SiliconFlow API Key.
273+
274+
### `SILICONFLOW_URL` (optional)
275+
276+
SiliconFlow API URL.
270277

271278
## 开发
272279

app/api/[provider]/[...path]/route.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { handle as moonshotHandler } from "../../moonshot";
1111
import { handle as stabilityHandler } from "../../stability";
1212
import { handle as iflytekHandler } from "../../iflytek";
1313
import { handle as deepseekHandler } from "../../deepseek";
14+
import { handle as siliconflowHandler } from "../../siliconflow";
1415
import { handle as xaiHandler } from "../../xai";
1516
import { handle as chatglmHandler } from "../../glm";
1617
import { handle as proxyHandler } from "../../proxy";
@@ -47,6 +48,8 @@ async function handle(
4748
return xaiHandler(req, { params });
4849
case ApiPath.ChatGLM:
4950
return chatglmHandler(req, { params });
51+
case ApiPath.SiliconFlow:
52+
return siliconflowHandler(req, { params });
5053
case ApiPath.OpenAI:
5154
return openaiHandler(req, { params });
5255
default:

app/api/auth.ts

+3
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
101101
case ModelProvider.ChatGLM:
102102
systemApiKey = serverConfig.chatglmApiKey;
103103
break;
104+
case ModelProvider.SiliconFlow:
105+
systemApiKey = serverConfig.siliconFlowApiKey;
106+
break;
104107
case ModelProvider.GPT:
105108
default:
106109
if (req.nextUrl.pathname.includes("azure/deployments")) {

app/api/openai.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
1414
if (config.disableGPT4) {
1515
remoteModelRes.data = remoteModelRes.data.filter(
1616
(m) =>
17-
!(m.id.startsWith("gpt-4") || m.id.startsWith("chatgpt-4o") || m.id.startsWith("o1")) ||
18-
m.id.startsWith("gpt-4o-mini"),
17+
!(
18+
m.id.startsWith("gpt-4") ||
19+
m.id.startsWith("chatgpt-4o") ||
20+
m.id.startsWith("o1") ||
21+
m.id.startsWith("o3")
22+
) || m.id.startsWith("gpt-4o-mini"),
1923
);
2024
}
2125

app/api/siliconflow.ts

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { getServerSideConfig } from "@/app/config/server";
2+
import {
3+
SILICONFLOW_BASE_URL,
4+
ApiPath,
5+
ModelProvider,
6+
ServiceProvider,
7+
} from "@/app/constant";
8+
import { prettyObject } from "@/app/utils/format";
9+
import { NextRequest, NextResponse } from "next/server";
10+
import { auth } from "@/app/api/auth";
11+
import { isModelNotavailableInServer } from "@/app/utils/model";
12+
13+
const serverConfig = getServerSideConfig();
14+
15+
export async function handle(
16+
req: NextRequest,
17+
{ params }: { params: { path: string[] } },
18+
) {
19+
console.log("[SiliconFlow Route] params ", params);
20+
21+
if (req.method === "OPTIONS") {
22+
return NextResponse.json({ body: "OK" }, { status: 200 });
23+
}
24+
25+
const authResult = auth(req, ModelProvider.SiliconFlow);
26+
if (authResult.error) {
27+
return NextResponse.json(authResult, {
28+
status: 401,
29+
});
30+
}
31+
32+
try {
33+
const response = await request(req);
34+
return response;
35+
} catch (e) {
36+
console.error("[SiliconFlow] ", e);
37+
return NextResponse.json(prettyObject(e));
38+
}
39+
}
40+
41+
async function request(req: NextRequest) {
42+
const controller = new AbortController();
43+
44+
// alibaba use base url or just remove the path
45+
let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.SiliconFlow, "");
46+
47+
let baseUrl = serverConfig.siliconFlowUrl || SILICONFLOW_BASE_URL;
48+
49+
if (!baseUrl.startsWith("http")) {
50+
baseUrl = `https://${baseUrl}`;
51+
}
52+
53+
if (baseUrl.endsWith("/")) {
54+
baseUrl = baseUrl.slice(0, -1);
55+
}
56+
57+
console.log("[Proxy] ", path);
58+
console.log("[Base Url]", baseUrl);
59+
60+
const timeoutId = setTimeout(
61+
() => {
62+
controller.abort();
63+
},
64+
10 * 60 * 1000,
65+
);
66+
67+
const fetchUrl = `${baseUrl}${path}`;
68+
const fetchOptions: RequestInit = {
69+
headers: {
70+
"Content-Type": "application/json",
71+
Authorization: req.headers.get("Authorization") ?? "",
72+
},
73+
method: req.method,
74+
body: req.body,
75+
redirect: "manual",
76+
// @ts-ignore
77+
duplex: "half",
78+
signal: controller.signal,
79+
};
80+
81+
// #1815 try to refuse some request to some models
82+
if (serverConfig.customModels && req.body) {
83+
try {
84+
const clonedBody = await req.text();
85+
fetchOptions.body = clonedBody;
86+
87+
const jsonBody = JSON.parse(clonedBody) as { model?: string };
88+
89+
// not undefined and is false
90+
if (
91+
isModelNotavailableInServer(
92+
serverConfig.customModels,
93+
jsonBody?.model as string,
94+
ServiceProvider.SiliconFlow as string,
95+
)
96+
) {
97+
return NextResponse.json(
98+
{
99+
error: true,
100+
message: `you are not allowed to use ${jsonBody?.model} model`,
101+
},
102+
{
103+
status: 403,
104+
},
105+
);
106+
}
107+
} catch (e) {
108+
console.error(`[SiliconFlow] filter`, e);
109+
}
110+
}
111+
try {
112+
const res = await fetch(fetchUrl, fetchOptions);
113+
114+
// to prevent browser prompt for credentials
115+
const newHeaders = new Headers(res.headers);
116+
newHeaders.delete("www-authenticate");
117+
// to disable nginx buffering
118+
newHeaders.set("X-Accel-Buffering", "no");
119+
120+
return new Response(res.body, {
121+
status: res.status,
122+
statusText: res.statusText,
123+
headers: newHeaders,
124+
});
125+
} finally {
126+
clearTimeout(timeoutId);
127+
}
128+
}

app/client/api.ts

+12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { SparkApi } from "./platforms/iflytek";
2323
import { DeepSeekApi } from "./platforms/deepseek";
2424
import { XAIApi } from "./platforms/xai";
2525
import { ChatGLMApi } from "./platforms/glm";
26+
import { SiliconflowApi } from "./platforms/siliconflow";
2627

2728
export const ROLES = ["system", "user", "assistant"] as const;
2829
export type MessageRole = (typeof ROLES)[number];
@@ -164,6 +165,9 @@ export class ClientApi {
164165
case ModelProvider.ChatGLM:
165166
this.llm = new ChatGLMApi();
166167
break;
168+
case ModelProvider.SiliconFlow:
169+
this.llm = new SiliconflowApi();
170+
break;
167171
default:
168172
this.llm = new ChatGPTApi();
169173
}
@@ -254,6 +258,8 @@ export function getHeaders(ignoreHeaders: boolean = false) {
254258
const isDeepSeek = modelConfig.providerName === ServiceProvider.DeepSeek;
255259
const isXAI = modelConfig.providerName === ServiceProvider.XAI;
256260
const isChatGLM = modelConfig.providerName === ServiceProvider.ChatGLM;
261+
const isSiliconFlow =
262+
modelConfig.providerName === ServiceProvider.SiliconFlow;
257263
const isEnabledAccessControl = accessStore.enabledAccessControl();
258264
const apiKey = isGoogle
259265
? accessStore.googleApiKey
@@ -273,6 +279,8 @@ export function getHeaders(ignoreHeaders: boolean = false) {
273279
? accessStore.deepseekApiKey
274280
: isChatGLM
275281
? accessStore.chatglmApiKey
282+
: isSiliconFlow
283+
? accessStore.siliconflowApiKey
276284
: isIflytek
277285
? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
278286
? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
@@ -290,6 +298,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
290298
isDeepSeek,
291299
isXAI,
292300
isChatGLM,
301+
isSiliconFlow,
293302
apiKey,
294303
isEnabledAccessControl,
295304
};
@@ -317,6 +326,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
317326
isDeepSeek,
318327
isXAI,
319328
isChatGLM,
329+
isSiliconFlow,
320330
apiKey,
321331
isEnabledAccessControl,
322332
} = getConfig();
@@ -365,6 +375,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
365375
return new ClientApi(ModelProvider.XAI);
366376
case ServiceProvider.ChatGLM:
367377
return new ClientApi(ModelProvider.ChatGLM);
378+
case ServiceProvider.SiliconFlow:
379+
return new ClientApi(ModelProvider.SiliconFlow);
368380
default:
369381
return new ClientApi(ModelProvider.GPT);
370382
}

0 commit comments

Comments
 (0)