Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Aug 7, 2024
2 parents 594e0de + 624e4db commit abd95e7
Show file tree
Hide file tree
Showing 20 changed files with 599 additions and 19 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,18 @@ Alibaba Cloud Api Key.

Alibaba Cloud Api Url.

### `IFLYTEK_URL` (Optional)

iflytek Api Url.

### `IFLYTEK_API_KEY` (Optional)

iflytek Api Key.

### `IFLYTEK_API_SECRET` (Optional)

iflytek Api Secret.

### `HIDE_USER_API_KEY` (optional)

> Default: Empty
Expand Down
14 changes: 14 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,20 @@ ByteDance Api Url.

阿里云(千问)Api Url.

### `IFLYTEK_URL` (可选)

讯飞星火Api Url.

### `IFLYTEK_API_KEY` (可选)

讯飞星火Api Key.

### `IFLYTEK_API_SECRET` (可选)

讯飞星火Api Secret.



### `HIDE_USER_API_KEY` (可选)

如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
Expand Down
4 changes: 3 additions & 1 deletion app/api/[provider]/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ 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,
{ params }: { params: { provider: string; path: string[] } },
Expand All @@ -34,6 +34,8 @@ async function handle(
return moonshotHandler(req, { params });
case ApiPath.Stability:
return stabilityHandler(req, { params });
case ApiPath.Iflytek:
return iflytekHandler(req, { params });
default:
return openaiHandler(req, { params });
}
Expand Down
4 changes: 4 additions & 0 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
case ModelProvider.Moonshot:
systemApiKey = serverConfig.moonshotApiKey;
break;
case ModelProvider.Iflytek:
systemApiKey =
serverConfig.iflytekApiKey + ":" + serverConfig.iflytekApiSecret;
break;
case ModelProvider.GPT:
default:
if (req.nextUrl.pathname.includes("azure/deployments")) {
Expand Down
131 changes: 131 additions & 0 deletions app/api/iflytek.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { getServerSideConfig } from "@/app/config/server";
import {
Iflytek,
IFLYTEK_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";
// iflytek

const serverConfig = getServerSideConfig();

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

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

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

try {
const response = await request(req);
return response;
} catch (e) {
console.error("[Iflytek] ", e);
return NextResponse.json(prettyObject(e));
}
}

async function request(req: NextRequest) {
const controller = new AbortController();

// iflytek use base url or just remove the path
let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Iflytek, "");

let baseUrl = serverConfig.iflytekUrl || IFLYTEK_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,
};

// 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.Iflytek as string,
)
) {
return NextResponse.json(
{
error: true,
message: `you are not allowed to use ${jsonBody?.model} model`,
},
{
status: 403,
},
);
}
} catch (e) {
console.error(`[Iflytek] 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);
}
}
17 changes: 11 additions & 6 deletions app/api/webdav/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async function handle(

const requestUrl = new URL(req.url);
let endpoint = requestUrl.searchParams.get("endpoint");
let proxy_method = requestUrl.searchParams.get("proxy_method") || req.method;

// Validate the endpoint to prevent potential SSRF attacks
if (
Expand Down Expand Up @@ -65,7 +66,11 @@ async function handle(
const targetPath = `${endpoint}${endpointPath}`;

// only allow MKCOL, GET, PUT
if (req.method !== "MKCOL" && req.method !== "GET" && req.method !== "PUT") {
if (
proxy_method !== "MKCOL" &&
proxy_method !== "GET" &&
proxy_method !== "PUT"
) {
return NextResponse.json(
{
error: true,
Expand All @@ -78,7 +83,7 @@ async function handle(
}

// for MKCOL request, only allow request ${folder}
if (req.method === "MKCOL" && !targetPath.endsWith(folder)) {
if (proxy_method === "MKCOL" && !targetPath.endsWith(folder)) {
return NextResponse.json(
{
error: true,
Expand All @@ -91,7 +96,7 @@ async function handle(
}

// for GET request, only allow request ending with fileName
if (req.method === "GET" && !targetPath.endsWith(fileName)) {
if (proxy_method === "GET" && !targetPath.endsWith(fileName)) {
return NextResponse.json(
{
error: true,
Expand All @@ -104,7 +109,7 @@ async function handle(
}

// for PUT request, only allow request ending with fileName
if (req.method === "PUT" && !targetPath.endsWith(fileName)) {
if (proxy_method === "PUT" && !targetPath.endsWith(fileName)) {
return NextResponse.json(
{
error: true,
Expand All @@ -118,7 +123,7 @@ async function handle(

const targetUrl = targetPath;

const method = req.method;
const method = proxy_method || req.method;
const shouldNotHaveBody = ["get", "head"].includes(
method?.toLowerCase() ?? "",
);
Expand All @@ -143,7 +148,7 @@ async function handle(
"[Any Proxy]",
targetUrl,
{
method: req.method,
method: method,
},
{
status: fetchResult?.status,
Expand Down
12 changes: 12 additions & 0 deletions app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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;
export type MessageRole = (typeof ROLES)[number];
Expand Down Expand Up @@ -128,6 +129,9 @@ export class ClientApi {
case ModelProvider.Moonshot:
this.llm = new MoonshotApi();
break;
case ModelProvider.Iflytek:
this.llm = new SparkApi();
break;
default:
this.llm = new ChatGPTApi();
}
Expand Down Expand Up @@ -211,6 +215,7 @@ export function getHeaders() {
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
Expand All @@ -224,6 +229,10 @@ export function getHeaders() {
? accessStore.alibabaApiKey
: isMoonshot
? accessStore.moonshotApiKey
: isIflytek
? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
: ""
: accessStore.openaiApiKey;
return {
isGoogle,
Expand All @@ -233,6 +242,7 @@ export function getHeaders() {
isByteDance,
isAlibaba,
isMoonshot,
isIflytek,
apiKey,
isEnabledAccessControl,
};
Expand Down Expand Up @@ -286,6 +296,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
return new ClientApi(ModelProvider.Hunyuan);
case ServiceProvider.Moonshot:
return new ClientApi(ModelProvider.Moonshot);
case ServiceProvider.Iflytek:
return new ClientApi(ModelProvider.Iflytek);
default:
return new ClientApi(ModelProvider.GPT);
}
Expand Down
18 changes: 13 additions & 5 deletions app/client/platforms/baidu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,24 @@ export class ErnieApi implements LLMApi {

async chat(options: ChatOptions) {
const messages = options.messages.map((v) => ({
role: v.role,
// "error_code": 336006, "error_msg": "the role of message with even index in the messages must be user or function",
role: v.role === "system" ? "user" : v.role,
content: getMessageTextContent(v),
}));

// "error_code": 336006, "error_msg": "the length of messages must be an odd number",
if (messages.length % 2 === 0) {
messages.unshift({
role: "user",
content: " ",
});
if (messages.at(0)?.role === "user") {
messages.splice(1, 0, {
role: "assistant",
content: " ",
});
} else {
messages.unshift({
role: "user",
content: " ",
});
}
}

const modelConfig = {
Expand Down
Loading

0 comments on commit abd95e7

Please sign in to comment.