Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/src/components/keys/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const PLATFORMS: { value: Platform; label: string; url: string; keyless?:
{ value: 'nara', label: 'NaraRouter (free key)', url: 'https://router.bynara.id' },
{ value: 'sealion', label: 'SEA-LION (free key)', url: 'https://sea-lion.ai' },
{ value: 'orcarouter', label: 'OrcaRouter (free key)', url: 'https://www.orcarouter.ai' },
{ value: 'xkiro', label: 'xkiro (free key)', url: 'https://xkiro.com' },
// AnyAPI advertises 100K tokens/day free, but live testing on 2026-08-10
// could not get a single free-tier request served (see
// CATALOG-ANYAPI-SMOKE-2026-08-10 in the ops repo). No quota claim until
Expand Down
1 change: 1 addition & 0 deletions client/src/lib/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ export const platformColors: Record<string, string> = {
nara: '#2563eb',
sealion: '#0ea5e9',
orcarouter: '#f97316',
xkiro: '#a855f7',
anyapi: '#0891b2',
modelscope: '#624aff',
aihorde: '#dc2626',
Expand Down
1 change: 1 addition & 0 deletions server/src/__tests__/lib/key-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ describe('key parser', () => {
expect(detectPlatform('SEALION_')).toBe('sealion');
expect(detectPlatform('ORCAROUTER_')).toBe('orcarouter');
expect(detectPlatform('ORCA_')).toBe('orcarouter');
expect(detectPlatform('XKIRO_')).toBe('xkiro');
expect(detectPlatform('MODELSCOPE_')).toBe('modelscope');
expect(detectPlatform('ANYAPI_')).toBe('anyapi');
expect(detectPlatform('ANY_API_')).toBe('anyapi');
Expand Down
3 changes: 3 additions & 0 deletions server/src/__tests__/providers/openai-compat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,9 @@ describe('OpenAICompatProvider - platform instances', () => {
{ platform: 'nara', name: 'NaraRouter', baseUrl: 'https://router.bynara.id/v1' },
{ platform: 'sealion', name: 'SEA-LION', baseUrl: 'https://api.sea-lion.ai/v1' },
{ platform: 'orcarouter', name: 'OrcaRouter', baseUrl: 'https://api.orcarouter.ai/v1' },
// xkiro validates against /v1/usage (its /v1/models is public — 200 with no
// key), so it carries a validateUrl; chat routing is stock openai-compat.
{ platform: 'xkiro', name: 'xkiro', baseUrl: 'https://xkiro.com/v1' },
// modelscope registers a ModelScopeProvider subclass (custom validateKey,
// see providers/modelscope.test.ts) but chat routing is stock openai-compat.
{ platform: 'modelscope', name: 'ModelScope', baseUrl: 'https://api-inference.modelscope.cn/v1' },
Expand Down
2 changes: 2 additions & 0 deletions server/src/lib/key-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const PREFIX_MAP: Record<string, string> = {
ORCAROUTER_: 'orcarouter',
ORCA_ROUTER_: 'orcarouter',
ORCA_: 'orcarouter',
XKIRO_: 'xkiro',
MODELSCOPE_: 'modelscope',
MODEL_SCOPE_: 'modelscope',
ANYAPI_: 'anyapi',
Expand Down Expand Up @@ -122,6 +123,7 @@ export const AUTH_JSON_PROVIDER_MAP: Record<string, string> = {
orcarouter: 'orcarouter',
'orca-router': 'orcarouter',
orca: 'orcarouter',
xkiro: 'xkiro',
modelscope: 'modelscope',
'model-scope': 'modelscope',
anyapi: 'anyapi',
Expand Down
17 changes: 17 additions & 0 deletions server/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,23 @@ register(new OpenAICompatProvider({
baseUrl: 'https://api.orcarouter.ai/v1',
}));

// xkiro.com — OpenAI-compatible gateway (https://xkiro.com/v1). Free key from
// xkiro.com (no card); advertises a free tier alongside paid top-ups.
// Live-probed 2026-08-22: GET /v1/models answers 200 with NO key (public
// catalog), so the default /v1/models key validation would be a false
// positive — validateUrl points at /v1/usage instead, which 401s on a missing
// or invalid ClientApiKey ("Invalid or disabled ClientApiKey"). Accepts
// Authorization: Bearer or x-api-key. Catalog rows live in the hosted catalog
// (premium now, free after the 30-day model-age gate); the ids in #947 are
// unverified against a live account and are candidates for catalog authoring,
// where a bad id is caught by the health check instead of shipped as a default.
register(new OpenAICompatProvider({
platform: 'xkiro',
name: 'xkiro',
baseUrl: 'https://xkiro.com/v1',
validateUrl: 'https://xkiro.com/v1/usage',
}));

// ModelScope (魔搭社区, Alibaba) — OpenAI-compatible inference API
// (api-inference.modelscope.cn/v1, Bearer auth). Free tier: 2000 requests/day
// account-wide. Token from modelscope.cn/my/myaccesstoken, BUT calls only work
Expand Down
2 changes: 1 addition & 1 deletion server/src/routes/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const PLATFORMS = [
'google', 'groq', 'cerebras', 'bai', 'nvidia', 'mistral',
'openrouter', 'github', 'cohere', 'cloudflare', 'zhipu', 'ollama',
'kilo', 'pollinations', 'llm7', 'huggingface', 'opencode', 'ovh', 'agnes', 'reka', 'siliconflow',
'routeway', 'bazaarlink', 'ainative', 'aion', 'anyapi', 'requesty', 'navy', 'nara', 'sealion', 'orcarouter', 'modelscope',
'routeway', 'bazaarlink', 'ainative', 'aion', 'anyapi', 'requesty', 'navy', 'nara', 'sealion', 'orcarouter', 'xkiro', 'modelscope',
'qianfan', 'volcengine', 'longcat', 'xfyun', 'aihorde', 'custom',
] as const;

Expand Down
5 changes: 4 additions & 1 deletion server/src/services/provider-quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ function inferPoolForPlatform(platform: Platform, modelId?: string | null): stri
// OrcaRouter: one rate-limited free allowance across all `*-free` aliases
// and the `orcarouter/free` auto route (limits unpublished; 429 on cap).
if (platform === 'orcarouter') return 'orcarouter::free';
// xkiro: one account-level allowance shared across its free models (the
// free tier is a per-account grant, not per-model), so one pool.
if (platform === 'xkiro') return 'xkiro::free';
// AnyAPI: the free tier is one 100K-tokens/day budget for the whole account,
// shared across every free/basic model — so one pool, not one per model.
if (platform === 'anyapi') return 'anyapi::free';
Expand All @@ -154,7 +157,7 @@ function inferPoolForPlatform(platform: Platform, modelId?: string | null): stri
}

function isSharedPool(platform: Platform): boolean {
return ['openrouter', 'google', 'groq', 'cerebras', 'bai', 'sambanova', 'nvidia', 'mistral', 'github', 'cohere', 'cloudflare', 'zhipu', 'ollama', 'kilo', 'pollinations', 'llm7', 'huggingface', 'opencode', 'routeway', 'bazaarlink', 'ainative', 'aion', 'requesty', 'navy', 'nara', 'sealion', 'orcarouter', 'anyapi', 'modelscope', 'aihorde'].includes(platform);
return ['openrouter', 'google', 'groq', 'cerebras', 'bai', 'sambanova', 'nvidia', 'mistral', 'github', 'cohere', 'cloudflare', 'zhipu', 'ollama', 'kilo', 'pollinations', 'llm7', 'huggingface', 'opencode', 'routeway', 'bazaarlink', 'ainative', 'aion', 'requesty', 'navy', 'nara', 'sealion', 'orcarouter', 'xkiro', 'anyapi', 'modelscope', 'aihorde'].includes(platform);
}

type HeaderSpec = { metric: QuotaMetric; limit: string; remaining?: string; reset?: string; strategy?: QuotaResetStrategy };
Expand Down
6 changes: 6 additions & 0 deletions shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ export type Platform =
// (never fall back to paid). Catalog rows live in the Oracle catalog
// (premium now, free after the 30-day model-age gate).
| 'orcarouter'
// xkiro.com — OpenAI-compatible gateway (https://xkiro.com/v1). Free key
// from xkiro.com (no card); advertises a free tier alongside paid top-ups.
// /v1/models is public (200 with no key), so key validation must probe
// /v1/usage, which 401s on a missing/invalid ClientApiKey. Catalog rows live
// in the hosted catalog (premium now, free after the 30-day model-age gate).
| 'xkiro'
// ModelScope (魔搭社区, Alibaba) — OpenAI-compatible inference API
// (api-inference.modelscope.cn/v1). Free tier is 2000 requests/day
// account-wide, but calls only work after the ModelScope account is bound to
Expand Down
Loading