mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
feat(provider): add Gitlawb Opengateway provider (xiaomi-mimo + gmi-cloud)
Add two OpenAI-compatible API-key providers via the Gitlawb Opengateway gateway at opengateway.gitlawb.com: - gitlawb (alias glb): xiaomi-mimo endpoint with 5 MiMo models - gitlawb-gmi (alias glb-gmi): gmi-cloud endpoint with 40+ models including GPT-5.x, Claude 4.x, DeepSeek, Gemini, Qwen, GLM, Kimi Both providers include CLI-mimicking headers (User-Agent, X-Title, HTTP-Referer) to avoid upstream rate limiting. GMI Cloud provider has passthroughModels enabled since model access varies per API key.
This commit is contained in:
@@ -422,6 +422,226 @@ const CHAT_OPENAI_COMPAT_MODELS: Record<string, RegistryModel[]> = {
|
||||
{ id: "mimo-v2-omni", name: "MiMo-V2-Omni", contextLength: 262144, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-flash", name: "MiMo-V2-Flash", contextLength: 262144, maxOutputTokens: 65536 },
|
||||
],
|
||||
gitlawb: [
|
||||
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", contextLength: 1048576, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", contextLength: 1048576, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-pro", name: "MiMo-V2-Pro", contextLength: 262144, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-omni", name: "MiMo-V2-Omni", contextLength: 262144, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-flash", name: "MiMo-V2-Flash", contextLength: 262144, maxOutputTokens: 65536 },
|
||||
],
|
||||
"gitlawb-gmi": [
|
||||
{
|
||||
id: "XiaomiMiMo/MiMo-V2.5-Pro",
|
||||
name: "MiMo-V2.5-Pro (GMI)",
|
||||
contextLength: 1050000,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "XiaomiMiMo/MiMo-V2.5",
|
||||
name: "MiMo-V2.5 (GMI)",
|
||||
contextLength: 1050000,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{ id: "openai/gpt-5.5", name: "GPT-5.5", contextLength: 1050000, maxOutputTokens: 131072 },
|
||||
{
|
||||
id: "openai/gpt-5.4-pro",
|
||||
name: "GPT-5.4 Pro",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{ id: "openai/gpt-5.4", name: "GPT-5.4", contextLength: 409600, maxOutputTokens: 131072 },
|
||||
{
|
||||
id: "openai/gpt-5.4-mini",
|
||||
name: "GPT-5.4 Mini",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "openai/gpt-5.4-nano",
|
||||
name: "GPT-5.4 Nano",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "openai/gpt-5.3-codex",
|
||||
name: "GPT-5.3 Codex",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "openai/gpt-5.2-codex",
|
||||
name: "GPT-5.2 Codex",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{ id: "openai/gpt-5.2", name: "GPT-5.2", contextLength: 409600, maxOutputTokens: 131072 },
|
||||
{ id: "openai/gpt-5.1", name: "GPT-5.1", contextLength: 409600, maxOutputTokens: 131072 },
|
||||
{ id: "openai/gpt-5", name: "GPT-5", contextLength: 409600, maxOutputTokens: 131072 },
|
||||
{ id: "openai/gpt-4o", name: "GPT-4o", contextLength: 131072, maxOutputTokens: 16384 },
|
||||
{
|
||||
id: "openai/gpt-4o-mini",
|
||||
name: "GPT-4o Mini",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 16384,
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-opus-4.7",
|
||||
name: "Claude Opus 4.7",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-opus-4.6",
|
||||
name: "Claude Opus 4.6",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-opus-4.5",
|
||||
name: "Claude Opus 4.5",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-opus-4.1",
|
||||
name: "Claude Opus 4.1",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-sonnet-4.6",
|
||||
name: "Claude Sonnet 4.6",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-sonnet-4.5",
|
||||
name: "Claude Sonnet 4.5",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-sonnet-4",
|
||||
name: "Claude Sonnet 4",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "anthropic/claude-haiku-4.5",
|
||||
name: "Claude Haiku 4.5",
|
||||
contextLength: 409600,
|
||||
maxOutputTokens: 131072,
|
||||
targetFormat: "claude",
|
||||
},
|
||||
{
|
||||
id: "deepseek-ai/DeepSeek-V4-Pro",
|
||||
name: "DeepSeek V4 Pro",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "deepseek-ai/DeepSeek-V4-Flash",
|
||||
name: "DeepSeek V4 Flash",
|
||||
contextLength: 1048575,
|
||||
maxOutputTokens: 131072,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "deepseek-ai/DeepSeek-R1-0528",
|
||||
name: "DeepSeek R1",
|
||||
contextLength: 163840,
|
||||
maxOutputTokens: 131072,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "deepseek-ai/DeepSeek-V3.2",
|
||||
name: "DeepSeek V3.2",
|
||||
contextLength: 163840,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "google/gemini-3.1-pro-preview",
|
||||
name: "Gemini 3.1 Pro",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "google/gemini-3.1-flash-lite-preview",
|
||||
name: "Gemini 3.1 Flash Lite",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "google/gemini-3-flash-preview",
|
||||
name: "Gemini 3 Flash",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{ id: "zai-org/GLM-5.1-FP8", name: "GLM-5.1", contextLength: 202752, maxOutputTokens: 131072 },
|
||||
{ id: "zai-org/GLM-5-FP8", name: "GLM-5", contextLength: 202752, maxOutputTokens: 131072 },
|
||||
{
|
||||
id: "moonshotai/Kimi-K2.6",
|
||||
name: "Kimi K2.6",
|
||||
contextLength: 65536,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "moonshotai/Kimi-K2.5",
|
||||
name: "Kimi K2.5",
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "MiniMaxAI/MiniMax-M2.7",
|
||||
name: "MiniMax M2.7",
|
||||
contextLength: 196608,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "MiniMaxAI/MiniMax-M2.5",
|
||||
name: "MiniMax M2.5",
|
||||
contextLength: 196608,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen3.6-Max-Preview",
|
||||
name: "Qwen3.6 Max",
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen3.6-Plus",
|
||||
name: "Qwen3.6 Plus",
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen3.5-397B-A17B",
|
||||
name: "Qwen3.5 397B",
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8",
|
||||
name: "Qwen3 Coder 480B",
|
||||
contextLength: 262128,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
{
|
||||
id: "nvidia/NVIDIA-Nemotron-3-Nano-Omni",
|
||||
name: "Nemotron 3 Nano",
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 131072,
|
||||
},
|
||||
],
|
||||
"inference-net": buildModels([
|
||||
"meta-llama/Llama-3.3-70B-Instruct",
|
||||
"deepseek-ai/DeepSeek-R1",
|
||||
@@ -2785,6 +3005,39 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
models: CHAT_OPENAI_COMPAT_MODELS["xiaomi-mimo"],
|
||||
},
|
||||
|
||||
gitlawb: {
|
||||
id: "gitlawb",
|
||||
alias: "glb",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://opengateway.gitlawb.com/v1/xiaomi-mimo",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
headers: {
|
||||
"User-Agent": "OpenClaude/1.0 (linux; x86_64)",
|
||||
"X-Title": "OpenClaude CLI",
|
||||
"HTTP-Referer": "https://github.com/Gitlawb/openclaude",
|
||||
},
|
||||
models: CHAT_OPENAI_COMPAT_MODELS["gitlawb"],
|
||||
},
|
||||
|
||||
"gitlawb-gmi": {
|
||||
id: "gitlawb-gmi",
|
||||
alias: "glb-gmi",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://opengateway.gitlawb.com/v1/gmi-cloud",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
headers: {
|
||||
"User-Agent": "OpenClaude/1.0 (linux; x86_64)",
|
||||
"X-Title": "OpenClaude CLI",
|
||||
"HTTP-Referer": "https://github.com/Gitlawb/openclaude",
|
||||
},
|
||||
passthroughModels: true,
|
||||
models: CHAT_OPENAI_COMPAT_MODELS["gitlawb-gmi"],
|
||||
},
|
||||
|
||||
"inference-net": {
|
||||
id: "inference-net",
|
||||
alias: "inet",
|
||||
|
||||
@@ -1228,6 +1228,30 @@ export const APIKEY_PROVIDERS = {
|
||||
textIcon: "MM",
|
||||
website: "https://mimo.mi.com",
|
||||
},
|
||||
gitlawb: {
|
||||
id: "gitlawb",
|
||||
alias: "glb",
|
||||
name: "Gitlawb Opengateway (MiMo)",
|
||||
icon: "hub",
|
||||
color: "#10B981",
|
||||
textIcon: "GLB",
|
||||
website: "https://opengateway.gitlawb.com",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier available — no credit card required",
|
||||
apiHint: "Get your API key from Gitlawb Opengateway dashboard.",
|
||||
},
|
||||
"gitlawb-gmi": {
|
||||
id: "gitlawb-gmi",
|
||||
alias: "glb-gmi",
|
||||
name: "Gitlawb Opengateway (GMI Cloud)",
|
||||
icon: "hub",
|
||||
color: "#10B981",
|
||||
textIcon: "GMI",
|
||||
website: "https://opengateway.gitlawb.com",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier available — no credit card required",
|
||||
apiHint: "Get your API key from Gitlawb Opengateway dashboard.",
|
||||
},
|
||||
"inference-net": {
|
||||
id: "inference-net",
|
||||
alias: "inet",
|
||||
|
||||
101
tests/unit/gitlawb-provider.test.ts
Normal file
101
tests/unit/gitlawb-provider.test.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert";
|
||||
|
||||
describe("Gitlawb Opengateway providers", () => {
|
||||
describe("gitlawb (xiaomi-mimo)", () => {
|
||||
it("should be registered in APIKEY_PROVIDERS", async () => {
|
||||
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
const provider = APIKEY_PROVIDERS["gitlawb"];
|
||||
assert.ok(provider, "gitlawb should exist in APIKEY_PROVIDERS");
|
||||
assert.strictEqual(provider.id, "gitlawb");
|
||||
assert.strictEqual(provider.alias, "glb");
|
||||
assert.ok(provider.name.includes("Gitlawb"));
|
||||
assert.strictEqual(provider.hasFree, true);
|
||||
});
|
||||
|
||||
it("should have registry entry with correct baseUrl", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb"];
|
||||
assert.ok(entry, "gitlawb should exist in REGISTRY");
|
||||
assert.strictEqual(entry.baseUrl, "https://opengateway.gitlawb.com/v1/xiaomi-mimo");
|
||||
assert.strictEqual(entry.format, "openai");
|
||||
assert.strictEqual(entry.executor, "default");
|
||||
assert.strictEqual(entry.authType, "apikey");
|
||||
});
|
||||
|
||||
it("should have CLI-mimicking headers", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb"];
|
||||
assert.ok(entry.headers, "should have headers");
|
||||
assert.ok(entry.headers["User-Agent"].includes("OpenClaude"));
|
||||
assert.strictEqual(entry.headers["X-Title"], "OpenClaude CLI");
|
||||
assert.ok(entry.headers["HTTP-Referer"].includes("Gitlawb/openclaude"));
|
||||
});
|
||||
|
||||
it("should list MiMo models", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb"];
|
||||
assert.ok(
|
||||
entry.models.length >= 5,
|
||||
`should have at least 5 models, got ${entry.models.length}`
|
||||
);
|
||||
const pro = entry.models.find((m: any) => m.id === "mimo-v2.5-pro");
|
||||
assert.ok(pro, "mimo-v2.5-pro should be listed");
|
||||
assert.strictEqual(pro.contextLength, 1048576);
|
||||
});
|
||||
});
|
||||
|
||||
describe("gitlawb-gmi (gmi-cloud)", () => {
|
||||
it("should be registered in APIKEY_PROVIDERS", async () => {
|
||||
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
const provider = APIKEY_PROVIDERS["gitlawb-gmi"];
|
||||
assert.ok(provider, "gitlawb-gmi should exist in APIKEY_PROVIDERS");
|
||||
assert.strictEqual(provider.id, "gitlawb-gmi");
|
||||
assert.strictEqual(provider.alias, "glb-gmi");
|
||||
assert.strictEqual(provider.hasFree, true);
|
||||
});
|
||||
|
||||
it("should have registry entry with gmi-cloud baseUrl", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb-gmi"];
|
||||
assert.ok(entry, "gitlawb-gmi should exist in REGISTRY");
|
||||
assert.strictEqual(entry.baseUrl, "https://opengateway.gitlawb.com/v1/gmi-cloud");
|
||||
assert.strictEqual(entry.format, "openai");
|
||||
assert.strictEqual(entry.authType, "apikey");
|
||||
});
|
||||
|
||||
it("should list GPT, Claude, DeepSeek, Gemini models", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb-gmi"];
|
||||
assert.ok(entry.models.length >= 30, `should have 30+ models, got ${entry.models.length}`);
|
||||
|
||||
const modelIds = entry.models.map((m: any) => m.id);
|
||||
assert.ok(modelIds.includes("openai/gpt-5.5"), "should have GPT-5.5");
|
||||
assert.ok(
|
||||
modelIds.some((id: string) => id.startsWith("anthropic/claude")),
|
||||
"should have Claude models"
|
||||
);
|
||||
assert.ok(
|
||||
modelIds.some((id: string) => id.startsWith("deepseek-ai/")),
|
||||
"should have DeepSeek models"
|
||||
);
|
||||
assert.ok(
|
||||
modelIds.some((id: string) => id.startsWith("google/gemini")),
|
||||
"should have Gemini models"
|
||||
);
|
||||
});
|
||||
|
||||
it("should have CLI-mimicking headers", async () => {
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const entry = REGISTRY["gitlawb-gmi"];
|
||||
assert.ok(entry.headers, "should have headers");
|
||||
assert.ok(entry.headers["User-Agent"].includes("OpenClaude"));
|
||||
});
|
||||
});
|
||||
|
||||
it("both providers should pass schema validation", async () => {
|
||||
const { AI_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
assert.ok(AI_PROVIDERS["gitlawb"], "gitlawb should be in AI_PROVIDERS");
|
||||
assert.ok(AI_PROVIDERS["gitlawb-gmi"], "gitlawb-gmi should be in AI_PROVIDERS");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user