Skip to content

Commit

Permalink
Merge pull request ChatGPTNextWeb#6141 from siliconflow/provider_silicon
Browse files Browse the repository at this point in the history
New provider SiliconFlow and Its Latest DeekSeek Models
  • Loading branch information
Leizhenpeng authored Feb 4, 2025
2 parents bb4832e + ad9ab9d commit 28cbe56
Show file tree
Hide file tree
Showing 13 changed files with 527 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ Customize Stability API url.

Enable MCP(Model Context Protocol)Feature

### `SILICONFLOW_API_KEY` (optional)

SiliconFlow API Key.

### `SILICONFLOW_URL` (optional)

SiliconFlow API URL.

## Requirements

Expand Down
7 changes: 7 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ Stability API密钥

启用MCP(Model Context Protocol)功能

### `SILICONFLOW_API_KEY` (optional)

SiliconFlow API Key.

### `SILICONFLOW_URL` (optional)

SiliconFlow API URL.

## 开发

Expand Down
3 changes: 3 additions & 0 deletions app/api/[provider]/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { handle as moonshotHandler } from "../../moonshot";
import { handle as stabilityHandler } from "../../stability";
import { handle as iflytekHandler } from "../../iflytek";
import { handle as deepseekHandler } from "../../deepseek";
import { handle as siliconflowHandler } from "../../siliconflow";
import { handle as xaiHandler } from "../../xai";
import { handle as chatglmHandler } from "../../glm";
import { handle as proxyHandler } from "../../proxy";
Expand Down Expand Up @@ -47,6 +48,8 @@ async function handle(
return xaiHandler(req, { params });
case ApiPath.ChatGLM:
return chatglmHandler(req, { params });
case ApiPath.SiliconFlow:
return siliconflowHandler(req, { params });
case ApiPath.OpenAI:
return openaiHandler(req, { params });
default:
Expand Down
3 changes: 3 additions & 0 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
case ModelProvider.ChatGLM:
systemApiKey = serverConfig.chatglmApiKey;
break;
case ModelProvider.SiliconFlow:
systemApiKey = serverConfig.siliconFlowApiKey;
break;
case ModelProvider.GPT:
default:
if (req.nextUrl.pathname.includes("azure/deployments")) {
Expand Down
128 changes: 128 additions & 0 deletions app/api/siliconflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { getServerSideConfig } from "@/app/config/server";
import {
SILICONFLOW_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 { isModelNotavailableInServer } from "@/app/utils/model";

const serverConfig = getServerSideConfig();

export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[SiliconFlow Route] params ", params);

if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}

const authResult = auth(req, ModelProvider.SiliconFlow);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}

try {
const response = await request(req);
return response;
} catch (e) {
console.error("[SiliconFlow] ", 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.SiliconFlow, "");

let baseUrl = serverConfig.siliconFlowUrl || SILICONFLOW_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 (
isModelNotavailableInServer(
serverConfig.customModels,
jsonBody?.model as string,
ServiceProvider.SiliconFlow as string,
)
) {
return NextResponse.json(
{
error: true,
message: `you are not allowed to use ${jsonBody?.model} model`,
},
{
status: 403,
},
);
}
} catch (e) {
console.error(`[SiliconFlow] 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);
}
}
12 changes: 12 additions & 0 deletions app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SparkApi } from "./platforms/iflytek";
import { DeepSeekApi } from "./platforms/deepseek";
import { XAIApi } from "./platforms/xai";
import { ChatGLMApi } from "./platforms/glm";
import { SiliconflowApi } from "./platforms/siliconflow";

export const ROLES = ["system", "user", "assistant"] as const;
export type MessageRole = (typeof ROLES)[number];
Expand Down Expand Up @@ -164,6 +165,9 @@ export class ClientApi {
case ModelProvider.ChatGLM:
this.llm = new ChatGLMApi();
break;
case ModelProvider.SiliconFlow:
this.llm = new SiliconflowApi();
break;
default:
this.llm = new ChatGPTApi();
}
Expand Down Expand Up @@ -254,6 +258,8 @@ export function getHeaders(ignoreHeaders: boolean = false) {
const isDeepSeek = modelConfig.providerName === ServiceProvider.DeepSeek;
const isXAI = modelConfig.providerName === ServiceProvider.XAI;
const isChatGLM = modelConfig.providerName === ServiceProvider.ChatGLM;
const isSiliconFlow =
modelConfig.providerName === ServiceProvider.SiliconFlow;
const isEnabledAccessControl = accessStore.enabledAccessControl();
const apiKey = isGoogle
? accessStore.googleApiKey
Expand All @@ -273,6 +279,8 @@ export function getHeaders(ignoreHeaders: boolean = false) {
? accessStore.deepseekApiKey
: isChatGLM
? accessStore.chatglmApiKey
: isSiliconFlow
? accessStore.siliconflowApiKey
: isIflytek
? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
Expand All @@ -290,6 +298,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
isDeepSeek,
isXAI,
isChatGLM,
isSiliconFlow,
apiKey,
isEnabledAccessControl,
};
Expand Down Expand Up @@ -317,6 +326,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
isDeepSeek,
isXAI,
isChatGLM,
isSiliconFlow,
apiKey,
isEnabledAccessControl,
} = getConfig();
Expand Down Expand Up @@ -365,6 +375,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
return new ClientApi(ModelProvider.XAI);
case ServiceProvider.ChatGLM:
return new ClientApi(ModelProvider.ChatGLM);
case ServiceProvider.SiliconFlow:
return new ClientApi(ModelProvider.SiliconFlow);
default:
return new ClientApi(ModelProvider.GPT);
}
Expand Down
Loading

0 comments on commit 28cbe56

Please sign in to comment.