feat: Implement provider groupings with account calculator and compre… (#113)

* feat: Implement provider groupings with account calculator and comprehensive fixes

- Add provider detection utilities (detectProvider, PROVIDER_REGISTRY) for Claude/Gemini/Others categorization
- Implement grouping logic (groupModelsByProvider, calculateProviderStats, calculateAccountStats) with health status calculation
- Create useProviderGrouping hook for collapsible state management and persistence
- Add ProviderGroup component with health indicators, progress bars, and model visibility integration
- Enhance CloudAccountList with account-level calculator showing overall percentage and earliest reset time
- Implement grid layout selector (auto/2-col/3-col/list) with config persistence and tooltips
- Add global quota aggregate bar across all accounts in top statistics
- Fix model visibility settings regression by accessing account.quota.models instead of account.quota
- Show 'Rate Limited' instead of '0%' for exhausted quotas with consistent getQuotaLabel function
- Add comprehensive i18n translations for layout selector and rate limited labels (en, zh-CN, ru)
- Disable batch operations when provider groupings are enabled to prevent conflicts
- Include unit tests for provider detection logic and grouping functionality

Addresses critical issues: model list regression, rate limited display, layout control, global quota visibility, and consistent quota labeling across all components.

* fix: resolve ESLint no-ex-assign violations in proxy service

- Fix exception parameter reassignment in catch blocks
- Rename catch parameters to avoid no-ex-assign rule violations
- Maintain same error handling logic while complying with linting rules

* fix: add missing Models tab to settings page

- Add Models tab between General and Proxy tabs
- Import and integrate ModelVisibilitySettings component
- Fix critical regression where model visibility settings were inaccessible

* fix: add missing React hook imports in settings route

* test: remove obsolete tests for removed controller APIs

---------

Co-authored-by: Draculabo <[email protected]>
This commit is contained in:
ArtemisAIandDraculabo authored and GitHub committed 2026-02-18 16:38:15 +08:00
1 parent 118fad567e
commit 1e59a1c901
12 files changed
+1237 -97

No files matched your search

@@ -12,22 +12,6 @@ function createReplyMock() {
}
describe('ProxyController Integration', () => {
it('supports models listing endpoints', () => {
const proxyService = {};
const controller = new ProxyController(proxyService as any);
const allModels = controller.getModels();
const claudeModels = controller.getClaudeModels();
expect(allModels.object).toBe('list');
expect(Array.isArray(allModels.data)).toBe(true);
expect(allModels.data.length).toBeGreaterThan(0);
expect(allModels.data.every((model) => model.owned_by === 'antigravity')).toBe(true);
expect(allModels.data.every((model) => model.created === 1770652800)).toBe(true);
expect(claudeModels).toEqual(allModels);
});
it('routes Claude OpenAI requests to protocol parity path', async () => {
const proxyService = {
handleChatCompletions: vi.fn().mockResolvedValue({ ok: true }),
@@ -439,24 +423,6 @@ describe('ProxyController Integration', () => {
expect(reply.send).toHaveBeenCalledWith('Invalid `boundary` for `multipart/form-data` request');
});
it('supports model detect endpoint', () => {
const proxyService = {
detectModelCapabilities: vi.fn().mockReturnValue({
model: 'claude-sonnet-4-5',
mapped_model: 'gemini-3-pro-preview',
type: 'generate-content',
features: { has_web_search: false, is_image_gen: false },
}),
};
const controller = new ProxyController(proxyService as any);
const reply = createReplyMock();
controller.detectModel({ model: 'claude-sonnet-4-5' }, reply as any);
expect(proxyService.detectModelCapabilities).toHaveBeenCalledWith('claude-sonnet-4-5');
expect(reply.status).toHaveBeenCalledWith(200);
});
it('supports Anthropic messages endpoint', async () => {
const proxyService = {
handleAnthropicMessages: vi.fn().mockResolvedValue({