From c83814af44081ec786163d3bdb45c40abf949fb2 Mon Sep 17 00:00:00 2001 From: xxapi-ai <349712653+xxapi-ai@users.noreply.github.com> Date: Thu, 29 Oct 2026 01:03:35 -0700 Subject: [PATCH] feat(provider): add xxapi Disabled built-in OpenAI-compatible provider for https://xxapi.ai/v1 with live /models discovery, the xxapi swift icon (mono) and focused defaults and registry tests. --- src/main/provider/defaults.ts | 14 ++++++++++++++ src/main/provider/providerRegistry.ts | 8 ++++++++ src/renderer/src/assets/llm-icons/xxapi.svg | 3 +++ .../src/components/icons/modelIconRegistry.ts | 3 +++ .../provider/basicApiKeyProviders.test.ts | 19 +++++++++++++++++++ test/main/provider/defaultProviders.test.ts | 19 +++++++++++++++++++ 6 files changed, 66 insertions(+) create mode 100644 src/renderer/src/assets/llm-icons/xxapi.svg diff --git a/src/main/provider/defaults.ts b/src/main/provider/defaults.ts index 01a037545..83b2331d4 100644 --- a/src/main/provider/defaults.ts +++ b/src/main/provider/defaults.ts @@ -192,6 +192,20 @@ const PROVIDER_DEFAULTS: LLM_PROVIDER_BASE[] = [ models: 'https://y-api.bestvirtualgoods.com/models' } }, + { + id: 'xxapi', + name: 'xxapi', + apiType: 'openai-completions', + apiKey: '', + baseUrl: 'https://xxapi.ai/v1', + enable: false, + websites: { + official: 'https://xxapi.ai/', + apiKey: 'https://xxapi.ai/keys', + docs: 'https://docs.xxapi.ai/getting-started', + models: 'https://xxapi.ai/pricing' + } + }, { id: 'apimodels', name: 'apimodels.app', diff --git a/src/main/provider/providerRegistry.ts b/src/main/provider/providerRegistry.ts index 06927717d..f029ffb57 100644 --- a/src/main/provider/providerRegistry.ts +++ b/src/main/provider/providerRegistry.ts @@ -349,6 +349,14 @@ const PROVIDER_ID_REGISTRY = new Map([ embeddingStrategy: 'none' }) ], + [ + 'xxapi', + createDefinition({ + ...OPENAI_BASE, + credentialStrategy: 'api-key', + embeddingStrategy: 'none' + }) + ], [ 'apimodels', createDefinition({ diff --git a/src/renderer/src/assets/llm-icons/xxapi.svg b/src/renderer/src/assets/llm-icons/xxapi.svg new file mode 100644 index 000000000..ae51fd42b --- /dev/null +++ b/src/renderer/src/assets/llm-icons/xxapi.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/components/icons/modelIconRegistry.ts b/src/renderer/src/components/icons/modelIconRegistry.ts index 63c712098..0c3c8e5a4 100644 --- a/src/renderer/src/components/icons/modelIconRegistry.ts +++ b/src/renderer/src/components/icons/modelIconRegistry.ts @@ -102,6 +102,7 @@ import cheaperInferenceIcon from '@/assets/llm-icons/cheaper-inference.svg?url' import futureinfraIcon from '@/assets/llm-icons/futureinfra.svg?url' import atmorouterIcon from '@/assets/llm-icons/atmorouter.svg?url' import yapiIcon from '@/assets/llm-icons/yapi.svg?url' +import xxapiIcon from '@/assets/llm-icons/xxapi.svg?url' import apimodelsIcon from '@/assets/llm-icons/apimodels.png?url' import anonrouterIcon from '@/assets/llm-icons/anonrouter.svg?url' import typesafeIcon from '@/assets/llm-icons/typesafe.png?url' @@ -158,6 +159,7 @@ export const modelIcons = { futureinfra: futureinfraIcon, atmorouter: atmorouterIcon, yapi: yapiIcon, + xxapi: xxapiIcon, apimodels: apimodelsIcon, zeabur: zeaburColorIcon, zeroone: zerooneColorIcon, @@ -290,6 +292,7 @@ const monoIconUrls = new Set([ aistupidlevelIcon, runcrateIcon, cloudgpuIcon, + xxapiIcon, demonrouteColorIcon, githubColorIcon, qiniuIcon, diff --git a/test/main/provider/basicApiKeyProviders.test.ts b/test/main/provider/basicApiKeyProviders.test.ts index 24ab4d245..553e41be9 100644 --- a/test/main/provider/basicApiKeyProviders.test.ts +++ b/test/main/provider/basicApiKeyProviders.test.ts @@ -1090,6 +1090,25 @@ describe('basic API-key provider registrations', () => { }) }) + it('resolves xxapi through authenticated OpenAI-compatible model discovery', () => { + expect( + resolveAiSdkProviderDefinition( + createProvider({ + id: 'xxapi', + name: 'xxapi', + baseUrl: 'https://xxapi.ai/v1' + }) + ) + ).toMatchObject({ + runtimeKind: 'openai-compatible', + modelSource: 'openai', + checkStrategy: 'fetch-models', + credentialStrategy: 'api-key', + routeStrategy: 'none', + embeddingStrategy: 'none' + }) + }) + it('resolves API Route through authenticated OpenAI-compatible model discovery', () => { expect( resolveAiSdkProviderDefinition( diff --git a/test/main/provider/defaultProviders.test.ts b/test/main/provider/defaultProviders.test.ts index 31fc3c09c..cb79209d2 100644 --- a/test/main/provider/defaultProviders.test.ts +++ b/test/main/provider/defaultProviders.test.ts @@ -302,6 +302,25 @@ describe('DEFAULT_PROVIDERS', () => { ) }) + it('includes xxapi as a disabled built-in OpenAI-compatible provider', () => { + expect(DEFAULT_PROVIDERS).toContainEqual( + expect.objectContaining({ + id: 'xxapi', + name: 'xxapi', + apiType: 'openai-completions', + baseUrl: 'https://xxapi.ai/v1', + enable: false, + websites: expect.objectContaining({ + official: 'https://xxapi.ai/', + apiKey: 'https://xxapi.ai/keys', + docs: 'https://docs.xxapi.ai/getting-started', + models: 'https://xxapi.ai/pricing', + defaultBaseUrl: 'https://xxapi.ai/v1' + }) + }) + ) + }) + it('includes apimodels.app as a disabled built-in OpenAI-compatible provider', () => { expect(DEFAULT_PROVIDERS).toContainEqual( expect.objectContaining({