feat: add baidu cloud provider

This commit is contained in:
kangfenmao 2025-02-07 16:47:29 +08:00
parent 762c3d4950
commit 2898215a00
17 changed files with 156 additions and 12 deletions

View File

@ -0,0 +1,19 @@
diff --git a/dist/embeddings.js b/dist/embeddings.js
index 1f8154be3e9c22442a915eb4b85fa6d2a21b0d0c..dc13ef4a30e6c282824a5357bcee9bd0ae222aab 100644
--- a/dist/embeddings.js
+++ b/dist/embeddings.js
@@ -214,10 +214,12 @@ export class OpenAIEmbeddings extends Embeddings {
* @returns Promise that resolves to an embedding for the document.
*/
async embedQuery(text) {
+ const isBaiduCloud = this.clientConfig.baseURL.includes('baidubce.com')
+ const input = this.stripNewLines ? text.replace(/\n/g, ' ') : text
const params = {
model: this.model,
- input: this.stripNewLines ? text.replace(/\n/g, " ") : text,
- };
+ input: isBaiduCloud ? [input] : input
+ }
if (this.dimensions) {
params.dimensions = this.dimensions;
}

View File

@ -151,7 +151,9 @@
},
"resolutions": {
"pdf-parse@npm:1.1.1": "patch:pdf-parse@npm%3A1.1.1#~/.yarn/patches/pdf-parse-npm-1.1.1-04a6109b2a.patch",
"@llm-tools/embedjs-utils@npm:0.1.25": "patch:@llm-tools/embedjs-utils@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-utils-npm-0.1.25-fd8fe8a193.patch"
"@llm-tools/embedjs-utils@npm:0.1.25": "patch:@llm-tools/embedjs-utils@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-utils-npm-0.1.25-fd8fe8a193.patch",
"@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch",
"@langchain/openai@npm:>=0.1.0 <0.4.0": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch"
},
"packageManager": "yarn@4.5.0"
}

View File

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>BaiduCloud</title><path d="M21.715 5.61l-3.983 2.31a.903.903 0 01-.896 0L12.44 5.384a.903.903 0 00-.897 0L7.156 7.92a.903.903 0 01-.896 0L2.276 5.617 12.002 0l9.713 5.61z" fill="#5BCA87"></path><path d="M18.641 9.467a.89.89 0 00-.438.77v5.072a.896.896 0 01-.445.77l-4.428 2.51a.884.884 0 00-.445.777v4.607l4.429-2.536 5.31-3.047V7.157l-3.983 2.31z" fill="#EC5D3E"></path><path d="M10.98 18.941a.936.936 0 00-.305-.352l-4.429-2.516a.903.903 0 01-.431-.764v-5.078a.89.89 0 00-.452-.757l-.451-.26L1.38 7.158V18.39l5.311 3.047L11.126 24v-4.608a.881.881 0 00-.146-.45z" fill="#2464F5"></path></svg>

After

Width:  |  Height:  |  Size: 717 B

View File

@ -991,6 +991,56 @@ export const SYSTEM_MODELS: Record<string, Model[]> = {
name: 'Gemma 7B',
group: 'Gemma'
}
],
'baidu-cloud': [
{
id: 'deepseek-r1',
provider: 'baidu-cloud',
name: 'DeepSeek R1',
group: 'DeepSeek'
},
{
id: 'deepseek-v3',
provider: 'baidu-cloud',
name: 'DeepSeek V3',
group: 'DeepSeek'
},
{
id: 'ernie-4.0-8k-latest',
provider: 'baidu-cloud',
name: 'ERNIE-4.0',
group: 'ERNIE'
},
{
id: 'ernie-4.0-turbo-8k-latest',
provider: 'baidu-cloud',
name: 'ERNIE 4.0 Trubo',
group: 'ERNIE'
},
{
id: 'ernie-speed-8k',
provider: 'baidu-cloud',
name: 'ERNIE Speed',
group: 'ERNIE'
},
{
id: 'ernie-lite-8k',
provider: 'baidu-cloud',
name: 'ERNIE Lite',
group: 'ERNIE'
},
{
id: 'bge-large-zh',
provider: 'baidu-cloud',
name: 'BGE Large ZH',
group: 'Embedding'
},
{
id: 'bge-large-en',
provider: 'baidu-cloud',
name: 'BGE Large EN',
group: 'Embedding'
}
]
}

View File

@ -4,6 +4,7 @@ import AzureProviderLogo from '@renderer/assets/images/models/microsoft.png'
import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.jpg'
import AnthropicProviderLogo from '@renderer/assets/images/providers/anthropic.png'
import BaichuanProviderLogo from '@renderer/assets/images/providers/baichuan.png'
import BaiduCloudProviderLogo from '@renderer/assets/images/providers/baidu-cloud.svg'
import BailianProviderLogo from '@renderer/assets/images/providers/bailian.png'
import BytedanceProviderLogo from '@renderer/assets/images/providers/bytedance.png'
import DeepSeekProviderLogo from '@renderer/assets/images/providers/deepseek.png'
@ -91,6 +92,8 @@ export function getProviderLogo(providerId: string) {
return MistralProviderLogo
case 'jina':
return JinaProviderLogo
case 'baidu-cloud':
return BaiduCloudProviderLogo
default:
return undefined
}
@ -418,5 +421,16 @@ export const PROVIDER_CONFIG = {
docs: 'https://learn.microsoft.com/en-us/azure/ai-services/openai/',
models: 'https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models'
}
},
'baidu-cloud': {
api: {
url: 'https://qianfan.baidubce.com/v2/'
},
websites: {
official: 'https://cloud.baidu.com/',
apiKey: 'https://cloud.baidu.com/console/qianfan/apikey',
docs: 'https://cloud.baidu.com/doc/index.html',
models: 'https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Fm2vrveyu'
}
}
}

View File

@ -253,6 +253,9 @@
"error.chunk_overlap_too_large": "Chunk overlap cannot be greater than chunk size",
"error.invalid.proxy.url": "Invalid proxy URL",
"error.invalid.webdav": "Invalid WebDAV settings",
"error.invalid.enter.api.host": "Please enter a valid API host",
"error.invalid.enter.api.key": "Please enter a valid API key",
"error.invalid.enter.model": "Please select a model",
"message.code_style": "Code style",
"message.delete.content": "Are you sure you want to delete this message?",
"message.delete.title": "Delete Message",
@ -316,6 +319,7 @@
"aihubmix": "AiHubMix",
"anthropic": "Anthropic",
"azure-openai": "Azure OpenAI",
"baidu-cloud": "Baidu Cloud",
"baichuan": "Baichuan",
"dashscope": "Alibaba Cloud",
"deepseek": "DeepSeek",

View File

@ -247,6 +247,9 @@
"error.chunk_overlap_too_large": "チャンクの重なりは、チャンクサイズを超えることはできません",
"error.invalid.proxy.url": "無効なプロキシURL",
"error.invalid.webdav": "無効なWebDAV設定",
"error.invalid.enter.api.host": "APIホストを入力してください",
"error.invalid.enter.api.key": "APIキーを入力してください",
"error.invalid.enter.model": "モデルを選択してください",
"message.code_style": "コードスタイル",
"message.delete.content": "このメッセージを削除してもよろしいですか?",
"message.delete.title": "メッセージを削除",
@ -310,6 +313,7 @@
"aihubmix": "AiHubMix",
"anthropic": "Anthropic",
"azure-openai": "Azure OpenAI",
"baidu-cloud": "Baidu Cloud",
"baichuan": "百川",
"dashscope": "Alibaba Cloud",
"deepseek": "DeepSeek",

View File

@ -248,6 +248,9 @@
"error.chunk_overlap_too_large": "Перекрытие фрагментов не может быть больше размера фрагмента.",
"error.invalid.proxy.url": "Неверный URL прокси",
"error.invalid.webdav": "Неверные настройки WebDAV",
"error.invalid.enter.api.host": "Пожалуйста, введите правильный API хост",
"error.invalid.enter.api.key": "Пожалуйста, введите правильный API ключ",
"error.invalid.enter.model": "Пожалуйста, выберите модель",
"message.code_style": "Стиль кода",
"message.delete.content": "Вы уверены, что хотите удалить это сообщение?",
"message.delete.title": "Удалить сообщение",
@ -311,6 +314,7 @@
"aihubmix": "AiHubMix",
"anthropic": "Anthropic",
"azure-openai": "Azure OpenAI",
"baidu-cloud": "Baidu Cloud",
"baichuan": "Baichuan",
"dashscope": "Alibaba Cloud",
"deepseek": "DeepSeek",

View File

@ -254,6 +254,9 @@
"error.chunk_overlap_too_large": "分段重叠不能大于分段大小",
"error.invalid.proxy.url": "无效的代理地址",
"error.invalid.webdav": "无效的 WebDAV 设置",
"error.invalid.api.key": "无效的 API 密钥",
"error.invalid.api.host": "无效的 API 地址",
"error.invalid.enter.model": "请选择一个模型",
"message.code_style": "代码风格",
"message.delete.content": "确定要删除此消息吗?",
"message.delete.title": "删除消息",
@ -317,6 +320,7 @@
"aihubmix": "AiHubMix",
"anthropic": "Anthropic",
"azure-openai": "Azure OpenAI",
"baidu-cloud": "百度云千帆",
"baichuan": "百川",
"dashscope": "阿里云百炼",
"deepseek": "深度求索",

View File

@ -253,6 +253,9 @@
"error.chunk_overlap_too_large": "分段重疊不能大於分段大小",
"error.invalid.proxy.url": "無效的代理 URL",
"error.invalid.webdav": "無效的 WebDAV 設定",
"error.invalid.enter.api.host": "請輸入有效的 API 主機地址",
"error.invalid.enter.api.key": "請輸入有效的 API 密鑰",
"error.invalid.enter.model": "請選擇一個模型",
"message.code_style": "程式碼風格",
"message.delete.content": "確定要刪除此訊息嗎?",
"message.delete.title": "刪除訊息",
@ -316,6 +319,7 @@
"aihubmix": "AiHubMix",
"anthropic": "Anthropic",
"azure-openai": "Azure OpenAI",
"baidu-cloud": "百度云千帆",
"baichuan": "百川",
"dashscope": "阿里雲百鍊",
"deepseek": "深度求索",

View File

@ -42,7 +42,7 @@ const PopupContainer: React.FC<Props> = ({ title, provider, model, apiKeys, reso
for (let i = 0; i < newStatuses.length; i++) {
setKeyStatuses((prev) => prev.map((status, idx) => (idx === i ? { ...status, checking: true } : status)))
const valid = await checkApi({ ...provider, apiKey: newStatuses[i].key }, model)
const { valid } = await checkApi({ ...provider, apiKey: newStatuses[i].key }, model)
setKeyStatuses((prev) =>
prev.map((status, idx) => (idx === i ? { ...status, checking: false, isValid: valid } : status))

View File

@ -128,13 +128,19 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
} else {
setApiChecking(true)
const valid = await checkApi({ ...provider, apiKey, apiHost }, model)
const { valid, error } = await checkApi({ ...provider, apiKey, apiHost }, model)
const errorMessage = error && error?.message ? ' ' + error?.message : ''
window.message[valid ? 'success' : 'error']({
key: 'api-check',
style: { marginTop: '3vh' },
duration: valid ? 2 : 8,
content: valid ? i18n.t('message.api.connection.success') : i18n.t('message.api.connection.failed')
content: valid
? i18n.t('message.api.connection.success')
: i18n.t('message.api.connection.failed') + errorMessage
})
setApiValid(valid)
setApiChecking(false)
setTimeout(() => setApiValid(false), 3000)

View File

@ -3,7 +3,7 @@ import { isEmbeddingModel } from '@renderer/config/models'
import i18n from '@renderer/i18n'
import { Provider } from '@renderer/types'
import { Modal, Select } from 'antd'
import { last, orderBy } from 'lodash'
import { first, orderBy } from 'lodash'
import { useState } from 'react'
interface ShowParams {
@ -18,7 +18,7 @@ interface Props extends ShowParams {
const PopupContainer: React.FC<Props> = ({ provider, resolve, reject }) => {
const models = orderBy(provider.models, 'group').filter((i) => !isEmbeddingModel(i))
const [open, setOpen] = useState(true)
const [model, setModel] = useState(last(models))
const [model, setModel] = useState(first(models))
const onOk = () => {
if (!model) {

View File

@ -461,7 +461,7 @@ export default class OpenAIProvider extends BaseProvider {
public async getEmbeddingDimensions(model: Model): Promise<number> {
const data = await this.sdk.embeddings.create({
model: model.id,
input: 'hi'
input: model?.provider === 'baidu-cloud' ? ['hi'] : 'hi'
})
return data.data[0].embedding.length
}

View File

@ -206,25 +206,37 @@ export async function checkApi(provider: Provider, model: Model) {
if (provider.id !== 'ollama') {
if (!provider.apiKey) {
window.message.error({ content: i18n.t('message.error.enter.api.key'), key, style })
return false
return {
valid: false,
error: new Error('message.error.enter.api.key')
}
}
}
if (!provider.apiHost) {
window.message.error({ content: i18n.t('message.error.enter.api.host'), key, style })
return false
return {
valid: false,
error: new Error('message.error.enter.api.host')
}
}
if (isEmpty(provider.models)) {
window.message.error({ content: i18n.t('message.error.enter.model'), key, style })
return false
return {
valid: false,
error: new Error('message.error.enter.model')
}
}
const AI = new AiProvider(provider)
const { valid } = await AI.check(model)
const { valid, error } = await AI.check(model)
return valid
return {
valid,
error
}
}
function hasApiKey(provider: Provider) {

View File

@ -63,6 +63,16 @@ const initialState: LlmState = {
isSystem: true,
enabled: false
},
{
id: 'baidu-cloud',
name: 'Baidu Cloud',
type: 'openai',
apiKey: '',
apiHost: 'https://qianfan.baidubce.com/v2/',
models: SYSTEM_MODELS['baidu-cloud'],
isSystem: true,
enabled: false
},
{
id: 'ollama',
name: 'Ollama',

View File

@ -910,6 +910,16 @@ const migrateConfig = {
},
'64': (state: RootState) => {
state.llm.providers = state.llm.providers.filter((provider) => provider.id !== 'qwenlm')
state.llm.providers.push({
id: 'baidu-cloud',
name: 'Baidu Cloud',
type: 'openai',
apiKey: '',
apiHost: 'https://qianfan.baidubce.com/v2/',
models: SYSTEM_MODELS['baidu-cloud'],
isSystem: true,
enabled: false
})
return state
}
}