diff --git a/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch b/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch
new file mode 100644
index 00000000..3f37fb3e
--- /dev/null
+++ b/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch
@@ -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;
+ }
diff --git a/package.json b/package.json
index 1d15015c..c88c54f9 100644
--- a/package.json
+++ b/package.json
@@ -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"
}
diff --git a/src/renderer/src/assets/images/providers/baidu-cloud.svg b/src/renderer/src/assets/images/providers/baidu-cloud.svg
new file mode 100644
index 00000000..40565efc
--- /dev/null
+++ b/src/renderer/src/assets/images/providers/baidu-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts
index 23fb99e3..639550a9 100644
--- a/src/renderer/src/config/models.ts
+++ b/src/renderer/src/config/models.ts
@@ -991,6 +991,56 @@ export const SYSTEM_MODELS: Record = {
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'
+ }
]
}
diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts
index 03455bf6..574bc043 100644
--- a/src/renderer/src/config/providers.ts
+++ b/src/renderer/src/config/providers.ts
@@ -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'
+ }
}
}
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index cad2d5fb..9ec4b756 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -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",
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index ab51d583..cfdf243a 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -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",
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index 4b8e4e88..3e45653d 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -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",
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 36a53a3e..1ca0a49b 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -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": "深度求索",
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 8e1e3f31..87d61eec 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -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": "深度求索",
diff --git a/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx
index 910acce2..b3c80dbc 100644
--- a/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx
+++ b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx
@@ -42,7 +42,7 @@ const PopupContainer: React.FC = ({ 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))
diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx
index 52a27180..41f2cbe2 100644
--- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx
+++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx
@@ -128,13 +128,19 @@ const ProviderSetting: FC = ({ 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)
diff --git a/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx
index d46afbe4..7b9297c6 100644
--- a/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx
+++ b/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx
@@ -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 = ({ 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) {
diff --git a/src/renderer/src/providers/OpenAIProvider.ts b/src/renderer/src/providers/OpenAIProvider.ts
index 525350a8..6f82579a 100644
--- a/src/renderer/src/providers/OpenAIProvider.ts
+++ b/src/renderer/src/providers/OpenAIProvider.ts
@@ -461,7 +461,7 @@ export default class OpenAIProvider extends BaseProvider {
public async getEmbeddingDimensions(model: Model): Promise {
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
}
diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts
index e08fa4f7..c8648c99 100644
--- a/src/renderer/src/services/ApiService.ts
+++ b/src/renderer/src/services/ApiService.ts
@@ -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) {
diff --git a/src/renderer/src/store/llm.ts b/src/renderer/src/store/llm.ts
index 943c8002..f98b5b42 100644
--- a/src/renderer/src/store/llm.ts
+++ b/src/renderer/src/store/llm.ts
@@ -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',
diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts
index 3d2b38ff..33fb5ace 100644
--- a/src/renderer/src/store/migrate.ts
+++ b/src/renderer/src/store/migrate.ts
@@ -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
}
}