Add platform adapters and tests for CLIProxyAPI, OneHub, Sub2API, and title hint detection

- Implemented `CliProxyApiAdapter` with detection logic for CLIProxyAPI.
- Added tests for `CliProxyApiAdapter` to verify detection and response handling.
- Created `OneHubAdapter` with methods for fetching models and user groups.
- Added tests for `OneHubAdapter` to ensure correct fallback and response parsing.
- Developed `Sub2ApiAdapter` with detection and user info fetching capabilities.
- Added comprehensive tests for `Sub2ApiAdapter` covering various scenarios.
- Introduced `TitleHintPlatform` type and detection logic based on HTML title.
- Implemented tests for title hint detection to identify platforms from URLs.
This commit is contained in:
ciat-777
2026-03-02 20:26:02 +08:00
parent 897273fad4
commit 487bd23a02
19 changed files with 2032 additions and 45 deletions
@@ -0,0 +1,116 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
const fetchModelPricingCatalogMock = vi.fn(async (_arg?: unknown) => null);
vi.mock('../../services/modelPricingService.js', () => ({
fetchModelPricingCatalog: (arg: unknown) => fetchModelPricingCatalogMock(arg),
}));
import { resolveUpstreamEndpointCandidates } from './upstreamEndpoint.js';
const baseContext = {
site: {
id: 1,
url: 'https://upstream.example.com',
platform: '',
apiKey: null,
},
account: {
id: 2,
accessToken: 'token-demo',
apiToken: null,
},
};
describe('resolveUpstreamEndpointCandidates', () => {
beforeEach(() => {
fetchModelPricingCatalogMock.mockReset();
fetchModelPricingCatalogMock.mockResolvedValue(null);
});
it('uses responses -> messages -> chat for unknown platforms', async () => {
const openaiOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'new-api' },
},
'gpt-5.3',
'openai',
);
expect(openaiOrder).toEqual(['responses', 'messages', 'chat']);
const claudeOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'new-api' },
},
'gpt-5.3',
'claude',
);
expect(claudeOrder).toEqual(['responses', 'messages', 'chat']);
const responsesOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'new-api' },
},
'gpt-5.3',
'responses',
);
expect(responsesOrder).toEqual(['responses', 'messages', 'chat']);
});
it('keeps explicit platform priority rules', async () => {
const openaiOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'openai' },
},
'gpt-5.3',
'openai',
);
expect(openaiOrder).toEqual(['chat', 'responses']);
const openaiResponsesOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'openai' },
},
'gpt-5.3',
'responses',
);
expect(openaiResponsesOrder).toEqual(['responses', 'chat']);
const claudeOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'claude' },
},
'claude-opus-4-6',
'claude',
);
expect(claudeOrder).toEqual(['messages']);
});
it('keeps anyrouter messages-first special case', async () => {
const openaiOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'anyrouter' },
},
'claude-opus-4-6',
'openai',
);
expect(openaiOrder).toEqual(['messages', 'chat']);
const responsesOrder = await resolveUpstreamEndpointCandidates(
{
...baseContext,
site: { ...baseContext.site, platform: 'anyrouter' },
},
'claude-opus-4-6',
'responses',
);
expect(responsesOrder).toEqual(['responses', 'messages', 'chat']);
});
});
+3 -7
View File
@@ -381,13 +381,9 @@ function preferredEndpointOrder(downstreamFormat: EndpointPreference, sitePlatfo
return ['messages'];
}
if (downstreamFormat === 'responses') {
return ['responses', 'chat', 'messages'];
}
return downstreamFormat === 'claude'
? ['messages', 'chat']
: ['chat', 'messages'];
// Unknown/generic upstreams: try the most expressive protocol first,
// then degrade to anthropic messages, and finally chat completions.
return ['responses', 'messages', 'chat'];
}
export async function resolveUpstreamEndpointCandidates(