-
Notifications
You must be signed in to change notification settings - Fork 60k
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
feat: [#5714] 支持GLM #5741
feat: [#5714] 支持GLM #5741
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,129 @@ | ||||||||||||||||||||||||||||||||||||
import { getServerSideConfig } from "@/app/config/server"; | ||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||
GLM_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("[GLM Route] params ", params); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
if (req.method === "OPTIONS") { | ||||||||||||||||||||||||||||||||||||
return NextResponse.json({ body: "OK" }, { status: 200 }); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+21
to
+23
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. Set CORS Headers for Preflight Requests When handling Apply this diff to include the necessary CORS headers: return NextResponse.json({ body: "OK" }, {
status: 200,
+ headers: {
+ 'Access-Control-Allow-Origin': '*', // Adjust as needed
+ 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
}); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
const authResult = auth(req, ModelProvider.GLM); | ||||||||||||||||||||||||||||||||||||
if (authResult.error) { | ||||||||||||||||||||||||||||||||||||
return NextResponse.json(authResult, { | ||||||||||||||||||||||||||||||||||||
status: 401, | ||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||
const response = await request(req); | ||||||||||||||||||||||||||||||||||||
return response; | ||||||||||||||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||||||||||||||
Comment on lines
+32
to
+35
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. Add error cleanup in catch block. The try-catch block should clean up any resources (like response streams) in case of an error to prevent memory leaks. Apply this diff: try {
const response = await request(req);
return response;
} catch (e) {
+ if (response?.body) {
+ try {
+ await response.body.cancel();
+ } catch {} // Ignore cleanup errors
+ }
console.error("[GLM] ", e);
return NextResponse.json(prettyObject(e));
}
|
||||||||||||||||||||||||||||||||||||
console.error("[GLM] ", e); | ||||||||||||||||||||||||||||||||||||
return NextResponse.json(prettyObject(e)); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+36
to
+38
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. Avoid Exposing Internal Error Details to Clients Returning the full error object in the API response may reveal sensitive internal information. It's better to return a generic error message to the client and log the detailed error on the server side. Apply this diff to return a generic error response: console.error("[GLM] ", e);
- return NextResponse.json(prettyObject(e));
+ return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
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.GLM, ""); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
let baseUrl = serverConfig.glmUrl || GLM_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}`; | ||||||||||||||||||||||||||||||||||||
console.log("[Fetch Url] ", fetchUrl); | ||||||||||||||||||||||||||||||||||||
const fetchOptions: RequestInit = { | ||||||||||||||||||||||||||||||||||||
headers: { | ||||||||||||||||||||||||||||||||||||
"Content-Type": "application/json", | ||||||||||||||||||||||||||||||||||||
Authorization: req.headers.get("Authorization") ?? "", | ||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||
method: req.method, | ||||||||||||||||||||||||||||||||||||
body: req.body, | ||||||||||||||||||||||||||||||||||||
redirect: "manual", | ||||||||||||||||||||||||||||||||||||
Comment on lines
+75
to
+76
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. Ensure Request Body Consistency When Reading Streams The request body ( To fix this, read the body once and reuse it: - body: req.body,
+ const clonedBody = await req.text();
+ body: clonedBody,
...
if (serverConfig.customModels && clonedBody) {
try {
- const clonedBody = await req.text();
- fetchOptions.body = clonedBody;
+ fetchOptions.body = clonedBody; Also applies to: 85-86 |
||||||||||||||||||||||||||||||||||||
// @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.GLM as string, | ||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||
return NextResponse.json( | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
error: true, | ||||||||||||||||||||||||||||||||||||
message: `you are not allowed to use ${jsonBody?.model} model`, | ||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
status: 403, | ||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||||||||||||||
console.error(`[GLM] filter`, e); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+108
to
+111
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. Improve error handling in model validation. The catch block silently logs errors from JSON parsing or model validation. These should be properly handled as they indicate invalid requests. Apply this diff: } catch (e) {
console.error(`[GLM] filter`, e);
+ return NextResponse.json(
+ {
+ error: true,
+ message: "Invalid request format",
+ },
+ {
+ status: 400,
+ }
+ );
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
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.
💡 Codebase verification
GLM configuration is properly implemented but needs documentation
The implementation of GLM API key is correctly structured in the codebase:
server.ts
isGLM
checkHowever, the GLM configuration is missing from the documentation. The developer should:
🔗 Analysis chain
LGTM! Verify server configuration for GLM.
The implementation follows the established pattern for model providers and correctly assigns the GLM API key.
Let's verify the server configuration includes the GLM API key definition:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 189
Script:
Length of output: 615
Script:
Length of output: 2251
Script:
Length of output: 368