-
Notifications
You must be signed in to change notification settings - Fork 59.8k
xAi support #5704
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
xAi support #5704
Changes from all commits
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 { | ||
| XAI_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("[XAI Route] params ", params); | ||
|
|
||
| if (req.method === "OPTIONS") { | ||
| return NextResponse.json({ body: "OK" }, { status: 200 }); | ||
| } | ||
|
|
||
| const authResult = auth(req, ModelProvider.XAI); | ||
| if (authResult.error) { | ||
| return NextResponse.json(authResult, { | ||
| status: 401, | ||
| }); | ||
| } | ||
|
|
||
| try { | ||
| const response = await request(req); | ||
| return response; | ||
| } catch (e) { | ||
| console.error("[XAI] ", e); | ||
| return NextResponse.json(prettyObject(e)); | ||
| } | ||
|
lloydzhou marked this conversation as resolved.
|
||
| } | ||
|
|
||
| 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.XAI, ""); | ||
|
|
||
| let baseUrl = serverConfig.xaiUrl || XAI_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); | ||
|
lloydzhou marked this conversation as resolved.
|
||
|
|
||
| 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") ?? "", | ||
| }, | ||
|
lloydzhou marked this conversation as resolved.
|
||
| method: req.method, | ||
| body: req.body, | ||
| redirect: "manual", | ||
| // @ts-ignore | ||
| duplex: "half", | ||
|
lloydzhou marked this conversation as resolved.
|
||
| signal: controller.signal, | ||
| }; | ||
|
lloydzhou marked this conversation as resolved.
|
||
|
|
||
| // #1815 try to refuse some request to some models | ||
| if (serverConfig.customModels && req.body) { | ||
| try { | ||
| const clonedBody = await req.text(); | ||
| fetchOptions.body = clonedBody; | ||
|
lloydzhou marked this conversation as resolved.
|
||
|
|
||
| const jsonBody = JSON.parse(clonedBody) as { model?: string }; | ||
|
|
||
| // not undefined and is false | ||
| if ( | ||
| isModelAvailableInServer( | ||
| serverConfig.customModels, | ||
| jsonBody?.model as string, | ||
| ServiceProvider.XAI as string, | ||
| ) | ||
| ) { | ||
| return NextResponse.json( | ||
| { | ||
| error: true, | ||
| message: `you are not allowed to use ${jsonBody?.model} model`, | ||
| }, | ||
| { | ||
| status: 403, | ||
| }, | ||
| ); | ||
| } | ||
| } catch (e) { | ||
| console.error(`[XAI] filter`, e); | ||
| } | ||
| } | ||
|
lloydzhou marked this conversation as resolved.
|
||
| 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); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ import { QwenApi } from "./platforms/alibaba"; | |
| import { HunyuanApi } from "./platforms/tencent"; | ||
| import { MoonshotApi } from "./platforms/moonshot"; | ||
| import { SparkApi } from "./platforms/iflytek"; | ||
| import { XAIApi } from "./platforms/xai"; | ||
|
|
||
| export const ROLES = ["system", "user", "assistant"] as const; | ||
| export type MessageRole = (typeof ROLES)[number]; | ||
|
|
@@ -152,6 +153,9 @@ export class ClientApi { | |
| case ModelProvider.Iflytek: | ||
| this.llm = new SparkApi(); | ||
| break; | ||
| case ModelProvider.XAI: | ||
| this.llm = new XAIApi(); | ||
| break; | ||
| default: | ||
| this.llm = new ChatGPTApi(); | ||
| } | ||
|
|
@@ -239,6 +243,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { | |
| const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba; | ||
| const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot; | ||
| const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek; | ||
| const isXAI = modelConfig.providerName === ServiceProvider.XAI; | ||
|
Contributor
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 Consider refactoring the complex ternary chain for better maintainability. While the XAI integration is correct, the nested ternary operator chain is becoming increasingly complex and harder to maintain. This could lead to bugs when adding future providers. Consider refactoring to use a more maintainable approach: function getApiKey(modelConfig: ModelConfig, accessStore: AccessStore): string {
const providerApiKeys = {
[ServiceProvider.Google]: accessStore.googleApiKey,
[ServiceProvider.Azure]: accessStore.azureApiKey,
[ServiceProvider.Anthropic]: accessStore.anthropicApiKey,
[ServiceProvider.ByteDance]: accessStore.bytedanceApiKey,
[ServiceProvider.Alibaba]: accessStore.alibabaApiKey,
[ServiceProvider.Moonshot]: accessStore.moonshotApiKey,
[ServiceProvider.XAI]: accessStore.xaiApiKey,
[ServiceProvider.Iflytek]: accessStore.iflytekApiKey && accessStore.iflytekApiSecret
? `${accessStore.iflytekApiKey}:${accessStore.iflytekApiSecret}`
: "",
[ServiceProvider.Default]: accessStore.openaiApiKey,
};
return providerApiKeys[modelConfig.providerName] ?? providerApiKeys.Default;
}Also applies to: 260-261, 276-276 |
||
| const isEnabledAccessControl = accessStore.enabledAccessControl(); | ||
| const apiKey = isGoogle | ||
| ? accessStore.googleApiKey | ||
|
|
@@ -252,6 +257,8 @@ export function getHeaders(ignoreHeaders: boolean = false) { | |
| ? accessStore.alibabaApiKey | ||
| : isMoonshot | ||
| ? accessStore.moonshotApiKey | ||
| : isXAI | ||
| ? accessStore.xaiApiKey | ||
| : isIflytek | ||
| ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret | ||
| ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret | ||
|
|
@@ -266,6 +273,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { | |
| isAlibaba, | ||
| isMoonshot, | ||
| isIflytek, | ||
| isXAI, | ||
| apiKey, | ||
| isEnabledAccessControl, | ||
| }; | ||
|
|
@@ -328,6 +336,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi { | |
| return new ClientApi(ModelProvider.Moonshot); | ||
| case ServiceProvider.Iflytek: | ||
| return new ClientApi(ModelProvider.Iflytek); | ||
| case ServiceProvider.XAI: | ||
| return new ClientApi(ModelProvider.XAI); | ||
| default: | ||
| return new ClientApi(ModelProvider.GPT); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.