mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 18:18:21 +00:00
Merge pull request #723 from Alex-wuhu/novita-integration
feat: add Novita AI provider preset
This commit is contained in:
5 files changed
+81
-1
No files matched your search
@@ -110,6 +110,7 @@ The dashboard provides visual management for all account types:
|
||||
| **Azure Foundry** | API Key | `ccs foundry` | Claude via Microsoft Azure |
|
||||
| **Minimax** | API Key | `ccs mm` | M2 series, 1M context |
|
||||
| **DeepSeek** | API Key | `ccs deepseek` | V3.2 and R1 reasoning |
|
||||
| **Novita AI** | API Key | `ccs api create --preset novita` | Anthropic-compatible Novita endpoint for Claude Code |
|
||||
| **Qwen (OAuth)** | OAuth | `ccs qwen` | Qwen Code via CLIProxy |
|
||||
| **Qwen API** | API Key | `ccs api create --preset qwen` | DashScope Anthropic-compatible API |
|
||||
| **Alibaba Coding Plan** | API Key | `ccs api create --preset alibaba-coding-plan` | Model Studio Coding Plan endpoint |
|
||||
|
||||
@@ -21,6 +21,7 @@ export const PROVIDER_PRESET_IDS = [
|
||||
'deepseek',
|
||||
'qwen',
|
||||
'ollama-cloud',
|
||||
'novita',
|
||||
] as const;
|
||||
|
||||
export type ProviderPresetId = (typeof PROVIDER_PRESET_IDS)[number];
|
||||
@@ -254,6 +255,20 @@ const RAW_PROVIDER_PRESET_DEFINITIONS: readonly ProviderPresetDefinition[] = [
|
||||
badge: 'Cloud',
|
||||
icon: '/icons/ollama.svg',
|
||||
},
|
||||
{
|
||||
id: 'novita',
|
||||
name: 'Novita AI',
|
||||
description: 'Anthropic-compatible API for Claude Code and CCS profiles',
|
||||
baseUrl: 'https://api.novita.ai/anthropic',
|
||||
defaultProfileName: 'novita',
|
||||
defaultModel: 'deepseek/deepseek-v3.2',
|
||||
apiKeyPlaceholder: 'YOUR_NOVITA_API_KEY',
|
||||
apiKeyHint: 'Get your API key at novita.ai',
|
||||
category: 'alternative',
|
||||
requiresApiKey: true,
|
||||
badge: 'Anthropic-compatible',
|
||||
icon: '/icons/novita.svg',
|
||||
},
|
||||
];
|
||||
|
||||
function clonePresetDefinition(preset: ProviderPresetDefinition): ProviderPresetDefinition {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { getPresetById, isValidPresetId } from '../../../src/api/services/provider-presets';
|
||||
|
||||
describe('provider-presets-novita', () => {
|
||||
it('resolves novita preset id', () => {
|
||||
const preset = getPresetById('novita');
|
||||
expect(preset?.id).toBe('novita');
|
||||
expect(preset?.baseUrl).toBe('https://api.novita.ai/anthropic');
|
||||
expect(preset?.defaultProfileName).toBe('novita');
|
||||
});
|
||||
|
||||
it('resolves novita preset with expected model IDs', () => {
|
||||
const preset = getPresetById('novita');
|
||||
expect(preset?.defaultModel).toBe('deepseek/deepseek-v3.2');
|
||||
});
|
||||
|
||||
it('validates novita preset requires API key', () => {
|
||||
const preset = getPresetById('novita');
|
||||
expect(preset?.requiresApiKey).toBe(true);
|
||||
});
|
||||
|
||||
it('treats novita as a valid preset id', () => {
|
||||
expect(isValidPresetId('novita')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles whitespace in novita preset id', () => {
|
||||
const preset = getPresetById(' novita ');
|
||||
expect(preset?.id).toBe('novita');
|
||||
});
|
||||
|
||||
it('handles uppercase novita preset id', () => {
|
||||
const preset = getPresetById('NOVITA');
|
||||
expect(preset?.id).toBe('novita');
|
||||
});
|
||||
|
||||
it('does not resolve partial or invalid novita ids', () => {
|
||||
expect(getPresetById('novita-invalid')).toBeUndefined();
|
||||
expect(isValidPresetId('novita-invalid')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,11 @@
|
||||
import { existsSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { getPresetById, isValidPresetId } from '../../../src/api/services/provider-presets';
|
||||
import {
|
||||
PROVIDER_PRESETS,
|
||||
getPresetById,
|
||||
isValidPresetId,
|
||||
} from '../../../src/api/services/provider-presets';
|
||||
|
||||
describe('provider-presets', () => {
|
||||
it('resolves Alibaba Coding Plan preset id', () => {
|
||||
@@ -54,4 +60,13 @@ describe('provider-presets', () => {
|
||||
const preset = getPresetById('qwen');
|
||||
expect(preset?.defaultProfileName).toBe('qwen-api');
|
||||
});
|
||||
|
||||
it('only references provider preset icons that exist in ui/public', () => {
|
||||
for (const preset of PROVIDER_PRESETS) {
|
||||
if (!preset.icon) continue;
|
||||
|
||||
const iconPath = resolve(import.meta.dir, '../../../ui/public', preset.icon.replace(/^\/+/, ''));
|
||||
expect(existsSync(iconPath)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Novita AI</title>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M9.167 4.17v5.665L0 19.003h9.167v-5.666l5.666 5.666H24L9.167 4.17z"
|
||||
fill="#23D57C"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
Reference in new issue
Block a user