diff --git a/src/shared/models/chinallm.ts b/src/shared/models/chinallm.ts new file mode 100644 index 0000000000..53b45b75e3 --- /dev/null +++ b/src/shared/models/chinallm.ts @@ -0,0 +1,9 @@ +import { OpenAICompatible } from './openai-compatible' + +export class ChinaLLM extends OpenAICompatible { + name = 'ChinaLLM' + + constructor(apiKey: string, apiHost: string) { + super(apiKey, apiHost) + } +} diff --git a/src/shared/models/index.ts b/src/shared/models/index.ts index 304c6909a5..1d0c54d497 100644 --- a/src/shared/models/index.ts +++ b/src/shared/models/index.ts @@ -30,6 +30,7 @@ export const aiProviderNameHash: Record = { [ModelProviderEnum.OpenRouter]: 'OpenRouter API', [ModelProviderEnum.Bedrock]: 'AWS Bedrock', [ModelProviderEnum.VercelAIGateway]: 'Vercel AI Gateway', + [ModelProviderEnum.ChinaLLM]: 'ChinaLLM API', [ModelProviderEnum.Custom]: 'Custom Provider', } @@ -150,6 +151,11 @@ export const AIModelProviderMenuOptionList = [ label: aiProviderNameHash[ModelProviderEnum.VercelAIGateway], disabled: false, }, + { + value: ModelProviderEnum.ChinaLLM, + label: aiProviderNameHash[ModelProviderEnum.ChinaLLM], + disabled: false, + }, { value: ModelProviderEnum.ChatGLM6B, label: aiProviderNameHash[ModelProviderEnum.ChatGLM6B], diff --git a/src/shared/providers/definitions/chinallm.ts b/src/shared/providers/definitions/chinallm.ts new file mode 100644 index 0000000000..4e0ae3a3db --- /dev/null +++ b/src/shared/providers/definitions/chinallm.ts @@ -0,0 +1,59 @@ +import { ModelProviderEnum, ModelProviderType } from '../../types' +import { defineProvider } from '../registry' +import ChinaLLMModel from './models/chinallm' + +export const chinallmProvider = defineProvider({ + id: ModelProviderEnum.ChinaLLM, + name: 'ChinaLLM', + type: ModelProviderType.OpenAI, + modelsDevProviderId: 'chinallm', + curatedModelIds: ['deepseek-chat', 'deepseek-reasoner', 'qwen-plus', 'qwen-max', 'glm-4-flash'], + urls: { + website: 'https://chinallm.dev', + }, + defaultSettings: { + models: [ + { + modelId: 'deepseek-chat', + contextWindow: 65536, + capabilities: ['tool_use'], + }, + { + modelId: 'deepseek-reasoner', + contextWindow: 65536, + capabilities: ['reasoning', 'tool_use'], + }, + { + modelId: 'qwen-plus', + contextWindow: 131072, + capabilities: ['vision'], + }, + { + modelId: 'qwen-max', + contextWindow: 32768, + capabilities: ['vision', 'tool_use'], + }, + { + modelId: 'glm-4-flash', + contextWindow: 128000, + capabilities: ['tool_use'], + }, + ], + }, + createModel: (config) => { + return new ChinaLLMModel( + { + apiKey: config.effectiveApiKey, + 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 `ChinaLLM (${providerSettings?.models?.find((m) => m.modelId === modelId)?.nickname || modelId})` + }, +}) diff --git a/src/shared/providers/definitions/models/chinallm.ts b/src/shared/providers/definitions/models/chinallm.ts new file mode 100644 index 0000000000..241be9a472 --- /dev/null +++ b/src/shared/providers/definitions/models/chinallm.ts @@ -0,0 +1,64 @@ +import { createOpenAI } from '@ai-sdk/openai' +import type { LanguageModelV3 } from '@ai-sdk/provider' +import AbstractAISDKModel, { type CallSettings } from '../../../models/abstract-ai-sdk' +import type { CallChatCompletionOptions } from '../../../models/types' +import type { ProviderModelInfo, ToolUseScope } from '../../../types' +import type { ModelDependencies } from '../../../types/adapters' + +interface Options { + apiKey: string + model: ProviderModelInfo + temperature?: number + topP?: number + maxOutputTokens?: number + stream?: boolean +} + +export default class ChinaLLM extends AbstractAISDKModel { + public name = 'ChinaLLM' + + constructor( + public options: Options, + dependencies: ModelDependencies + ) { + super(options, dependencies) + } + + protected getProvider() { + return createOpenAI({ + apiKey: this.options.apiKey, + baseURL: 'https://api.chinallm.dev/v1', + compatibility: 'strict', + }) + } + + protected getChatModel(_options: CallChatCompletionOptions): LanguageModelV3 { + const provider = this.getProvider() + return provider.chat(this.options.model.modelId) + } + + protected getCallSettings(_options: CallChatCompletionOptions): CallSettings { + const isReasonerModel = this.options.model.modelId === 'deepseek-reasoner' + const settings: CallSettings = { + maxOutputTokens: this.options.maxOutputTokens, + } + + if (!isReasonerModel) { + settings.temperature = this.options.temperature + settings.topP = this.options.topP + } + + return settings + } + + isSupportToolUse(scope?: ToolUseScope) { + if ( + scope && + ['web-browsing', 'read-file'].includes(scope) && + /deepseek-(v3|r1)$/.test(this.options.model.modelId.toLowerCase()) + ) { + return false + } + return super.isSupportToolUse() + } +} diff --git a/src/shared/providers/index.ts b/src/shared/providers/index.ts index 06742110e7..4fcc26a4ff 100644 --- a/src/shared/providers/index.ts +++ b/src/shared/providers/index.ts @@ -29,6 +29,7 @@ import './definitions/chatglm' import './definitions/github-copilot' import './definitions/bedrock' import './definitions/vercel-ai-gateway' +import './definitions/chinallm' import { clearProviderRegistry, defineProvider, diff --git a/src/shared/types/provider.ts b/src/shared/types/provider.ts index 8f12db4f03..bf98a4efb4 100644 --- a/src/shared/types/provider.ts +++ b/src/shared/types/provider.ts @@ -27,6 +27,7 @@ export enum ModelProviderEnum { OpenRouter = 'openrouter', Bedrock = 'bedrock', VercelAIGateway = 'vercel-ai-gateway', + ChinaLLM = 'chinallm', Custom = 'custom', }