Skip to content
Merged
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
176 changes: 175 additions & 1 deletion resources/model-db/providers.json
Original file line number Diff line number Diff line change
Expand Up @@ -184267,6 +184267,180 @@
"type": "chat"
}
]
},

"novita": {
"id": "novita",
"name": "Novita AI",
"display_name": "Novita AI",
"api": "https://api.novita.ai/openai",
"doc": "https://novita.ai/docs",
"models": [
{
"id": "moonshotai/kimi-k2.5",
"name": "Kimi K2.5",
"display_name": "Kimi K2.5",
"modalities": {
"input": [
"text",
"image",
"video"
],
"output": [
"text"
]
},
"limit": {
"context": 262144,
"output": 262144
},
"temperature": true,
"tool_call": true,
"reasoning": {
"supported": true,
"default": true
},
"extra_capabilities": {
"reasoning": {
"supported": true,
"interleaved": true,
"summaries": true,
"visibility": "summary",
"continuation": [
"thinking_blocks"
]
}
},
"attachment": true,
"open_weights": true,
"release_date": "2026-01-27",
"last_updated": "2026-01-27",
"cost": {
"input": 0.6,
"output": 3,
"cache_read": 0.1
},
"type": "chat"
},
{
"id": "zai-org/glm-5",
"name": "GLM-5",
"display_name": "GLM-5",
"modalities": {
"input": [
"text"
],
"output": [
"text"
]
},
"limit": {
"context": 202800,
"output": 131072
},
"temperature": true,
"tool_call": true,
"reasoning": {
"supported": true,
"default": true
},
"extra_capabilities": {
"reasoning": {
"supported": true,
"interleaved": true,
"summaries": true,
"visibility": "summary",
"continuation": [
"thinking_blocks"
]
}
},
"attachment": false,
"open_weights": true,
"release_date": "2026-02-11",
"last_updated": "2026-02-11",
"cost": {
"input": 1,
"output": 3.2,
"cache_read": 0.2
},
"type": "chat"
},
{
"id": "minimax/minimax-m2.5",
"name": "MiniMax M2.5",
"display_name": "MiniMax M2.5",
"modalities": {
"input": [
"text"
],
"output": [
"text"
]
},
"limit": {
"context": 204800,
"output": 131100
},
"temperature": true,
"tool_call": true,
"reasoning": {
"supported": true,
"default": true
},
"extra_capabilities": {
"reasoning": {
"supported": true,
"interleaved": true,
"summaries": true,
"visibility": "summary",
"continuation": [
"thinking_blocks"
]
}
},
"attachment": false,
"open_weights": true,
"release_date": "2026-02-12",
"last_updated": "2026-02-12",
"cost": {
"input": 0.3,
"output": 1.2,
"cache_read": 0.03
},
"type": "chat"
},
{
"id": "qwen/qwen3-embedding-0.6b",
"name": "Qwen3 Embedding 0.6B",
"display_name": "Qwen3 Embedding 0.6B",
"modalities": {
"input": [
"text"
],
"output": [
"text"
]
},
"limit": {
"context": 8192,
"output": 1024
},
"temperature": false,
"tool_call": false,
"reasoning": {
"supported": false
},
"attachment": false,
"open_weights": true,
"release_date": "2025-06-01",
"last_updated": "2025-06-01",
"cost": {
"input": 0.014
},
"type": "embedding"
}
]
}
}
}
}
15 changes: 15 additions & 0 deletions src/main/presenter/configPresenter/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,5 +776,20 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [
models: 'https://o3.fan/info/models',
defaultBaseUrl: 'https://api.o3.fan/v1'
}
},
{
id: 'novita',
name: 'Novita AI',
apiType: 'openai-completions',
apiKey: '',
baseUrl: 'https://api.novita.ai/openai',
enable: false,
websites: {
official: 'https://novita.ai/',
apiKey: 'https://novita.ai/',
docs: 'https://novita.ai/docs',
models: 'https://novita.ai/models',
defaultBaseUrl: 'https://api.novita.ai/openai'
}
}
]
3 changes: 3 additions & 0 deletions src/renderer/src/assets/llm-icons/novitaai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/renderer/src/components/icons/ModelIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import burncloudColorIcon from '@/assets/llm-icons/burncloud-color.svg?url'
import xiaomiColorIcon from '@/assets/llm-icons/xiaomi.png?url'
import o3fanColorIcon from '@/assets/llm-icons/o3-fan.png?url'
import voiceAiColorIcon from '@/assets/llm-icons/voiceai.svg?url'
import novitaAiIcon from '@/assets/llm-icons/novitaai.svg?url'

// 导入所有图标
const icons = {
Expand Down Expand Up @@ -157,6 +158,9 @@ const icons = {
burncloud: burncloudColorIcon,
xiaomi: xiaomiColorIcon,
voiceai: voiceAiColorIcon,
novita: novitaAiIcon,
novitaai: novitaAiIcon,
'novita.ai': novitaAiIcon,
default: defaultIcon
}

Expand Down Expand Up @@ -244,7 +248,8 @@ const monoIconUrls = new Set([
lmstudioColorIcon,
_302aiIcon,
awsBedrockIcon,
voiceAiColorIcon
voiceAiColorIcon,
novitaAiIcon
])

const invert = computed(() => {
Expand Down
25 changes: 25 additions & 0 deletions test/main/presenter/llmProviderPresenter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, vi, beforeAll, afterEach } from 'vite
import { LLMProviderPresenter } from '../../../src/main/presenter/llmProviderPresenter/index'
import { ConfigPresenter } from '../../../src/main/presenter/configPresenter/index'
import { LLM_PROVIDER, ChatMessage, ISQLitePresenter } from '../../../src/shared/presenter'
import { OpenAICompatibleProvider } from '../../../src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider'

// Ensure electron is mocked for this suite to avoid CJS named export issues
vi.mock('electron', () => {
Expand Down Expand Up @@ -214,6 +215,30 @@ describe('LLMProviderPresenter Integration Tests', () => {
const currentProvider = llmProviderPresenter.getCurrentProvider()
expect(currentProvider?.id).toBe('mock-openai-api')
})

it('should resolve novita via apiType fallback without an id-specific provider mapping', () => {
const novitaProvider: LLM_PROVIDER = {
id: 'novita',
name: 'Novita AI',
apiType: 'openai-completions',
apiKey: 'deepchatIsAwesome',
baseUrl: 'https://api.novita.ai/openai',
enable: true
}

mockConfigPresenter.getProviders = vi.fn().mockReturnValue([novitaProvider])
mockConfigPresenter.getProviderById = vi.fn().mockReturnValue(novitaProvider)

llmProviderPresenter = new LLMProviderPresenter(
mockConfigPresenter,
mockSqlitePresenter,
presenterRuntimeMock.mcpPresenter as any
)

const providerInstance = llmProviderPresenter.getProviderInstance('novita')

expect(providerInstance).toBeInstanceOf(OpenAICompatibleProvider)
})
})

describe('Model Management', () => {
Expand Down
15 changes: 15 additions & 0 deletions test/renderer/components/ModelIcon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,19 @@ describe('ModelIcon', () => {
expect(image.attributes('alt')).toBe('dimcode')
expect(image.attributes('src')).toBe(dimcodeIcon)
})

it('resolves novita to the novita.ai icon', async () => {
const ModelIcon = (await import('@/components/icons/ModelIcon.vue')).default
const novitaAiIcon = (await import('@/assets/llm-icons/novitaai.svg?url')).default
const wrapper = mount(ModelIcon, {
props: {
modelId: 'novita'
}
})

const image = wrapper.get('img')

expect(image.attributes('alt')).toBe('novita')
expect(image.attributes('src')).toBe(novitaAiIcon)
})
})
Loading