-
Notifications
You must be signed in to change notification settings - Fork 59.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
since #5984, add DeepSeek as a new ModelProvider (with deepseek-chat&deepseek-coder models), so that user can use openai and deepseek at same time with different api url & key #5989
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { getServerSideConfig } from "@/app/config/server"; | ||
import { | ||
DEEPSEEK_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"; | ||
|
||
const serverConfig = getServerSideConfig(); | ||
|
||
export async function handle( | ||
req: NextRequest, | ||
{ params }: { params: { path: string[] } }, | ||
) { | ||
console.log("[DeepSeek Route] params ", params); | ||
|
||
if (req.method === "OPTIONS") { | ||
return NextResponse.json({ body: "OK" }, { status: 200 }); | ||
} | ||
|
||
const authResult = auth(req, ModelProvider.DeepSeek); | ||
if (authResult.error) { | ||
return NextResponse.json(authResult, { | ||
status: 401, | ||
}); | ||
} | ||
|
||
try { | ||
const response = await request(req); | ||
return response; | ||
} catch (e) { | ||
console.error("[DeepSeek] ", 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.DeepSeek, ""); | ||
|
||
let baseUrl = serverConfig.deepseekUrl || DEEPSEEK_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, | ||
Comment on lines
+76
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Remove @ts-ignore and properly type the duplex option. The Consider creating a proper type for the fetch options: type ExtendedRequestInit = RequestInit & {
duplex: 'half' | 'full';
};
const fetchOptions: ExtendedRequestInit = {
// ... other options ...
duplex: 'half',
}; |
||
}; | ||
Comment on lines
+67
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Review of sensitive information handling reveals potential logging concerns The code review reveals consistent handling of Authorization headers across all API files, which is good. However, there are potential security concerns with logging:
Recommendations:
🔗 Analysis chainVerify secure handling of sensitive information. Let's ensure that sensitive information is properly handled in the request headers and logging. Let me check the authentication header handling in the other files to ensure consistent and secure practices. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for potential sensitive data exposure in logging statements
rg -n "console\." app/api/deepseek.ts
# Check if authentication headers are properly handled elsewhere in the codebase
rg -l "Authorization.*header" --type ts
Length of output: 430 Script: #!/bin/bash
# Check how Authorization header is handled in other API files
for file in app/api/xai.ts app/api/iflytek.ts app/api/glm.ts app/api/moonshot.ts app/api/bytedance.ts app/api/alibaba.ts; do
echo "=== $file ==="
rg -A 2 "Authorization.*header" "$file"
done
# Check for any sensitive data logging in these files
for file in app/api/xai.ts app/api/iflytek.ts app/api/glm.ts app/api/moonshot.ts app/api/bytedance.ts app/api/alibaba.ts; do
echo "=== $file ==="
rg "console\." "$file"
done
Length of output: 4569 |
||
|
||
// #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(`[DeepSeek] 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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling in the handle function.
While the basic flow is correct, the error handling could be improved:
Consider applying this improvement:
📝 Committable suggestion