diff --git a/src/lib/providers/validation.ts b/src/lib/providers/validation.ts index 4c014a20ff..8e5eafe5de 100644 --- a/src/lib/providers/validation.ts +++ b/src/lib/providers/validation.ts @@ -3743,6 +3743,64 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi return toValidationErrorResult(error); } }, + // Gitlawb Opengateway — Xiaomi MiMo compatible, same /models endpoint limitation. + // Bypass /models probe in favor of chat/completions, matching xiaomi-mimo's pattern. + gitlawb: async ({ apiKey, providerSpecificData }: any) => { + try { + const baseUrl = normalizeBaseUrl( + providerSpecificData?.baseUrl || "https://opengateway.gitlawb.com/v1/xiaomi-mimo" + ); + const chatUrl = `${baseUrl.replace(/\/chat\/completions$/, "")}/chat/completions`; + const res = await validationWrite( + chatUrl, + { + method: "POST", + headers: buildBearerHeaders(apiKey, providerSpecificData), + body: JSON.stringify({ + model: "mimo-v2.5-pro", + messages: [{ role: "user", content: "test" }], + max_tokens: 1, + }), + }, + isLocal + ); + if (res.status === 401 || res.status === 403) { + return { valid: false, error: "Invalid API key" }; + } + // Any non-auth response (200, 400, 422, 429) means auth passed + return { valid: true, error: null }; + } catch (error: any) { + return toValidationErrorResult(error); + } + }, + "gitlawb-gmi": async ({ apiKey, providerSpecificData }: any) => { + try { + const baseUrl = normalizeBaseUrl( + providerSpecificData?.baseUrl || "https://opengateway.gitlawb.com/v1/gmi-cloud" + ); + const chatUrl = `${baseUrl.replace(/\/chat\/completions$/, "")}/chat/completions`; + const res = await validationWrite( + chatUrl, + { + method: "POST", + headers: buildBearerHeaders(apiKey, providerSpecificData), + body: JSON.stringify({ + model: "XiaomiMiMo/MiMo-V2.5-Pro", + messages: [{ role: "user", content: "test" }], + max_tokens: 1, + }), + }, + isLocal + ); + if (res.status === 401 || res.status === 403) { + return { valid: false, error: "Invalid API key" }; + } + // Any non-auth response (200, 400, 422, 429) means auth passed + return { valid: true, error: null }; + } catch (error: any) { + return toValidationErrorResult(error); + } + }, // Search providers — use factored validator ...Object.fromEntries( Object.entries(SEARCH_VALIDATOR_CONFIGS).map(([id, configFn]) => [