diff --git a/src/renderer/components/icons/ProviderIcon.tsx b/src/renderer/components/icons/ProviderIcon.tsx index 0aceb77af4..ea6047a44d 100644 --- a/src/renderer/components/icons/ProviderIcon.tsx +++ b/src/renderer/components/icons/ProviderIcon.tsx @@ -69,6 +69,11 @@ export default function ProviderIcon(props: { className?: string; size?: number; )} + {provider === ModelProviderEnum.GreenPT && ( + + + + )} {provider === ModelProviderEnum.Gemini && ( <> diff --git a/src/shared/models/index.ts b/src/shared/models/index.ts index 304c6909a5..ab1a31b5a1 100644 --- a/src/shared/models/index.ts +++ b/src/shared/models/index.ts @@ -21,6 +21,7 @@ export const aiProviderNameHash: Record = { [ModelProviderEnum.Ollama]: 'Ollama API', [ModelProviderEnum.Groq]: 'Groq API', [ModelProviderEnum.DeepSeek]: 'DeepSeek API', + [ModelProviderEnum.GreenPT]: 'GreenPT API', [ModelProviderEnum.SiliconFlow]: 'SiliconFlow API', [ModelProviderEnum.VolcEngine]: 'VolcEngine API', [ModelProviderEnum.MistralAI]: 'MistralAI', @@ -75,6 +76,11 @@ export const AIModelProviderMenuOptionList = [ label: aiProviderNameHash[ModelProviderEnum.DeepSeek], disabled: false, }, + { + value: ModelProviderEnum.GreenPT, + label: aiProviderNameHash[ModelProviderEnum.GreenPT], + disabled: false, + }, { value: ModelProviderEnum.Qwen, label: aiProviderNameHash[ModelProviderEnum.Qwen], diff --git a/src/shared/providers/definitions/greenpt.test.ts b/src/shared/providers/definitions/greenpt.test.ts new file mode 100644 index 0000000000..e5b2350038 --- /dev/null +++ b/src/shared/providers/definitions/greenpt.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest' +import { ModelProviderEnum, ModelProviderType } from '../../types' +import { greenPTProvider } from './greenpt' + +describe('GreenPT provider', () => { + it('registers the OpenAI-compatible endpoint and flagship models', () => { + expect(greenPTProvider).toMatchObject({ + id: ModelProviderEnum.GreenPT, + type: ModelProviderType.OpenAI, + defaultSettings: { + apiHost: 'https://api.greenpt.ai/v1', + }, + }) + expect(greenPTProvider.defaultSettings?.models).toEqual( + expect.arrayContaining([ + expect.objectContaining({ modelId: 'glm-5.2' }), + expect.objectContaining({ modelId: 'kimi-k2.7-code' }), + expect.objectContaining({ modelId: 'green-embedding', type: 'embedding' }), + ]) + ) + }) +}) diff --git a/src/shared/providers/definitions/greenpt.ts b/src/shared/providers/definitions/greenpt.ts new file mode 100644 index 0000000000..1935d1cf6a --- /dev/null +++ b/src/shared/providers/definitions/greenpt.ts @@ -0,0 +1,55 @@ +import { ModelProviderEnum, ModelProviderType } from '../../types' +import { defineProvider } from '../registry' +import GreenPT from './models/greenpt' + +export const greenPTProvider = defineProvider({ + id: ModelProviderEnum.GreenPT, + name: 'GreenPT', + type: ModelProviderType.OpenAI, + description: + 'GreenPT is a European AI provider running optimized inference in data centers powered by 100% renewable energy.', + curatedModelIds: ['glm-5.2', 'kimi-k2.7-code', 'green-embedding'], + urls: { + website: 'https://greenpt.com/', + apiKey: 'https://account.greenpt.ai/api/keys', + docs: 'https://docs.greenpt.ai/get-started', + models: 'https://docs.greenpt.ai/models', + }, + defaultSettings: { + apiHost: 'https://api.greenpt.ai/v1', + models: [ + { + modelId: 'glm-5.2', + contextWindow: 1_000_000, + capabilities: ['reasoning', 'tool_use'], + }, + { + modelId: 'kimi-k2.7-code', + contextWindow: 256_000, + maxOutput: 256_000, + capabilities: ['reasoning', 'tool_use'], + }, + { + modelId: 'green-embedding', + type: 'embedding', + }, + ], + }, + createModel: (config) => { + return new GreenPT( + { + apiKey: config.effectiveApiKey, + apiHost: config.formattedApiHost, + model: config.model, + temperature: config.settings.temperature, + topP: config.settings.topP, + maxOutputTokens: config.settings.maxTokens, + stream: config.settings.stream, + }, + config.dependencies + ) + }, + getDisplayName: (modelId, providerSettings) => { + return `GreenPT API (${providerSettings?.models?.find((model) => model.modelId === modelId)?.nickname || modelId})` + }, +}) diff --git a/src/shared/providers/definitions/models/greenpt.ts b/src/shared/providers/definitions/models/greenpt.ts new file mode 100644 index 0000000000..dc293222e0 --- /dev/null +++ b/src/shared/providers/definitions/models/greenpt.ts @@ -0,0 +1,5 @@ +import OpenAICompatible from '../../../models/openai-compatible' + +export default class GreenPT extends OpenAICompatible { + public name = 'GreenPT' +} diff --git a/src/shared/providers/index.ts b/src/shared/providers/index.ts index 06742110e7..2dafe452fa 100644 --- a/src/shared/providers/index.ts +++ b/src/shared/providers/index.ts @@ -11,6 +11,7 @@ import './definitions/openai-responses' import './definitions/gemini' import './definitions/claude' import './definitions/deepseek' +import './definitions/greenpt' import './definitions/qwen' import './definitions/qwen-portal' import './definitions/minimax' diff --git a/src/shared/types/provider.ts b/src/shared/types/provider.ts index 8f12db4f03..370326f5c8 100644 --- a/src/shared/types/provider.ts +++ b/src/shared/types/provider.ts @@ -18,6 +18,7 @@ export enum ModelProviderEnum { Ollama = 'ollama', Groq = 'groq', DeepSeek = 'deepseek', + GreenPT = 'greenpt', SiliconFlow = 'siliconflow', VolcEngine = 'volcengine', MistralAI = 'mistral-ai', diff --git a/src/shared/utils/llm_utils.test.ts b/src/shared/utils/llm_utils.test.ts index 7aa06422c3..c57098e901 100644 --- a/src/shared/utils/llm_utils.test.ts +++ b/src/shared/utils/llm_utils.test.ts @@ -1,5 +1,17 @@ import { describe, expect, it } from 'vitest' -import { normalizeClaudeHost, normalizeGeminiHost, normalizeOpenAIApiHostAndPath } from './llm_utils' +import { ModelProviderEnum } from '../types' +import { + isOpenAICompatible, + normalizeClaudeHost, + normalizeGeminiHost, + normalizeOpenAIApiHostAndPath, +} from './llm_utils' + +describe('isOpenAICompatible', () => { + it('recognizes GreenPT', () => { + expect(isOpenAICompatible(ModelProviderEnum.GreenPT, 'glm-5.2')).toBe(true) + }) +}) describe('normalizeOpenAIApiHostAndPath', () => { it('returns defaults when apiHost is empty', () => { diff --git a/src/shared/utils/llm_utils.ts b/src/shared/utils/llm_utils.ts index 2df5666a1a..de89a599db 100644 --- a/src/shared/utils/llm_utils.ts +++ b/src/shared/utils/llm_utils.ts @@ -134,6 +134,7 @@ export function isOpenAICompatible(providerId: string, _modelId: string) { ModelProviderEnum.XAI, ModelProviderEnum.Groq, ModelProviderEnum.DeepSeek, + ModelProviderEnum.GreenPT, ModelProviderEnum.LMStudio, ModelProviderEnum.Qwen, ModelProviderEnum.QwenPortal,