@lobehub/chat 1.72.1 → 1.73.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/Dockerfile +2 -0
- package/Dockerfile.database +2 -0
- package/Dockerfile.pglite +2 -0
- package/README.md +4 -3
- package/README.zh-CN.md +4 -3
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/environment-variables/model-provider.mdx +16 -2
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +17 -2
- package/docs/usage/providers/ppio.zh-CN.mdx +1 -1
- package/locales/en-US/providers.json +3 -0
- package/locales/zh-CN/providers.json +4 -0
- package/package.json +3 -3
- package/packages/web-crawler/README.md +3 -3
- package/packages/web-crawler/README.zh-CN.md +3 -3
- package/packages/web-crawler/src/crawImpl/__tests__/search1api.test.ts +147 -0
- package/packages/web-crawler/src/crawImpl/index.ts +2 -0
- package/packages/web-crawler/src/crawImpl/naive.ts +2 -15
- package/packages/web-crawler/src/crawImpl/search1api.ts +83 -0
- package/packages/web-crawler/src/crawler.ts +1 -1
- package/packages/web-crawler/src/type.ts +1 -1
- package/packages/web-crawler/src/urlRules.ts +10 -0
- package/packages/web-crawler/src/utils/__tests__/withTimeout.test.ts +63 -0
- package/packages/web-crawler/src/utils/withTimeout.ts +20 -0
- package/src/app/[variants]/(main)/settings/llm/ProviderList/providers.tsx +2 -0
- package/src/config/aiModels/cohere.ts +243 -0
- package/src/config/aiModels/index.ts +3 -0
- package/src/config/llm.ts +6 -0
- package/src/config/modelProviders/cohere.ts +19 -0
- package/src/config/modelProviders/index.ts +5 -0
- package/src/libs/agent-runtime/cohere/index.ts +69 -0
- package/src/libs/agent-runtime/runtimeMap.ts +2 -0
- package/src/libs/agent-runtime/types/type.ts +1 -0
- package/src/types/user/settings/keyVaults.ts +1 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,57 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.73.1](https://github.com/lobehub/lobe-chat/compare/v1.73.0...v1.73.1)
|
6
|
+
|
7
|
+
<sup>Released on **2025-03-21**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Update shiki to v3.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Update shiki to v3, closes [#7069](https://github.com/lobehub/lobe-chat/issues/7069) ([03c95ab](https://github.com/lobehub/lobe-chat/commit/03c95ab))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
## [Version 1.73.0](https://github.com/lobehub/lobe-chat/compare/v1.72.1...v1.73.0)
|
31
|
+
|
32
|
+
<sup>Released on **2025-03-19**</sup>
|
33
|
+
|
34
|
+
#### ✨ Features
|
35
|
+
|
36
|
+
- **misc**: Add Cohere provider support, add search1api crawler implementation for WeChat Sogou links.
|
37
|
+
|
38
|
+
<br/>
|
39
|
+
|
40
|
+
<details>
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
42
|
+
|
43
|
+
#### What's improved
|
44
|
+
|
45
|
+
- **misc**: Add Cohere provider support, closes [#7016](https://github.com/lobehub/lobe-chat/issues/7016) ([2a4e2ed](https://github.com/lobehub/lobe-chat/commit/2a4e2ed))
|
46
|
+
- **misc**: Add search1api crawler implementation for WeChat Sogou links, closes [#7036](https://github.com/lobehub/lobe-chat/issues/7036) ([7327138](https://github.com/lobehub/lobe-chat/commit/7327138))
|
47
|
+
|
48
|
+
</details>
|
49
|
+
|
50
|
+
<div align="right">
|
51
|
+
|
52
|
+
[](#readme-top)
|
53
|
+
|
54
|
+
</div>
|
55
|
+
|
5
56
|
### [Version 1.72.1](https://github.com/lobehub/lobe-chat/compare/v1.72.0...v1.72.1)
|
6
57
|
|
7
58
|
<sup>Released on **2025-03-19**</sup>
|
package/Dockerfile
CHANGED
@@ -157,6 +157,8 @@ ENV \
|
|
157
157
|
BAICHUAN_API_KEY="" BAICHUAN_MODEL_LIST="" \
|
158
158
|
# Cloudflare
|
159
159
|
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
|
160
|
+
# Cohere
|
161
|
+
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
|
160
162
|
# DeepSeek
|
161
163
|
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
|
162
164
|
# Fireworks AI
|
package/Dockerfile.database
CHANGED
@@ -200,6 +200,8 @@ ENV \
|
|
200
200
|
BAICHUAN_API_KEY="" BAICHUAN_MODEL_LIST="" \
|
201
201
|
# Cloudflare
|
202
202
|
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
|
203
|
+
# Cohere
|
204
|
+
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
|
203
205
|
# DeepSeek
|
204
206
|
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
|
205
207
|
# Fireworks AI
|
package/Dockerfile.pglite
CHANGED
@@ -158,6 +158,8 @@ ENV \
|
|
158
158
|
BAICHUAN_API_KEY="" BAICHUAN_MODEL_LIST="" \
|
159
159
|
# Cloudflare
|
160
160
|
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
|
161
|
+
# Cohere
|
162
|
+
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
|
161
163
|
# DeepSeek
|
162
164
|
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
|
163
165
|
# Fireworks AI
|
package/README.md
CHANGED
@@ -191,13 +191,14 @@ We have implemented support for the following model service providers:
|
|
191
191
|
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock is a service provided by Amazon AWS, focusing on delivering advanced AI language and visual models for enterprises. Its model family includes Anthropic's Claude series, Meta's Llama 3.1 series, and more, offering a range of options from lightweight to high-performance, supporting tasks such as text generation, conversation, and image processing for businesses of varying scales and needs.
|
192
192
|
- **[Google](https://lobechat.com/discover/provider/google)**: Google's Gemini series represents its most advanced, versatile AI models, developed by Google DeepMind, designed for multimodal capabilities, supporting seamless understanding and processing of text, code, images, audio, and video. Suitable for various environments from data centers to mobile devices, it significantly enhances the efficiency and applicability of AI models.
|
193
193
|
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek is a company focused on AI technology research and application, with its latest model DeepSeek-V2.5 integrating general dialogue and code processing capabilities, achieving significant improvements in human preference alignment, writing tasks, and instruction following.
|
194
|
+
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc.
|
194
195
|
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: The HuggingFace Inference API provides a fast and free way for you to explore thousands of models for various tasks. Whether you are prototyping for a new application or experimenting with the capabilities of machine learning, this API gives you instant access to high-performance models across multiple domains.
|
195
196
|
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter is a service platform providing access to various cutting-edge large model interfaces, supporting OpenAI, Anthropic, LLaMA, and more, suitable for diverse development and application needs. Users can flexibly choose the optimal model and pricing based on their requirements, enhancing the AI experience.
|
196
197
|
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: Run serverless GPU-powered machine learning models on Cloudflare's global network.
|
197
|
-
- **[GitHub](https://lobechat.com/discover/provider/github)**: With GitHub Models, developers can become AI engineers and leverage the industry's leading AI models.
|
198
198
|
|
199
|
-
<details><summary><kbd>See more providers (+
|
199
|
+
<details><summary><kbd>See more providers (+28)</kbd></summary>
|
200
200
|
|
201
|
+
- **[GitHub](https://lobechat.com/discover/provider/github)**: With GitHub Models, developers can become AI engineers and leverage the industry's leading AI models.
|
201
202
|
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI is a platform providing a variety of large language models and AI image generation API services, flexible, reliable, and cost-effective. It supports the latest open-source models like Llama3 and Mistral, offering a comprehensive, user-friendly, and auto-scaling API solution for generative AI application development, suitable for the rapid growth of AI startups.
|
202
203
|
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc.
|
203
204
|
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI is dedicated to achieving leading performance through innovative AI models, offering extensive customization capabilities, including rapid scaling support and intuitive deployment processes to meet various enterprise needs.
|
@@ -228,7 +229,7 @@ We have implemented support for the following model service providers:
|
|
228
229
|
|
229
230
|
</details>
|
230
231
|
|
231
|
-
> 📊 Total providers: [<kbd>**
|
232
|
+
> 📊 Total providers: [<kbd>**38**</kbd>](https://lobechat.com/discover/providers)
|
232
233
|
|
233
234
|
<!-- PROVIDER LIST -->
|
234
235
|
|
package/README.zh-CN.md
CHANGED
@@ -191,13 +191,14 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
|
191
191
|
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock 是亚马逊 AWS 提供的一项服务,专注于为企业提供先进的 AI 语言模型和视觉模型。其模型家族包括 Anthropic 的 Claude 系列、Meta 的 Llama 3.1 系列等,涵盖从轻量级到高性能的多种选择,支持文本生成、对话、图像处理等多种任务,适用于不同规模和需求的企业应用。
|
192
192
|
- **[Google](https://lobechat.com/discover/provider/google)**: Google 的 Gemini 系列是其最先进、通用的 AI 模型,由 Google DeepMind 打造,专为多模态设计,支持文本、代码、图像、音频和视频的无缝理解与处理。适用于从数据中心到移动设备的多种环境,极大提升了 AI 模型的效率与应用广泛性。
|
193
193
|
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek 是一家专注于人工智能技术研究和应用的公司,其最新模型 DeepSeek-V3 多项评测成绩超越 Qwen2.5-72B 和 Llama-3.1-405B 等开源模型,性能对齐领军闭源模型 GPT-4o 与 Claude-3.5-Sonnet。
|
194
|
+
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO 派欧云提供稳定、高性价比的开源模型 API 服务,支持 DeepSeek 全系列、Llama、Qwen 等行业领先大模型。
|
194
195
|
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: HuggingFace Inference API 提供了一种快速且免费的方式,让您可以探索成千上万种模型,适用于各种任务。无论您是在为新应用程序进行原型设计,还是在尝试机器学习的功能,这个 API 都能让您即时访问多个领域的高性能模型。
|
195
196
|
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter 是一个提供多种前沿大模型接口的服务平台,支持 OpenAI、Anthropic、LLaMA 及更多,适合多样化的开发和应用需求。用户可根据自身需求灵活选择最优的模型和价格,助力 AI 体验的提升。
|
196
197
|
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: 在 Cloudflare 的全球网络上运行由无服务器 GPU 驱动的机器学习模型。
|
197
|
-
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过 GitHub 模型,开发人员可以成为 AI 工程师,并使用行业领先的 AI 模型进行构建。
|
198
198
|
|
199
|
-
<details><summary><kbd>See more providers (+
|
199
|
+
<details><summary><kbd>See more providers (+28)</kbd></summary>
|
200
200
|
|
201
|
+
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过 GitHub 模型,开发人员可以成为 AI 工程师,并使用行业领先的 AI 模型进行构建。
|
201
202
|
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI 是一个提供多种大语言模型与 AI 图像生成的 API 服务的平台,灵活、可靠且具有成本效益。它支持 Llama3、Mistral 等最新的开源模型,并为生成式 AI 应用开发提供了全面、用户友好且自动扩展的 API 解决方案,适合 AI 初创公司的快速发展。
|
202
203
|
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO 派欧云提供稳定、高性价比的开源模型 API 服务,支持 DeepSeek 全系列、Llama、Qwen 等行业领先大模型。
|
203
204
|
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI 致力于通过创新的 AI 模型实现领先的性能,提供广泛的自定义能力,包括快速扩展支持和直观的部署流程,满足企业的各种需求。
|
@@ -228,7 +229,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
|
228
229
|
|
229
230
|
</details>
|
230
231
|
|
231
|
-
> 📊 Total providers: [<kbd>**
|
232
|
+
> 📊 Total providers: [<kbd>**38**</kbd>](https://lobechat.com/discover/providers)
|
232
233
|
|
233
234
|
<!-- PROVIDER LIST -->
|
234
235
|
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"improvements": [
|
5
|
+
"Update shiki to v3."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-03-21",
|
9
|
+
"version": "1.73.1"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"features": [
|
14
|
+
"Add Cohere provider support, add search1api crawler implementation for WeChat Sogou links."
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"date": "2025-03-19",
|
18
|
+
"version": "1.73.0"
|
19
|
+
},
|
2
20
|
{
|
3
21
|
"children": {
|
4
22
|
"fixes": [
|
@@ -173,8 +173,8 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
173
173
|
|
174
174
|
- Type: Optional
|
175
175
|
- Description: If you manually configure the DeepSeek API proxy, you can use this configuration item to override the default DeepSeek API request base URL
|
176
|
-
- Default:
|
177
|
-
- Example: `https://
|
176
|
+
- Default: `https://api.deepseek.com`
|
177
|
+
- Example: `https://my-deepseek-proxy.com`
|
178
178
|
|
179
179
|
### `DEEPSEEK_API_KEY`
|
180
180
|
|
@@ -320,6 +320,13 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
320
320
|
- Default: -
|
321
321
|
- Example: `Y2xpdGhpMzNhZXNoYjVtdnZjMWc6bXNrLWIxQlk3aDNPaXpBWnc0V1RaMDhSRmRFVlpZUWY=`
|
322
322
|
|
323
|
+
### `MOONSHOT_PROXY_URL`
|
324
|
+
|
325
|
+
- Type: Optional
|
326
|
+
- Description: If you manually configure the Moonshot API proxy, you can use this configuration item to override the default Moonshot API request base URL
|
327
|
+
- Default: `https://api.moonshot.cn/v1`
|
328
|
+
- Example: `https://my-moonshot-proxy.com/v1`
|
329
|
+
|
323
330
|
## Perplexity AI
|
324
331
|
|
325
332
|
### `PERPLEXITY_API_KEY`
|
@@ -575,3 +582,10 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
575
582
|
|
576
583
|
|
577
584
|
[model-list]: /docs/self-hosting/advanced/model-list
|
585
|
+
|
586
|
+
### `VOLCENGINE_PROXY_URL`
|
587
|
+
|
588
|
+
- Type: Optional
|
589
|
+
- Description: If you manually configure the Volcengine API proxy, you can use this configuration item to override the default Volcengine API request base URL
|
590
|
+
- Default: `https://ark.cn-beijing.volces.com/api/v3`
|
591
|
+
- Example: `https://my-volcengine-proxy.com/v1`
|
@@ -171,8 +171,8 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
171
171
|
|
172
172
|
- 类型:可选
|
173
173
|
- 描述:如果您手动配置了 DeepSeek API 代理,可以使用此配置项覆盖默认的 DeepSeek API 请求基础 URL
|
174
|
-
-
|
175
|
-
-
|
174
|
+
- 默认值:`https://api.deepseek.com`
|
175
|
+
- 示例:`https://my-deepseek-proxy.com`
|
176
176
|
|
177
177
|
### `DEEPSEEK_API_KEY`
|
178
178
|
|
@@ -318,6 +318,14 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
318
318
|
- 默认值:-
|
319
319
|
- 示例:`Y2xpdGhpMzNhZXNoYjVtdnZjMWc6bXNrLWIxQlk3aDNPaXpBWnc0V1RaMDhSRmRFVlpZUWY=`
|
320
320
|
|
321
|
+
### `MOONSHOT_PROXY_URL`
|
322
|
+
|
323
|
+
- 类型:可选
|
324
|
+
- 描述:如果你手动配置了 Moonshot 接口代理,可以使用此配置项来覆盖默认的 Moonshot API 请求基础 URL
|
325
|
+
- 默认值:`https://api.moonshot.cn/v1`
|
326
|
+
- 示例:`https://my-moonshot-proxy.com/v1`
|
327
|
+
|
328
|
+
|
321
329
|
## Perplexity AI
|
322
330
|
|
323
331
|
### `PERPLEXITY_API_KEY`
|
@@ -572,3 +580,10 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
572
580
|
- 示例:`-all,+deepseek-r1->deepseek-r1-250120,+deepseek-v3->deepseek-v3-241226,+doubao-1.5-pro-256k->doubao-1-5-pro-256k-250115,+doubao-1.5-pro-32k->doubao-1-5-pro-32k-250115,+doubao-1.5-lite-32k->doubao-1-5-lite-32k-250115`
|
573
581
|
|
574
582
|
[model-list]: /zh/docs/self-hosting/advanced/model-list
|
583
|
+
|
584
|
+
### `VOLCENGINE_PROXY_URL`
|
585
|
+
|
586
|
+
- 类型:可选
|
587
|
+
- 描述:如果你手动配置了 Volcengine 接口代理,可以使用此配置项来覆盖默认的 Volcengine API 请求基础 URL
|
588
|
+
- 默认值:`https://ark.cn-beijing.volces.com/api/v3`
|
589
|
+
- 示例:`https://my-volcengine-proxy.com/v1`
|
@@ -142,5 +142,8 @@
|
|
142
142
|
},
|
143
143
|
"zhipu": {
|
144
144
|
"description": "Zhipu AI offers an open platform for multimodal and language models, supporting a wide range of AI application scenarios, including text processing, image understanding, and programming assistance."
|
145
|
+
},
|
146
|
+
"ppio": {
|
147
|
+
"description": "PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc."
|
145
148
|
}
|
146
149
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.73.1",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -135,7 +135,7 @@
|
|
135
135
|
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
136
136
|
"@lobehub/icons": "^1.73.1",
|
137
137
|
"@lobehub/tts": "^1.28.0",
|
138
|
-
"@lobehub/ui": "^1.165.
|
138
|
+
"@lobehub/ui": "^1.165.8",
|
139
139
|
"@neondatabase/serverless": "^0.10.4",
|
140
140
|
"@next/third-parties": "^15.2.0",
|
141
141
|
"@react-spring/web": "^9.7.5",
|
@@ -229,7 +229,7 @@
|
|
229
229
|
"rtl-detect": "^1.1.2",
|
230
230
|
"semver": "^7.6.3",
|
231
231
|
"sharp": "^0.33.5",
|
232
|
-
"shiki": "^
|
232
|
+
"shiki": "^3.2.1",
|
233
233
|
"stripe": "^16.12.0",
|
234
234
|
"superjson": "^2.2.2",
|
235
235
|
"svix": "^1.59.1",
|
@@ -9,7 +9,7 @@ LobeChat's built-in web crawling module for intelligent extraction of web conten
|
|
9
9
|
## 🛠️ Core Features
|
10
10
|
|
11
11
|
- **Intelligent Content Extraction**: Identifies main content based on Mozilla Readability algorithm
|
12
|
-
- **Multi-level Crawling Strategy**: Supports multiple crawling implementations including basic crawling, Jina, and Browserless rendering
|
12
|
+
- **Multi-level Crawling Strategy**: Supports multiple crawling implementations including basic crawling, Jina, Search1API, and Browserless rendering
|
13
13
|
- **Custom URL Rules**: Handles specific website crawling logic through a flexible rule system
|
14
14
|
|
15
15
|
## 🤝 Contribution
|
@@ -32,8 +32,8 @@ const url = [
|
|
32
32
|
// Optional: URL transformation, redirects to an easier-to-crawl version
|
33
33
|
urlTransform: 'https://example.com/print/$1',
|
34
34
|
|
35
|
-
// Optional: specify crawling implementation, supports 'naive', 'jina', and 'browserless'
|
36
|
-
impls: ['naive', 'jina', 'browserless'],
|
35
|
+
// Optional: specify crawling implementation, supports 'naive', 'jina', 'search1api', and 'browserless'
|
36
|
+
impls: ['naive', 'jina', 'search1api', 'browserless'],
|
37
37
|
|
38
38
|
// Optional: content filtering configuration
|
39
39
|
filterOptions: {
|
@@ -9,7 +9,7 @@ LobeChat 内置的网页抓取模块,用于智能提取网页内容并转换
|
|
9
9
|
## 🛠️ 核心功能
|
10
10
|
|
11
11
|
- **智能内容提取**:基于 Mozilla Readability 算法识别主要内容
|
12
|
-
- **多级抓取策略**:支持多种抓取实现,包括基础抓取、Jina 和 Browserless 渲染抓取
|
12
|
+
- **多级抓取策略**:支持多种抓取实现,包括基础抓取、Jina、Search1API 和 Browserless 渲染抓取
|
13
13
|
- **自定义 URL 规则**:通过灵活的规则系统处理特定网站的抓取逻辑
|
14
14
|
|
15
15
|
## 🤝 参与共建
|
@@ -32,8 +32,8 @@ const url = [
|
|
32
32
|
// 可选:URL 转换,用于重定向到更易抓取的版本
|
33
33
|
urlTransform: 'https://example.com/print/$1',
|
34
34
|
|
35
|
-
// 可选:指定抓取实现方式,支持 'naive'、'jina' 和 'browserless'
|
36
|
-
impls: ['naive', 'jina', 'browserless'],
|
35
|
+
// 可选:指定抓取实现方式,支持 'naive'、'jina'、'search1api' 和 'browserless' 四种
|
36
|
+
impls: ['naive', 'jina', 'search1api', 'browserless'],
|
37
37
|
|
38
38
|
// 可选:内容过滤配置
|
39
39
|
filterOptions: {
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
2
|
+
|
3
|
+
import * as withTimeoutModule from '../../utils/withTimeout';
|
4
|
+
import { NetworkConnectionError, PageNotFoundError, TimeoutError } from '../../utils/errorType';
|
5
|
+
import { search1api } from '../search1api';
|
6
|
+
|
7
|
+
describe('search1api crawler', () => {
|
8
|
+
// Mock fetch function
|
9
|
+
const mockFetch = vi.fn();
|
10
|
+
global.fetch = mockFetch;
|
11
|
+
|
12
|
+
// Original env
|
13
|
+
let originalEnv: NodeJS.ProcessEnv;
|
14
|
+
|
15
|
+
beforeEach(() => {
|
16
|
+
vi.resetAllMocks();
|
17
|
+
originalEnv = { ...process.env };
|
18
|
+
process.env.SEARCH1API_API_KEY = 'test-api-key';
|
19
|
+
|
20
|
+
// Mock withTimeout to directly return the promise
|
21
|
+
vi.spyOn(withTimeoutModule, 'withTimeout').mockImplementation((promise) => promise);
|
22
|
+
});
|
23
|
+
|
24
|
+
afterEach(() => {
|
25
|
+
process.env = originalEnv;
|
26
|
+
});
|
27
|
+
|
28
|
+
it('should throw error when API key is not set', async () => {
|
29
|
+
delete process.env.SEARCH1API_API_KEY;
|
30
|
+
|
31
|
+
await expect(search1api('https://example.com', { filterOptions: {} })).rejects.toThrow(
|
32
|
+
'SEARCH1API_API_KEY environment variable is not set',
|
33
|
+
);
|
34
|
+
});
|
35
|
+
|
36
|
+
it('should throw NetworkConnectionError when fetch fails', async () => {
|
37
|
+
mockFetch.mockRejectedValue(new Error('fetch failed'));
|
38
|
+
|
39
|
+
await expect(search1api('https://example.com', { filterOptions: {} })).rejects.toThrow(
|
40
|
+
NetworkConnectionError,
|
41
|
+
);
|
42
|
+
});
|
43
|
+
|
44
|
+
it('should throw TimeoutError when request times out', async () => {
|
45
|
+
// Restore original withTimeout implementation for this test
|
46
|
+
vi.spyOn(withTimeoutModule, 'withTimeout').mockRestore();
|
47
|
+
|
48
|
+
// Mock withTimeout to throw TimeoutError
|
49
|
+
vi.spyOn(withTimeoutModule, 'withTimeout').mockImplementation(() => {
|
50
|
+
throw new TimeoutError('Request timeout after 10000ms');
|
51
|
+
});
|
52
|
+
|
53
|
+
await expect(search1api('https://example.com', { filterOptions: {} })).rejects.toThrow(
|
54
|
+
TimeoutError,
|
55
|
+
);
|
56
|
+
});
|
57
|
+
|
58
|
+
it('should throw PageNotFoundError when status is 404', async () => {
|
59
|
+
mockFetch.mockResolvedValue({
|
60
|
+
ok: false,
|
61
|
+
status: 404,
|
62
|
+
statusText: 'Not Found',
|
63
|
+
});
|
64
|
+
|
65
|
+
await expect(search1api('https://example.com', { filterOptions: {} })).rejects.toThrow(
|
66
|
+
PageNotFoundError,
|
67
|
+
);
|
68
|
+
});
|
69
|
+
|
70
|
+
it('should throw error for other failed responses', async () => {
|
71
|
+
mockFetch.mockResolvedValue({
|
72
|
+
ok: false,
|
73
|
+
status: 500,
|
74
|
+
statusText: 'Internal Server Error',
|
75
|
+
});
|
76
|
+
|
77
|
+
await expect(search1api('https://example.com', { filterOptions: {} })).rejects.toThrow(
|
78
|
+
'Search1API request failed with status 500: Internal Server Error',
|
79
|
+
);
|
80
|
+
});
|
81
|
+
|
82
|
+
it('should return undefined when content is too short', async () => {
|
83
|
+
mockFetch.mockResolvedValue({
|
84
|
+
ok: true,
|
85
|
+
json: () => Promise.resolve({
|
86
|
+
crawlParameters: { url: 'https://example.com' },
|
87
|
+
results: {
|
88
|
+
title: 'Test Title',
|
89
|
+
link: 'https://example.com',
|
90
|
+
content: 'Short', // Less than 100 characters
|
91
|
+
},
|
92
|
+
}),
|
93
|
+
});
|
94
|
+
|
95
|
+
const result = await search1api('https://example.com', { filterOptions: {} });
|
96
|
+
expect(result).toBeUndefined();
|
97
|
+
});
|
98
|
+
|
99
|
+
it('should return crawl result on successful fetch', async () => {
|
100
|
+
const mockContent = 'This is a test content that is longer than 100 characters. '.repeat(3);
|
101
|
+
|
102
|
+
mockFetch.mockResolvedValue({
|
103
|
+
ok: true,
|
104
|
+
json: () => Promise.resolve({
|
105
|
+
crawlParameters: { url: 'https://example.com' },
|
106
|
+
results: {
|
107
|
+
title: 'Test Title',
|
108
|
+
link: 'https://example.com',
|
109
|
+
content: mockContent,
|
110
|
+
},
|
111
|
+
}),
|
112
|
+
});
|
113
|
+
|
114
|
+
const result = await search1api('https://example.com', { filterOptions: {} });
|
115
|
+
|
116
|
+
expect(mockFetch).toHaveBeenCalledWith('https://api.search1api.com/crawl', {
|
117
|
+
method: 'POST',
|
118
|
+
headers: {
|
119
|
+
'Authorization': 'Bearer test-api-key',
|
120
|
+
'Content-Type': 'application/json',
|
121
|
+
},
|
122
|
+
body: JSON.stringify({
|
123
|
+
url: 'https://example.com',
|
124
|
+
}),
|
125
|
+
});
|
126
|
+
|
127
|
+
expect(result).toEqual({
|
128
|
+
content: mockContent,
|
129
|
+
contentType: 'text',
|
130
|
+
title: 'Test Title',
|
131
|
+
description: 'Test Title',
|
132
|
+
length: mockContent.length,
|
133
|
+
siteName: 'example.com',
|
134
|
+
url: 'https://example.com',
|
135
|
+
});
|
136
|
+
});
|
137
|
+
|
138
|
+
it('should handle JSON parse errors', async () => {
|
139
|
+
mockFetch.mockResolvedValue({
|
140
|
+
ok: true,
|
141
|
+
json: () => Promise.reject(new Error('Invalid JSON')),
|
142
|
+
});
|
143
|
+
|
144
|
+
const result = await search1api('https://example.com', { filterOptions: {} });
|
145
|
+
expect(result).toBeUndefined();
|
146
|
+
});
|
147
|
+
});
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import { browserless } from './browserless';
|
2
2
|
import { jina } from './jina';
|
3
3
|
import { naive } from './naive';
|
4
|
+
import { search1api } from './search1api';
|
4
5
|
|
5
6
|
export const crawlImpls = {
|
6
7
|
browserless,
|
7
8
|
jina,
|
8
9
|
naive,
|
10
|
+
search1api,
|
9
11
|
};
|
10
12
|
|
11
13
|
export type CrawlImplType = keyof typeof crawlImpls;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { CrawlImpl, CrawlSuccessResult } from '../type';
|
2
2
|
import { NetworkConnectionError, PageNotFoundError, TimeoutError } from '../utils/errorType';
|
3
3
|
import { htmlToMarkdown } from '../utils/htmlToMarkdown';
|
4
|
+
import { DEFAULT_TIMEOUT, withTimeout } from '../utils/withTimeout';
|
4
5
|
|
5
6
|
const mixinHeaders = {
|
6
7
|
// 接受的内容类型
|
@@ -31,20 +32,6 @@ const mixinHeaders = {
|
|
31
32
|
'sec-fetch-user': '?1',
|
32
33
|
};
|
33
34
|
|
34
|
-
const TIMEOUT_CONTROL = 10_000;
|
35
|
-
|
36
|
-
const withTimeout = <T>(promise: Promise<T>, ms: number): Promise<T> => {
|
37
|
-
const controller = new AbortController();
|
38
|
-
const timeoutPromise = new Promise<T>((_, reject) => {
|
39
|
-
setTimeout(() => {
|
40
|
-
controller.abort();
|
41
|
-
reject(new TimeoutError(`Request timeout after ${ms}ms`));
|
42
|
-
}, ms);
|
43
|
-
});
|
44
|
-
|
45
|
-
return Promise.race([promise, timeoutPromise]);
|
46
|
-
};
|
47
|
-
|
48
35
|
export const naive: CrawlImpl = async (url, { filterOptions }) => {
|
49
36
|
let res: Response;
|
50
37
|
|
@@ -54,7 +41,7 @@ export const naive: CrawlImpl = async (url, { filterOptions }) => {
|
|
54
41
|
headers: mixinHeaders,
|
55
42
|
signal: new AbortController().signal,
|
56
43
|
}),
|
57
|
-
|
44
|
+
DEFAULT_TIMEOUT,
|
58
45
|
);
|
59
46
|
} catch (e) {
|
60
47
|
const error = e as Error;
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import { CrawlImpl, CrawlSuccessResult } from '../type';
|
2
|
+
import { NetworkConnectionError, PageNotFoundError, TimeoutError } from '../utils/errorType';
|
3
|
+
import { DEFAULT_TIMEOUT, withTimeout } from '../utils/withTimeout';
|
4
|
+
|
5
|
+
interface Search1ApiResponse {
|
6
|
+
crawlParameters: {
|
7
|
+
url: string;
|
8
|
+
};
|
9
|
+
results: {
|
10
|
+
title?: string;
|
11
|
+
link?: string;
|
12
|
+
content?: string;
|
13
|
+
};
|
14
|
+
}
|
15
|
+
|
16
|
+
export const search1api: CrawlImpl = async (url, { filterOptions }) => {
|
17
|
+
// Get API key from environment variable
|
18
|
+
const apiKey = process.env.SEARCH1API_API_KEY;
|
19
|
+
|
20
|
+
if (!apiKey) {
|
21
|
+
throw new Error('SEARCH1API_API_KEY environment variable is not set. Visit https://www.search1api.com to get free quota.');
|
22
|
+
}
|
23
|
+
|
24
|
+
let res: Response;
|
25
|
+
|
26
|
+
try {
|
27
|
+
res = await withTimeout(
|
28
|
+
fetch('https://api.search1api.com/crawl', {
|
29
|
+
method: 'POST',
|
30
|
+
headers: {
|
31
|
+
'Authorization': `Bearer ${apiKey}`,
|
32
|
+
'Content-Type': 'application/json',
|
33
|
+
},
|
34
|
+
body: JSON.stringify({
|
35
|
+
url,
|
36
|
+
}),
|
37
|
+
}),
|
38
|
+
DEFAULT_TIMEOUT,
|
39
|
+
);
|
40
|
+
} catch (e) {
|
41
|
+
const error = e as Error;
|
42
|
+
if (error.message === 'fetch failed') {
|
43
|
+
throw new NetworkConnectionError();
|
44
|
+
}
|
45
|
+
|
46
|
+
if (error instanceof TimeoutError) {
|
47
|
+
throw error;
|
48
|
+
}
|
49
|
+
|
50
|
+
throw e;
|
51
|
+
}
|
52
|
+
|
53
|
+
if (!res.ok) {
|
54
|
+
if (res.status === 404) {
|
55
|
+
throw new PageNotFoundError(res.statusText);
|
56
|
+
}
|
57
|
+
|
58
|
+
throw new Error(`Search1API request failed with status ${res.status}: ${res.statusText}`);
|
59
|
+
}
|
60
|
+
|
61
|
+
try {
|
62
|
+
const data = await res.json() as Search1ApiResponse;
|
63
|
+
|
64
|
+
// Check if content is empty or too short
|
65
|
+
if (!data.results.content || data.results.content.length < 100) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
|
69
|
+
return {
|
70
|
+
content: data.results.content,
|
71
|
+
contentType: 'text',
|
72
|
+
title: data.results.title,
|
73
|
+
description: data.results.title, // Using title as description since API doesn't provide a separate description
|
74
|
+
length: data.results.content.length,
|
75
|
+
siteName: new URL(url).hostname,
|
76
|
+
url: data.results.link || url,
|
77
|
+
} satisfies CrawlSuccessResult;
|
78
|
+
} catch (error) {
|
79
|
+
console.error(error);
|
80
|
+
}
|
81
|
+
|
82
|
+
return;
|
83
|
+
};
|
@@ -13,7 +13,7 @@ export class Crawler {
|
|
13
13
|
constructor(options: CrawlOptions = {}) {
|
14
14
|
this.impls = !!options.impls?.length
|
15
15
|
? (options.impls.filter((impl) => Object.keys(crawlImpls).includes(impl)) as CrawlImplType[])
|
16
|
-
: (['naive', 'jina', 'browserless'] as const);
|
16
|
+
: (['naive', 'jina', 'search1api','browserless'] as const);
|
17
17
|
}
|
18
18
|
|
19
19
|
/**
|
@@ -22,7 +22,7 @@ export interface FilterOptions {
|
|
22
22
|
pureText?: boolean;
|
23
23
|
}
|
24
24
|
|
25
|
-
type CrawlImplType = 'naive' | 'jina' | 'browserless';
|
25
|
+
type CrawlImplType = 'naive' | 'jina' | 'browserless' | 'search1api';
|
26
26
|
|
27
27
|
type CrawlImplParams<T> = T & {
|
28
28
|
filterOptions: FilterOptions;
|
@@ -1,6 +1,16 @@
|
|
1
1
|
import { CrawlUrlRule } from './type';
|
2
2
|
|
3
3
|
export const crawUrlRules: CrawlUrlRule[] = [
|
4
|
+
// 搜狗微信链接,使用 search1api
|
5
|
+
{
|
6
|
+
impls: ['search1api'],
|
7
|
+
urlPattern: 'https://weixin.sogou.com/link(.*)',
|
8
|
+
},
|
9
|
+
// 搜狗链接,使用 search1api
|
10
|
+
{
|
11
|
+
impls: ['search1api'],
|
12
|
+
urlPattern: 'https://sogou.com/link(.*)',
|
13
|
+
},
|
4
14
|
// github 源码解析
|
5
15
|
{
|
6
16
|
filterOptions: {
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
2
|
+
|
3
|
+
import { TimeoutError } from '../errorType';
|
4
|
+
import { DEFAULT_TIMEOUT, withTimeout } from '../withTimeout';
|
5
|
+
|
6
|
+
describe('withTimeout', () => {
|
7
|
+
beforeEach(() => {
|
8
|
+
vi.useFakeTimers();
|
9
|
+
});
|
10
|
+
|
11
|
+
afterEach(() => {
|
12
|
+
vi.useRealTimers();
|
13
|
+
});
|
14
|
+
|
15
|
+
it('should resolve when promise resolves before timeout', async () => {
|
16
|
+
const promise = Promise.resolve('success');
|
17
|
+
const result = await withTimeout(promise, 1000);
|
18
|
+
expect(result).toBe('success');
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should reject with TimeoutError when promise takes too long', async () => {
|
22
|
+
const slowPromise = new Promise((resolve) => {
|
23
|
+
setTimeout(() => resolve('too late'), 200);
|
24
|
+
});
|
25
|
+
|
26
|
+
const timeoutPromise = withTimeout(slowPromise, 100);
|
27
|
+
vi.advanceTimersByTime(100);
|
28
|
+
|
29
|
+
await expect(timeoutPromise).rejects.toThrow(TimeoutError);
|
30
|
+
await expect(timeoutPromise).rejects.toThrow('Request timeout after 100ms');
|
31
|
+
});
|
32
|
+
|
33
|
+
it('should use DEFAULT_TIMEOUT when no timeout specified', async () => {
|
34
|
+
const slowPromise = new Promise((resolve) => {
|
35
|
+
setTimeout(() => resolve('success'), DEFAULT_TIMEOUT + 100);
|
36
|
+
});
|
37
|
+
|
38
|
+
const timeoutPromise = withTimeout(slowPromise);
|
39
|
+
vi.advanceTimersByTime(DEFAULT_TIMEOUT);
|
40
|
+
|
41
|
+
await expect(timeoutPromise).rejects.toThrow(TimeoutError);
|
42
|
+
await expect(timeoutPromise).rejects.toThrow(`Request timeout after ${DEFAULT_TIMEOUT}ms`);
|
43
|
+
});
|
44
|
+
|
45
|
+
it('should reject with original error if promise rejects before timeout', async () => {
|
46
|
+
const error = new Error('Original error');
|
47
|
+
const failingPromise = Promise.reject(error);
|
48
|
+
|
49
|
+
await expect(withTimeout(failingPromise, 1000)).rejects.toThrow('Original error');
|
50
|
+
});
|
51
|
+
|
52
|
+
it('should abort controller when timeout occurs', async () => {
|
53
|
+
const controller = new AbortController();
|
54
|
+
const slowPromise = new Promise((resolve) => {
|
55
|
+
setTimeout(() => resolve('too late'), 2000);
|
56
|
+
});
|
57
|
+
|
58
|
+
const timeoutPromise = withTimeout(slowPromise, 1000);
|
59
|
+
vi.advanceTimersByTime(1000);
|
60
|
+
|
61
|
+
await expect(timeoutPromise).rejects.toThrow(TimeoutError);
|
62
|
+
});
|
63
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { TimeoutError } from "./errorType";
|
2
|
+
export const DEFAULT_TIMEOUT = 10_000;
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Wraps a promise with a timeout
|
6
|
+
* @param promise Promise to wrap
|
7
|
+
* @param ms Timeout in milliseconds
|
8
|
+
* @returns Promise that will be rejected if it takes longer than ms to resolve
|
9
|
+
*/
|
10
|
+
export const withTimeout = <T>(promise: Promise<T>, ms: number = DEFAULT_TIMEOUT): Promise<T> => {
|
11
|
+
const controller = new AbortController();
|
12
|
+
const timeoutPromise = new Promise<T>((_, reject) => {
|
13
|
+
setTimeout(() => {
|
14
|
+
controller.abort();
|
15
|
+
reject(new TimeoutError(`Request timeout after ${ms}ms`));
|
16
|
+
}, ms);
|
17
|
+
});
|
18
|
+
|
19
|
+
return Promise.race([promise, timeoutPromise]);
|
20
|
+
};
|
@@ -5,6 +5,7 @@ import {
|
|
5
5
|
Ai360ProviderCard,
|
6
6
|
AnthropicProviderCard,
|
7
7
|
BaichuanProviderCard,
|
8
|
+
CohereProviderCard,
|
8
9
|
DeepSeekProviderCard,
|
9
10
|
FireworksAIProviderCard,
|
10
11
|
GiteeAIProviderCard,
|
@@ -82,6 +83,7 @@ export const useProviderList = (): ProviderItem[] => {
|
|
82
83
|
XAIProviderCard,
|
83
84
|
JinaProviderCard,
|
84
85
|
SambaNovaProviderCard,
|
86
|
+
CohereProviderCard,
|
85
87
|
QwenProviderCard,
|
86
88
|
WenxinProviderCard,
|
87
89
|
HunyuanProviderCard,
|
@@ -0,0 +1,243 @@
|
|
1
|
+
import { AIChatModelCard } from '@/types/aiModel';
|
2
|
+
|
3
|
+
const cohereChatModels: AIChatModelCard[] = [
|
4
|
+
{
|
5
|
+
abilities: {
|
6
|
+
functionCall: true,
|
7
|
+
},
|
8
|
+
contextWindowTokens: 256_000,
|
9
|
+
description:
|
10
|
+
'Command A 是我们迄今为止性能最强的模型,在工具使用、代理、检索增强生成(RAG)和多语言应用场景方面表现出色。Command A 具有 256K 的上下文长度,仅需两块 GPU 即可运行,并且相比于 Command R+ 08-2024,吞吐量提高了 150%。',
|
11
|
+
displayName: 'Command A 03-2025',
|
12
|
+
enabled: true,
|
13
|
+
id: 'command-a-03-2025',
|
14
|
+
maxOutput: 8000,
|
15
|
+
pricing: {
|
16
|
+
input: 2.5,
|
17
|
+
output: 10
|
18
|
+
},
|
19
|
+
type: 'chat'
|
20
|
+
},
|
21
|
+
{
|
22
|
+
abilities: {
|
23
|
+
functionCall: true,
|
24
|
+
},
|
25
|
+
contextWindowTokens: 128_000,
|
26
|
+
description:
|
27
|
+
'command-r-plus 是 command-r-plus-04-2024 的别名,因此如果您在 API 中使用 command-r-plus,实际上指向的就是该模型。',
|
28
|
+
displayName: 'Command R+',
|
29
|
+
enabled: true,
|
30
|
+
id: 'command-r-plus',
|
31
|
+
maxOutput: 4000,
|
32
|
+
pricing: {
|
33
|
+
input: 2.5,
|
34
|
+
output: 10
|
35
|
+
},
|
36
|
+
type: 'chat'
|
37
|
+
},
|
38
|
+
{
|
39
|
+
abilities: {
|
40
|
+
functionCall: true,
|
41
|
+
},
|
42
|
+
contextWindowTokens: 128_000,
|
43
|
+
description:
|
44
|
+
'Command R+ 是一个遵循指令的对话模型,在语言任务方面表现出更高的质量、更可靠,并且相比以往模型具有更长的上下文长度。它最适用于复杂的 RAG 工作流和多步工具使用。',
|
45
|
+
displayName: 'Command R+ 04-2024',
|
46
|
+
id: 'command-r-plus-04-2024',
|
47
|
+
maxOutput: 4000,
|
48
|
+
pricing: {
|
49
|
+
input: 3,
|
50
|
+
output: 15
|
51
|
+
},
|
52
|
+
type: 'chat'
|
53
|
+
},
|
54
|
+
{
|
55
|
+
abilities: {
|
56
|
+
functionCall: true,
|
57
|
+
},
|
58
|
+
contextWindowTokens: 128_000,
|
59
|
+
description:
|
60
|
+
'command-r 是 command-c-03-2024 的别名,因此如果您在 API 中使用 command-r,实际上指向的就是该模型。',
|
61
|
+
displayName: 'Command R',
|
62
|
+
enabled: true,
|
63
|
+
id: 'command-r',
|
64
|
+
maxOutput: 4000,
|
65
|
+
pricing: {
|
66
|
+
input: 0.15,
|
67
|
+
output: 0.6
|
68
|
+
},
|
69
|
+
type: 'chat'
|
70
|
+
},
|
71
|
+
{
|
72
|
+
abilities: {
|
73
|
+
functionCall: true,
|
74
|
+
},
|
75
|
+
contextWindowTokens: 128_000,
|
76
|
+
description:
|
77
|
+
'command-r-08-2024 是 Command R 模型的更新版本,于 2024 年 8 月发布。',
|
78
|
+
displayName: 'Command R 08-2024',
|
79
|
+
id: 'command-r-08-2024',
|
80
|
+
maxOutput: 4000,
|
81
|
+
pricing: {
|
82
|
+
input: 0.15,
|
83
|
+
output: 0.6
|
84
|
+
},
|
85
|
+
type: 'chat'
|
86
|
+
},
|
87
|
+
{
|
88
|
+
abilities: {
|
89
|
+
functionCall: true,
|
90
|
+
},
|
91
|
+
contextWindowTokens: 128_000,
|
92
|
+
description:
|
93
|
+
'Command R 是一个遵循指令的对话模型,在语言任务方面表现出更高的质量、更可靠,并且相比以往模型具有更长的上下文长度。它可用于复杂的工作流程,如代码生成、检索增强生成(RAG)、工具使用和代理。',
|
94
|
+
displayName: 'Command R 03-2024',
|
95
|
+
id: 'command-r-03-2024',
|
96
|
+
maxOutput: 4000,
|
97
|
+
pricing: {
|
98
|
+
input: 0.5,
|
99
|
+
output: 1.5
|
100
|
+
},
|
101
|
+
type: 'chat'
|
102
|
+
},
|
103
|
+
{
|
104
|
+
abilities: {
|
105
|
+
functionCall: true,
|
106
|
+
},
|
107
|
+
contextWindowTokens: 128_000,
|
108
|
+
description:
|
109
|
+
'command-r7b-12-2024 是一个小型且高效的更新版本,于 2024 年 12 月发布。它在 RAG、工具使用、代理等需要复杂推理和多步处理的任务中表现出色。',
|
110
|
+
displayName: 'Command R7B 12-2024',
|
111
|
+
enabled: true,
|
112
|
+
id: 'command-r7b-12-2024',
|
113
|
+
maxOutput: 4000,
|
114
|
+
pricing: {
|
115
|
+
input: 0.0375,
|
116
|
+
output: 0.15
|
117
|
+
},
|
118
|
+
type: 'chat'
|
119
|
+
},
|
120
|
+
{
|
121
|
+
contextWindowTokens: 4000,
|
122
|
+
description:
|
123
|
+
'一个遵循指令的对话模型,在语言任务中表现出高质量、更可靠,并且相比我们的基础生成模型具有更长的上下文长度。',
|
124
|
+
displayName: 'Command',
|
125
|
+
enabled: true,
|
126
|
+
id: 'command',
|
127
|
+
maxOutput: 4000,
|
128
|
+
pricing: {
|
129
|
+
input: 1,
|
130
|
+
output: 2
|
131
|
+
},
|
132
|
+
type: 'chat'
|
133
|
+
},
|
134
|
+
{
|
135
|
+
abilities: {
|
136
|
+
functionCall: true,
|
137
|
+
},
|
138
|
+
contextWindowTokens: 128_000,
|
139
|
+
description:
|
140
|
+
'为了缩短主要版本发布之间的时间间隔,我们推出了 Command 模型的每夜版本。对于 Command 系列,这一版本称为 command-cightly。请注意,command-nightly 是最新、最具实验性且(可能)不稳定的版本。每夜版本会定期更新,且不会提前通知,因此不建议在生产环境中使用。',
|
141
|
+
displayName: 'Command Nightly',
|
142
|
+
id: 'command-nightly',
|
143
|
+
maxOutput: 4000,
|
144
|
+
pricing: {
|
145
|
+
input: 1,
|
146
|
+
output: 2
|
147
|
+
},
|
148
|
+
type: 'chat'
|
149
|
+
},
|
150
|
+
{
|
151
|
+
contextWindowTokens: 4000,
|
152
|
+
description:
|
153
|
+
'一个更小、更快的 Command 版本,几乎同样强大,但速度更快。',
|
154
|
+
displayName: 'Command Light',
|
155
|
+
enabled: true,
|
156
|
+
id: 'command-light',
|
157
|
+
maxOutput: 4000,
|
158
|
+
pricing: {
|
159
|
+
input: 0.3,
|
160
|
+
output: 0.6
|
161
|
+
},
|
162
|
+
type: 'chat'
|
163
|
+
},
|
164
|
+
{
|
165
|
+
contextWindowTokens: 4000,
|
166
|
+
description:
|
167
|
+
'为了缩短主要版本发布之间的时间间隔,我们推出了 Command 模型的每夜版本。对于 command-light 系列,这一版本称为 command-light-nightly。请注意,command-light-nightly 是最新、最具实验性且(可能)不稳定的版本。每夜版本会定期更新,且不会提前通知,因此不建议在生产环境中使用。',
|
168
|
+
displayName: 'Command Light Nightly',
|
169
|
+
id: 'command-light-nightly',
|
170
|
+
maxOutput: 4000,
|
171
|
+
pricing: {
|
172
|
+
input: 0.3,
|
173
|
+
output: 0.6
|
174
|
+
},
|
175
|
+
type: 'chat'
|
176
|
+
},
|
177
|
+
{
|
178
|
+
contextWindowTokens: 128_000,
|
179
|
+
description:
|
180
|
+
'Aya Expanse 是一款高性能的 32B 多语言模型,旨在通过指令调优、数据套利、偏好训练和模型合并的创新,挑战单语言模型的表现。它支持 23 种语言。',
|
181
|
+
displayName: 'Aya Expanse 32B',
|
182
|
+
enabled: true,
|
183
|
+
id: 'c4ai-aya-expanse-32b',
|
184
|
+
maxOutput: 4000,
|
185
|
+
pricing: {
|
186
|
+
input: 0.5,
|
187
|
+
output: 1.5
|
188
|
+
},
|
189
|
+
type: 'chat'
|
190
|
+
},
|
191
|
+
{
|
192
|
+
contextWindowTokens: 8000,
|
193
|
+
description:
|
194
|
+
'Aya Expanse 是一款高性能的 8B 多语言模型,旨在通过指令调优、数据套利、偏好训练和模型合并的创新,挑战单语言模型的表现。它支持 23 种语言。',
|
195
|
+
displayName: 'Aya Expanse 8B',
|
196
|
+
enabled: true,
|
197
|
+
id: 'c4ai-aya-expanse-8b',
|
198
|
+
maxOutput: 4000,
|
199
|
+
pricing: {
|
200
|
+
input: 0.5,
|
201
|
+
output: 1.5
|
202
|
+
},
|
203
|
+
type: 'chat'
|
204
|
+
},
|
205
|
+
{
|
206
|
+
abilities: {
|
207
|
+
vision: true,
|
208
|
+
},
|
209
|
+
contextWindowTokens: 16_000,
|
210
|
+
description:
|
211
|
+
'Aya Vision 是一款最先进的多模态模型,在语言、文本和图像能力的多个关键基准上表现出色。它支持 23 种语言。这个 320 亿参数的版本专注于最先进的多语言表现。',
|
212
|
+
displayName: 'Aya Vision 32B',
|
213
|
+
enabled: true,
|
214
|
+
id: 'c4ai-aya-vision-32b',
|
215
|
+
maxOutput: 4000,
|
216
|
+
pricing: {
|
217
|
+
input: 0.5,
|
218
|
+
output: 1.5
|
219
|
+
},
|
220
|
+
type: 'chat'
|
221
|
+
},
|
222
|
+
{
|
223
|
+
abilities: {
|
224
|
+
vision: true,
|
225
|
+
},
|
226
|
+
contextWindowTokens: 16_000,
|
227
|
+
description:
|
228
|
+
'Aya Vision 是一款最先进的多模态模型,在语言、文本和图像能力的多个关键基准上表现出色。这个 80 亿参数的版本专注于低延迟和最佳性能。',
|
229
|
+
displayName: 'Aya Vision 8B',
|
230
|
+
enabled: true,
|
231
|
+
id: 'c4ai-aya-vision-8b',
|
232
|
+
maxOutput: 4000,
|
233
|
+
pricing: {
|
234
|
+
input: 0.5,
|
235
|
+
output: 1.5
|
236
|
+
},
|
237
|
+
type: 'chat'
|
238
|
+
},
|
239
|
+
]
|
240
|
+
|
241
|
+
export const allModels = [...cohereChatModels];
|
242
|
+
|
243
|
+
export default allModels;
|
@@ -8,6 +8,7 @@ import { default as azureai } from './azureai';
|
|
8
8
|
import { default as baichuan } from './baichuan';
|
9
9
|
import { default as bedrock } from './bedrock';
|
10
10
|
import { default as cloudflare } from './cloudflare';
|
11
|
+
import { default as cohere } from './cohere';
|
11
12
|
import { default as deepseek } from './deepseek';
|
12
13
|
import { default as doubao } from './doubao';
|
13
14
|
import { default as fireworksai } from './fireworksai';
|
@@ -77,6 +78,7 @@ export const LOBE_DEFAULT_MODEL_LIST = buildDefaultModelList({
|
|
77
78
|
baichuan,
|
78
79
|
bedrock,
|
79
80
|
cloudflare,
|
81
|
+
cohere,
|
80
82
|
deepseek,
|
81
83
|
doubao,
|
82
84
|
fireworksai,
|
@@ -127,6 +129,7 @@ export { default as azureai } from './azureai';
|
|
127
129
|
export { default as baichuan } from './baichuan';
|
128
130
|
export { default as bedrock } from './bedrock';
|
129
131
|
export { default as cloudflare } from './cloudflare';
|
132
|
+
export { default as cohere } from './cohere';
|
130
133
|
export { default as deepseek } from './deepseek';
|
131
134
|
export { default as doubao } from './doubao';
|
132
135
|
export { default as fireworksai } from './fireworksai';
|
package/src/config/llm.ts
CHANGED
@@ -150,6 +150,9 @@ export const getLLMConfig = () => {
|
|
150
150
|
|
151
151
|
ENABLED_PPIO: z.boolean(),
|
152
152
|
PPIO_API_KEY: z.string().optional(),
|
153
|
+
|
154
|
+
ENABLED_COHERE: z.boolean(),
|
155
|
+
COHERE_API_KEY: z.string().optional(),
|
153
156
|
},
|
154
157
|
runtimeEnv: {
|
155
158
|
API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
|
@@ -298,6 +301,9 @@ export const getLLMConfig = () => {
|
|
298
301
|
|
299
302
|
ENABLED_PPIO: !!process.env.PPIO_API_KEY,
|
300
303
|
PPIO_API_KEY: process.env.PPIO_API_KEY,
|
304
|
+
|
305
|
+
ENABLED_COHERE: !!process.env.COHERE_API_KEY,
|
306
|
+
COHERE_API_KEY: process.env.COHERE_API_KEY,
|
301
307
|
},
|
302
308
|
});
|
303
309
|
};
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { ModelProviderCard } from '@/types/llm';
|
2
|
+
|
3
|
+
const Cohere: ModelProviderCard = {
|
4
|
+
chatModels: [],
|
5
|
+
checkModel: 'command-r7b-12-2024',
|
6
|
+
description: 'Cohere 为您带来最前沿的多语言模型、先进的检索功能以及为现代企业量身定制的 AI 工作空间 — 一切都集成在一个安全的平台中。',
|
7
|
+
id: 'cohere',
|
8
|
+
modelsUrl: 'https://docs.cohere.com/v2/docs/models',
|
9
|
+
name: 'Cohere',
|
10
|
+
settings: {
|
11
|
+
proxyUrl: {
|
12
|
+
placeholder: 'https://api.cohere.ai/compatibility/v1',
|
13
|
+
},
|
14
|
+
sdkType: 'openai',
|
15
|
+
},
|
16
|
+
url: 'https://cohere.com',
|
17
|
+
};
|
18
|
+
|
19
|
+
export default Cohere;
|
@@ -8,6 +8,7 @@ import AzureAIProvider from './azureai';
|
|
8
8
|
import BaichuanProvider from './baichuan';
|
9
9
|
import BedrockProvider from './bedrock';
|
10
10
|
import CloudflareProvider from './cloudflare';
|
11
|
+
import CohereProvider from './cohere';
|
11
12
|
import DeepSeekProvider from './deepseek';
|
12
13
|
import DoubaoProvider from './doubao';
|
13
14
|
import FireworksAIProvider from './fireworksai';
|
@@ -75,6 +76,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
|
|
75
76
|
XAIProvider.chatModels,
|
76
77
|
JinaProvider.chatModels,
|
77
78
|
SambaNovaProvider.chatModels,
|
79
|
+
CohereProvider.chatModels,
|
78
80
|
ZeroOneProvider.chatModels,
|
79
81
|
StepfunProvider.chatModels,
|
80
82
|
NovitaProvider.chatModels,
|
@@ -107,6 +109,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
107
109
|
GoogleProvider,
|
108
110
|
VertexAIProvider,
|
109
111
|
DeepSeekProvider,
|
112
|
+
PPIOProvider,
|
110
113
|
HuggingFaceProvider,
|
111
114
|
OpenRouterProvider,
|
112
115
|
CloudflareProvider,
|
@@ -124,6 +127,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
124
127
|
XAIProvider,
|
125
128
|
JinaProvider,
|
126
129
|
SambaNovaProvider,
|
130
|
+
CohereProvider,
|
127
131
|
QwenProvider,
|
128
132
|
WenxinProvider,
|
129
133
|
TencentcloudProvider,
|
@@ -164,6 +168,7 @@ export { default as AzureAIProviderCard } from './azureai';
|
|
164
168
|
export { default as BaichuanProviderCard } from './baichuan';
|
165
169
|
export { default as BedrockProviderCard } from './bedrock';
|
166
170
|
export { default as CloudflareProviderCard } from './cloudflare';
|
171
|
+
export { default as CohereProviderCard } from './cohere';
|
167
172
|
export { default as DeepSeekProviderCard } from './deepseek';
|
168
173
|
export { default as DoubaoProviderCard } from './doubao';
|
169
174
|
export { default as FireworksAIProviderCard } from './fireworksai';
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { ModelProvider } from '../types';
|
2
|
+
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
3
|
+
|
4
|
+
import type { ChatModelCard } from '@/types/llm';
|
5
|
+
|
6
|
+
export interface CohereModelCard {
|
7
|
+
context_length: number;
|
8
|
+
features: string[] | null;
|
9
|
+
name: string;
|
10
|
+
supports_vision: boolean;
|
11
|
+
}
|
12
|
+
|
13
|
+
export const LobeCohereAI = LobeOpenAICompatibleFactory({
|
14
|
+
baseURL: 'https://api.cohere.ai/compatibility/v1',
|
15
|
+
chatCompletion: {
|
16
|
+
// https://docs.cohere.com/v2/docs/compatibility-api#unsupported-parameters
|
17
|
+
excludeUsage: true,
|
18
|
+
handlePayload: (payload) => {
|
19
|
+
const { frequency_penalty, presence_penalty, top_p, ...rest } = payload;
|
20
|
+
|
21
|
+
return {
|
22
|
+
...rest,
|
23
|
+
frequency_penalty:
|
24
|
+
frequency_penalty !== undefined && frequency_penalty > 0 && frequency_penalty <= 1
|
25
|
+
? frequency_penalty
|
26
|
+
: undefined,
|
27
|
+
presence_penalty:
|
28
|
+
presence_penalty !== undefined && presence_penalty > 0 && presence_penalty <= 1
|
29
|
+
? presence_penalty
|
30
|
+
: undefined,
|
31
|
+
top_p: top_p !== undefined && top_p > 0 && top_p < 1 ? top_p : undefined,
|
32
|
+
} as any;
|
33
|
+
},
|
34
|
+
noUserId: true,
|
35
|
+
},
|
36
|
+
debug: {
|
37
|
+
chatCompletion: () => process.env.DEBUG_COHERE_CHAT_COMPLETION === '1',
|
38
|
+
},
|
39
|
+
models: async ({ client }) => {
|
40
|
+
const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
|
41
|
+
|
42
|
+
client.baseURL = 'https://api.cohere.com/v1';
|
43
|
+
|
44
|
+
const modelsPage = await client.models.list() as any;
|
45
|
+
const modelList: CohereModelCard[] = modelsPage.body.models;
|
46
|
+
|
47
|
+
return modelList
|
48
|
+
.map((model) => {
|
49
|
+
const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase());
|
50
|
+
|
51
|
+
return {
|
52
|
+
contextWindowTokens: model.context_length,
|
53
|
+
displayName: knownModel?.displayName ?? undefined,
|
54
|
+
enabled: knownModel?.enabled || false,
|
55
|
+
functionCall:
|
56
|
+
(model.features && model.features.includes("tools"))
|
57
|
+
|| knownModel?.abilities?.functionCall
|
58
|
+
|| false,
|
59
|
+
id: model.name,
|
60
|
+
vision:
|
61
|
+
model.supports_vision
|
62
|
+
|| knownModel?.abilities?.vision
|
63
|
+
|| false,
|
64
|
+
};
|
65
|
+
})
|
66
|
+
.filter(Boolean) as ChatModelCard[];
|
67
|
+
},
|
68
|
+
provider: ModelProvider.Cohere,
|
69
|
+
});
|
@@ -6,6 +6,7 @@ import { LobeAzureAI } from './azureai';
|
|
6
6
|
import { LobeBaichuanAI } from './baichuan';
|
7
7
|
import LobeBedrockAI from './bedrock';
|
8
8
|
import { LobeCloudflareAI } from './cloudflare';
|
9
|
+
import { LobeCohereAI } from './cohere';
|
9
10
|
import { LobeDeepSeekAI } from './deepseek';
|
10
11
|
import { LobeFireworksAI } from './fireworksai';
|
11
12
|
import { LobeGiteeAI } from './giteeai';
|
@@ -54,6 +55,7 @@ export const providerRuntimeMap = {
|
|
54
55
|
baichuan: LobeBaichuanAI,
|
55
56
|
bedrock: LobeBedrockAI,
|
56
57
|
cloudflare: LobeCloudflareAI,
|
58
|
+
cohere: LobeCohereAI,
|
57
59
|
deepseek: LobeDeepSeekAI,
|
58
60
|
doubao: LobeVolcengineAI,
|
59
61
|
fireworksai: LobeFireworksAI,
|
@@ -41,6 +41,7 @@ export interface UserKeyVaults extends SearchEngineKeyVaults {
|
|
41
41
|
baichuan?: OpenAICompatibleKeyVault;
|
42
42
|
bedrock?: AWSBedrockKeyVault;
|
43
43
|
cloudflare?: CloudflareKeyVault;
|
44
|
+
cohere?: OpenAICompatibleKeyVault;
|
44
45
|
deepseek?: OpenAICompatibleKeyVault;
|
45
46
|
doubao?: OpenAICompatibleKeyVault;
|
46
47
|
fireworksai?: OpenAICompatibleKeyVault;
|