From 8e85fdd89e6869d1d8c8e870b9db507d4eae610b Mon Sep 17 00:00:00 2001 From: Wei Date: Wed, 4 Jun 2025 01:14:05 +0800 Subject: [PATCH 1/2] feat: add new model provider BurnCloud --- .../burncloud-setting-util.ts | 106 +++++++++++++++++ .../packages/model-setting-utils/index.ts | 2 + src/renderer/packages/models/burncloud.ts | 111 ++++++++++++++++++ src/renderer/packages/models/index.ts | 18 +++ src/shared/defaults.ts | 83 +++++++++++++ src/shared/types.ts | 1 + 6 files changed, 321 insertions(+) create mode 100644 src/renderer/packages/model-setting-utils/burncloud-setting-util.ts create mode 100644 src/renderer/packages/models/burncloud.ts diff --git a/src/renderer/packages/model-setting-utils/burncloud-setting-util.ts b/src/renderer/packages/model-setting-utils/burncloud-setting-util.ts new file mode 100644 index 000000000..1f4231f04 --- /dev/null +++ b/src/renderer/packages/model-setting-utils/burncloud-setting-util.ts @@ -0,0 +1,106 @@ +import { ModelProvider, ProviderSettings, SessionType } from 'src/shared/types' +import { ModelSettingUtil } from './interface' +import BurnCloud from '../models/burncloud' +import BaseConfig from './base-config' + +export default class BurnCloudSettingUtil extends BaseConfig implements ModelSettingUtil { + public provider: ModelProvider = ModelProvider.BurnCloud + + async getCurrentModelDisplayName( + model: string, + sessionType: SessionType, + providerSettings?: ProviderSettings + ): Promise { + if (sessionType === 'picture') { + return `BurnCloud API (DALL-E-3)` + } else { + return `BurnCloud API (${providerSettings?.models?.find((m) => m.modelId === model)?.nickname || model})` + } + } + + public getLocalOptionGroups() { + // BurnCloud支持的所有模型 + const claudeModels = [ + 'claude-sonnet-4-20250514', + 'claude-3-7-sonnet-20250219', + 'claude-3-5-sonnet-20241022', + ] + + const gptModels = [ + 'gpt-4o', + 'gpt-4o-mini', + 'o1', + 'gpt-4.5-preview', + 'o1-mini', + ] + + const imageModels = [ + 'gpt-image-1', + ] + + const geminiModels = [ + 'gemini-2.5-pro-preview-05-06', + 'gemini-2.0', + ] + + const deepseekModels = [ + 'deepseek-r1', + 'deepseek-v3', + ] + + return [ + { + group_name: 'Claude 系列', + options: claudeModels.map((value) => ({ + label: value, + value: value, + })), + collapsable: true, + }, + { + group_name: 'GPT 系列', + options: gptModels.map((value) => ({ + label: value, + value: value, + })), + collapsable: true, + }, + { + group_name: '图像生成', + options: imageModels.map((value) => ({ + label: value, + value: value, + })), + collapsable: true, + }, + { + group_name: 'Gemini 系列', + options: geminiModels.map((value) => ({ + label: value, + value: value, + })), + collapsable: true, + }, + { + group_name: 'DeepSeek 系列', + options: deepseekModels.map((value) => ({ + label: value, + value: value, + })), + collapsable: true, + }, + ] + } + + protected async listProviderModels() { + return [] + } + + isCurrentModelSupportImageInput(model: string): boolean { + return BurnCloud.helpers.isModelSupportVision(model) + } + + isCurrentModelSupportToolUse(model: string): boolean { + return BurnCloud.helpers.isModelSupportToolUse(model) + } +} \ No newline at end of file diff --git a/src/renderer/packages/model-setting-utils/index.ts b/src/renderer/packages/model-setting-utils/index.ts index e42c83551..975ee0930 100644 --- a/src/renderer/packages/model-setting-utils/index.ts +++ b/src/renderer/packages/model-setting-utils/index.ts @@ -14,6 +14,7 @@ import CustomModelSettingUtil from './custom-setting-util' import DeepSeekSettingUtil from './deepseek-setting-util' import SiliconFlowSettingUtil from './siliconflow-setting-util' import XAISettingUtil from './xai-setting-util' +import BurnCloudSettingUtil from './burncloud-setting-util' export function getModelSettingUtil(aiProvider: ModelProvider): ModelSettingUtil { const hash: Record ModelSettingUtil> = { @@ -31,6 +32,7 @@ export function getModelSettingUtil(aiProvider: ModelProvider): ModelSettingUtil [ModelProvider.Perplexity]: PerplexitySettingUtil, [ModelProvider.XAI]: XAISettingUtil, [ModelProvider.Custom]: CustomModelSettingUtil, + [ModelProvider.BurnCloud]: BurnCloudSettingUtil, } const Class = hash[aiProvider] || CustomModelSettingUtil return new Class() diff --git a/src/renderer/packages/models/burncloud.ts b/src/renderer/packages/models/burncloud.ts new file mode 100644 index 000000000..673bceffd --- /dev/null +++ b/src/renderer/packages/models/burncloud.ts @@ -0,0 +1,111 @@ +import { fetchWithProxy } from '@/utils/request' +import { createOpenAICompatible } from '@ai-sdk/openai-compatible' +import { extractReasoningMiddleware, wrapLanguageModel } from 'ai' +import AbstractAISDKModel from './abstract-ai-sdk' +import { ModelHelpers } from './types' +import { fetchRemoteModels } from './openai-compatible' + +const helpers: ModelHelpers = { + isModelSupportVision: (model: string) => { + // BurnCloud支持视觉的模型 + const visionModels = [ + 'claude-sonnet-4-20250514', 'claude-3-7-sonnet-20250219', 'claude-3-5-sonnet-20241022', // Claude系列都支持视觉 + 'gpt-4o', 'gpt-4o-mini', 'gpt-4.5-preview', // GPT视觉模型 + 'gemini-2.5-pro-preview-05-06', 'gemini-2.0', // Gemini系列支持视觉 + ] + return visionModels.some(visionModel => model.includes(visionModel.replace(/[.-]/g, ''))) || + model.includes('claude') || + model.includes('gpt-4o') || + model.includes('gemini') + }, + isModelSupportToolUse: (model: string) => { + // BurnCloud支持工具使用的模型(除了纯图像生成和推理专用模型) + const nonToolModels = [ + 'gpt-image-1', // 图像生成模型不支持工具 + 'o1', 'o1-mini', // 推理模型通常不支持工具调用 + ] + return !nonToolModels.some(nonToolModel => model.includes(nonToolModel)) + }, +} + +interface Options { + apiKey: string + apiHost: string + model: string + temperature?: number + topP?: number + useProxy?: boolean +} + +export default class BurnCloud extends AbstractAISDKModel { + public name = 'BurnCloud' + public static helpers = helpers + public options: Options + + constructor(options: Options) { + super() + this.options = { + ...options, + apiHost: options.apiHost || 'https://ai.burncloud.com/v1', + } + } + + public isSupportToolUse() { + return helpers.isModelSupportToolUse(this.options.model) + } + + private getProvider() { + return createOpenAICompatible({ + name: 'BurnCloud', + apiKey: this.options.apiKey, + baseURL: this.options.apiHost, + fetch: this.options.useProxy ? fetchWithProxy : undefined, + }) + } + + protected getChatModel() { + const provider = this.getProvider() + return wrapLanguageModel({ + model: provider.languageModel(this.options.model), + middleware: extractReasoningMiddleware({ tagName: 'think' }), + }) + } + + protected getImageModel() { + const provider = this.getProvider() + return provider.imageModel('dall-e-3') + } + + protected getCallSettings() { + return { + temperature: this.options.temperature, + topP: this.options.topP, + } + } + + public async listModels(): Promise { + return fetchRemoteModels({ + apiHost: this.options.apiHost, + apiKey: this.options.apiKey, + useProxy: this.options.useProxy, + }).catch((err) => { + console.error('BurnCloud models fetch error:', err) + // 返回BurnCloud支持的常见模型作为备选 + return [ + 'claude-sonnet-4-20250514', + 'claude-3-7-sonnet-20250219', + 'claude-3-5-sonnet-20241022', + 'gpt-4o', + 'gpt-4o-mini', + 'o1', + 'gpt-4.5-preview', + 'o1-mini', + 'gpt-image-1', + 'gemini-2.5-pro-preview-05-06', + 'gemini-2.0', + 'deepseek-r1', + 'deepseek-v3', + ] + }) + } +} \ No newline at end of file diff --git a/src/renderer/packages/models/index.ts b/src/renderer/packages/models/index.ts index 1e38f0f02..5c678c31d 100644 --- a/src/renderer/packages/models/index.ts +++ b/src/renderer/packages/models/index.ts @@ -12,6 +12,7 @@ import DeepSeek from './deepseek' import SiliconFlow from './siliconflow' import Perplexity from './perplexity' import XAI from './xai' +import BurnCloud from './burncloud' import type { ModelInterface } from './types' import CustomOpenAI from './custom-openai' import { SystemProviders } from 'src/shared/defaults' @@ -143,6 +144,17 @@ export function getModel(setting: Settings, config: Config): ModelInterface { temperature: setting.temperature, topP: setting.topP, }) + + case ModelProvider.BurnCloud: + return new BurnCloud({ + apiKey: providerSetting.apiKey || '', + apiHost: formattedApiHost, + model: setting.modelId || '', + temperature: setting.temperature, + topP: setting.topP, + useProxy: providerSetting.useProxy, + }) + default: if (providerBaseInfo.isCustom) { return new CustomOpenAI({ @@ -174,6 +186,7 @@ export const aiProviderNameHash: Record = { [ModelProvider.LMStudio]: 'LM Studio API', [ModelProvider.Perplexity]: 'Perplexity API', [ModelProvider.XAI]: 'xAI API', + [ModelProvider.BurnCloud]: 'BurnCloud API', [ModelProvider.Custom]: 'Custom Provider', } @@ -244,6 +257,11 @@ export const AIModelProviderMenuOptionList = [ label: aiProviderNameHash[ModelProvider.ChatGLM6B], disabled: false, }, + { + value: ModelProvider.BurnCloud, + label: aiProviderNameHash[ModelProvider.BurnCloud], + disabled: false, + }, // { // value: 'hunyuan', // label: '腾讯混元', diff --git a/src/shared/defaults.ts b/src/shared/defaults.ts index 60e1e860e..067d79a8c 100644 --- a/src/shared/defaults.ts +++ b/src/shared/defaults.ts @@ -603,4 +603,87 @@ export const SystemProviders: ProviderBaseInfo[] = [ ], }, }, + { + id: ModelProvider.BurnCloud, + name: 'BurnCloud', + type: ModelProviderType.OpenAI, + urls: { + website: 'https://burncloud.com', + }, + defaultSettings: { + apiHost: 'https://ai.burncloud.com/v1', + models: [ + // Claude 系列 + { + modelId: 'claude-sonnet-4-20250514', + capabilities: ['vision', 'tool_use'], + contextWindow: 200_000, + }, + { + modelId: 'claude-3-7-sonnet-20250219', + capabilities: ['vision', 'tool_use'], + contextWindow: 200_000, + }, + { + modelId: 'claude-3-5-sonnet-20241022', + capabilities: ['vision', 'tool_use'], + contextWindow: 200_000, + }, + // GPT 系列 + { + modelId: 'gpt-4o', + capabilities: ['vision', 'tool_use'], + contextWindow: 128_000, + }, + { + modelId: 'gpt-4o-mini', + capabilities: ['vision', 'tool_use'], + contextWindow: 128_000, + }, + { + modelId: 'o1', + capabilities: ['reasoning'], + contextWindow: 128_000, + }, + { + modelId: 'gpt-4.5-preview', + capabilities: ['vision', 'tool_use'], + contextWindow: 128_000, + }, + { + modelId: 'o1-mini', + capabilities: ['reasoning'], + contextWindow: 128_000, + }, + // 图像生成模型 + { + modelId: 'gpt-image-1', + capabilities: [], + contextWindow: 4_000, + }, + // Gemini 系列 + { + modelId: 'gemini-2.5-pro-preview-05-06', + capabilities: ['vision', 'tool_use'], + contextWindow: 1_000_000, + }, + { + modelId: 'gemini-2.0', + capabilities: ['vision', 'tool_use'], + contextWindow: 1_000_000, + }, + // DeepSeek 系列 + { + modelId: 'deepseek-r1', + capabilities: ['reasoning', 'tool_use'], + contextWindow: 64_000, + }, + { + modelId: 'deepseek-v3', + capabilities: ['tool_use'], + contextWindow: 64_000, + }, + ], + }, + }, ] diff --git a/src/shared/types.ts b/src/shared/types.ts index c75376bb2..91880f092 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -203,6 +203,7 @@ export enum ModelProvider { LMStudio = 'lm-studio', Perplexity = 'perplexity', XAI = 'xAI', + BurnCloud = 'burncloud', Custom = 'custom', } From a2314928b193c72cd115c2d2782020a205bd9c0b Mon Sep 17 00:00:00 2001 From: Wei Date: Wed, 4 Jun 2025 01:41:34 +0800 Subject: [PATCH 2/2] feat: add new model provider BurnCloud Logo --- .../static/icons/providers/burncloud.png | Bin 0 -> 2690 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/renderer/static/icons/providers/burncloud.png diff --git a/src/renderer/static/icons/providers/burncloud.png b/src/renderer/static/icons/providers/burncloud.png new file mode 100644 index 0000000000000000000000000000000000000000..65748adfd49a8ada0e95696729a5080a5dcc3c84 GIT binary patch literal 2690 zcmV-|3Vrp7P)&7J9^e<1pYxbJW$vJ1w?mhQz9_Ksr8D_HI^F6=cIoX@tO_m-#w6wIew6wIe zw6wIew6wIew6wIew8UyYIJ6g73^sXPOScv_AR4Ry*8lJ%Sz+Zf zuWRnI!s0&8_@QlwaTa~k>)N}Vu&7%dZiJN#uj}tJ!eaIT$3xf*gP(ZaN0$&5G7+2) zaWf2F^SYN#FDzoJ;NHS1*SzkhQws~2X0*;B{}E zPFS=k@R5U!r~~88v1@F+wO;qvA%unajRWr?2b*DRT$*KUw#1n3eYJ%Jc*$4@N$&~= zoAC}O!|r42pK3eoL_+fu%|!@Ect<(djJE|jk4lW6thUomAT&J?RGSOnxAEQ#u1Ic% z?fryARwcMeUALV;X!xLI0fc7=j+aSp#&*fM3na2$QPXKB5NkmM-VDjjc+X)CXC*Qx zh2Cukm+?{1+;S;OHy_9G|6XF@J_S(@>-erjZX+0~rt=OeG+4@MK9}JgCb4KfkmRzK z%@X;SK)=vC?|=#%$V&kK1BAg382&9Bua&z(sHZ3RkHj3uz&Aqcz5^=ohrEq|a9GsF zyFafl2mm5L`i7EQ)Be2S67v{zt;Z^XeM4>m&46&&O8JWfUV{qY=;wx@w$UJ9GoQf& zD(34Dy0K^QGC%};Ncs2K_@^kXZ|~sm17brXNLD$oXS%U_@aKToaFfMfFYsLqQ;j3Q zb(YWP)V5@-N~K_lnuEUp4?Hl<+8$XXM;LGBze*tmoMob5w_wj`eJ?hLO{T60?#4S zLo<`|MS+tXZxFRNk@YX8^#kt#(uQ@mEc-fRUD}@|{1JJ|ig_~4`OXPEV>mUw#qiz& zJ{I(KMP2dFr;Y$u0C7Q?CFf2Fe@|tF+xC@q31H4N?ssqf3u)&7q4=ER9nSDtDGs@4 z|Ffy%K>!e!l()$%O+5n$kB|#?(pzcjZgbv$2t0+HT4Oo>^@9FFJ4^g)Q^tb;AU2#9 z96L&7-XY2gsbJ^*J!P4DEYtI|l)iv47|!wc zuf6v5@zrf;c2%rP`B$e*0>osqz<0#r3v#Exzk}fqQpOv{Ro%+uL{JTgfJ%^U`g3f& zQB-~(b9_6m<6=V)=DXE0ceTJ*%&7H;T>mr4V*ufB+IgW*Cw&NrfC`XIjS>0BG3FS} z@%C|CuuIh~Pl^M?!nZlT+bEyG1e@$8#+V@T7cR9WiG_d&s0LY#aTf(+USt+slDG&E zigwxIR9y)csFm50K7-LVxyKk|f((-?u69XcZ{Pz&z!=WhJ%TaZYkeFmr@ZaXrf{lu zDdueD=K|j;%9~=7yO#1>`H@_43lj;$R~+9L5CO|=GoFn11H$3D#P|i^lx?w!gt^92H8StOP47~R z?`4VXRKZ#-wl8jO-0gq}VBVQh89Qw(Hb6r?K|}!ol_wH8UO&WEfln;r-o!163Er?E%D!z&pqEGy|#E;OJ}%%%=iv2hWZz z0E7egE|$vZFUSbOyae__sKrm!RwwWlo1R@#*@dw&fN+Sh4XAo7W-1^YR!KMcSWGz} zHhBKFQuX68(*dD4FYwPdJ@-lG=Q8pQ7qLv(ou9V~%IQPFyBU}Ot_&Ff~86!34xM6O?`!Pr?HU=xA}|KQp|Ps^#?Yha{` zY}RCbz`KlQT>U&TGbj4oeY~i|(ao-)mpN4KSrGnL5HQIyMpM!c9T|Cf;J8Pdu~5;P zyhP=`4tlG|mMy@zuQ)&zu|JgW2#mcST#nF2TmYjKtV3^b(2{HXFpts975B$+m zkn!;&{Y03P`D_?Bq85}W>fN}q`^?Psh1$1Lp_rk*mMf=9v%pWmT%XJ9ZePolU9qp_ z5?94Wd<2%N_!vRPNbpy11MCFSPkucS3LFjAn?*O6MNa}ZyUdI6wcOgqnsbD{7A@EtJ4_=r=KY1|1<05iZ6^Sgf<7^7+fsQFxd wGihmQX=!O`X=!O`X=!O`X=!O`X+=!`2NVfm30|-