Compare commits

...

21 Commits

Author SHA1 Message Date
Junyan Qin
b70001c579 chore: release v4.1.2 2025-08-03 22:52:47 +08:00
Junyan Qin (Chin)
4a8f5516f6 feat: add new api requester (#1596) 2025-08-03 22:30:52 +08:00
Junyan Qin
f1ac9c77e6 doc: update README_TW 2025-07-28 15:50:00 +08:00
Junyan Qin
b434a4e3d7 doc: add README_TW 2025-07-28 15:47:50 +08:00
Junyan Qin
2f209cd59f chore(i18n): add zh-Hant 2025-07-28 15:11:41 +08:00
Junyan Qin
0f585fd5ef fix(moonshot): make api.moonshot.ai the default api base url 2025-07-26 22:23:33 +08:00
Junyan Qin
a152dece9a chore: switch to pnpm 2025-07-26 19:45:38 +08:00
Junyan Qin
d3b31f7027 chore: release v4.1.1 2025-07-26 19:28:34 +08:00
How-Sean Xin
c00f05fca4 Add GitHub link redirection for front-end plugin cards (#1579)
* Update package.json

* Update PluginMarketComponent.tsx

* Update PluginMarketComponent.tsx

* Update package.json

* Update PluginCardComponent.tsx

* perf: no display github button when plugin has no github url

---------

Co-authored-by: Junyan Qin <rockchinq@gmail.com>
2025-07-26 19:22:00 +08:00
Junyan Qin
92c3a86356 feat: add qhaigc 2025-07-24 22:42:26 +08:00
Junyan Qin
341fdc409d perf: embedding model ui 2025-07-24 22:29:25 +08:00
Junyan Qin
ebd542f592 feat: 302.AI embeddings 2025-07-24 22:05:15 +08:00
Junyan Qin
194b2d9814 feat: supports more embedding providers 2025-07-24 22:03:20 +08:00
Junyan Qin
7aed5cf1ed feat: ollama embeddings models 2025-07-24 10:36:32 +08:00
Junyan Qin
abc88c4979 doc: update README 2025-07-23 18:53:15 +08:00
gaord
6754666845 feat(wechatpad): 添加对@所有人的支持并统一处理消息派发 (#1588)
在消息转换器中添加对AtAll组件的支持,将@所有人转换为特定格式。同时在消息派发时统一处理@所有人的情况,确保通知能正确发送。
2025-07-23 15:22:04 +08:00
Junyan Qin
08e6f46b19 fix(deps): react-focus-scope pkg bug 2025-07-22 11:05:16 +08:00
Junyan Qin
1497fdae56 doc(README): adjust structure 2025-07-20 22:10:32 +08:00
Junyan Qin
10a3cb40e1 perf(retrieve): ui 2025-07-20 17:57:33 +08:00
devin-ai-integration[bot]
dd1ec15a39 feat: add knowledge base retrieve test tab with Card-based UI (#1583)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>
2025-07-20 17:56:46 +08:00
devin-ai-integration[bot]
ea51cec57e feat: add pipeline sorting functionality with three sort options (#1582)
* feat: add pipeline sorting functionality with three sort options

Co-Authored-By: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>

* perf: ui

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>
2025-07-20 17:23:30 +08:00
43 changed files with 1290 additions and 224 deletions

View File

@@ -6,7 +6,7 @@
<div align="center">
简体中文 / [English](README_EN.md) / [日本語](README_JP.md) / (PR for your language)
[English](README_EN.md) / 简体中文 / [繁體中文](README_TW.md) / [日本語](README_JP.md) / (PR for your language)
[![Discord](https://img.shields.io/discord/1335141740050649118?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87)
[![QQ Group](https://img.shields.io/badge/%E7%A4%BE%E5%8C%BAQQ%E7%BE%A4-966235608-blue)](https://qm.qq.com/q/JLi38whHum)
@@ -25,13 +25,7 @@
</p>
## ✨ 特性
- 💬 大模型对话、Agent支持多种大模型适配群聊和私聊具有多轮对话、工具调用、多模态能力自带 RAG知识库实现并深度适配 [Dify](https://dify.ai)。
- 🤖 多平台支持:目前支持 QQ、QQ频道、企业微信、个人微信、飞书、Discord、Telegram 等平台。
- 🛠️ 高稳定性、功能完备:原生支持访问控制、限速、敏感词过滤等机制;配置简单,支持多种部署方式。支持多流水线配置,不同机器人用于不同应用场景。
- 🧩 插件扩展、活跃社区:支持事件驱动、组件扩展等插件机制;适配 Anthropic [MCP 协议](https://modelcontextprotocol.io/);目前已有数百个插件。
- 😻 Web 管理面板:支持通过浏览器管理 LangBot 实例,不再需要手动编写配置文件。
LangBot 是一个开源的大语言模型原生即时通信机器人开发平台,旨在提供开箱即用的 IM 机器人开发体验,具有 Agent、RAG、MCP 等多种 LLM 应用功能,适配全球主流即时通信平台,并提供丰富的 API 接口,支持自定义开发。
## 📦 开始使用
@@ -65,23 +59,25 @@ docker compose up -d
直接使用发行版运行,查看文档[手动部署](https://docs.langbot.app/zh/deploy/langbot/manual.html)。
## 📸 效果展示
## 😎 保持更新
<img alt="bots" src="https://docs.langbot.app/webui/bot-page.png" width="450px"/>
点击仓库右上角 Star 和 Watch 按钮,获取最新动态。
<img alt="bots" src="https://docs.langbot.app/webui/create-model.png" width="450px"/>
![star gif](https://docs.langbot.app/star.gif)
<img alt="bots" src="https://docs.langbot.app/webui/edit-pipeline.png" width="450px"/>
## ✨ 特性
<img alt="bots" src="https://docs.langbot.app/webui/plugin-market.png" width="450px"/>
- 💬 大模型对话、Agent支持多种大模型适配群聊和私聊具有多轮对话、工具调用、多模态能力自带 RAG知识库实现并深度适配 [Dify](https://dify.ai)。
- 🤖 多平台支持:目前支持 QQ、QQ频道、企业微信、个人微信、飞书、Discord、Telegram 等平台。
- 🛠️ 高稳定性、功能完备:原生支持访问控制、限速、敏感词过滤等机制;配置简单,支持多种部署方式。支持多流水线配置,不同机器人用于不同应用场景。
- 🧩 插件扩展、活跃社区:支持事件驱动、组件扩展等插件机制;适配 Anthropic [MCP 协议](https://modelcontextprotocol.io/);目前已有数百个插件。
- 😻 Web 管理面板:支持通过浏览器管理 LangBot 实例,不再需要手动编写配置文件。
<img alt="回复效果(带有联网插件)" src="https://docs.langbot.app/QChatGPT-0516.png" width="500px"/>
详细规格特性请访问[文档](https://docs.langbot.app/zh/insight/features.html)。
- WebUI Demo: https://demo.langbot.dev/
- 登录信息:邮箱:`demo@langbot.app` 密码:`langbot123456`
- 注意:仅展示webui效果,公开环境,请不要在其中填入您的任何敏感信息。
## 🔌 组件兼容性
或访问 demo 环境:https://demo.langbot.dev/
- 登录信息:邮箱:`demo@langbot.app` 密码:`langbot123456`
- 注意:仅展示 WebUI 效果,公开环境,请不要在其中填入您的任何敏感信息。
### 消息平台
@@ -89,19 +85,14 @@ docker compose up -d
| --- | --- | --- |
| QQ 个人号 | ✅ | QQ 个人号私聊、群聊 |
| QQ 官方机器人 | ✅ | QQ 官方机器人,支持频道、私聊、群聊 |
| 企业微信 | ✅ | |
| 微信 | ✅ | |
| 企微对外客服 | ✅ | |
| 个人微信 | ✅ | |
| 微信公众号 | ✅ | |
| 飞书 | ✅ | |
| 钉钉 | ✅ | |
| Discord | ✅ | |
| Telegram | ✅ | |
| Slack | ✅ | |
| LINE | 🚧 | |
| WhatsApp | 🚧 | |
🚧: 正在开发中
### 大模型能力
@@ -148,9 +139,3 @@ docker compose up -d
<a href="https://github.com/langbot-app/LangBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langbot-app/LangBot" />
</a>
## 😎 保持更新
点击仓库右上角 Star 和 Watch 按钮,获取最新动态。
![star gif](https://docs.langbot.app/star.gif)

View File

@@ -5,7 +5,7 @@
<div align="center">
[简体中文](README.md) / English / [日本語](README_JP.md) / (PR for your language)
English / [简体中文](README.md) / [繁體中文](README_TW.md) / [日本語](README_JP.md) / (PR for your language)
[![Discord](https://img.shields.io/discord/1335141740050649118?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/langbot-app/LangBot)
@@ -21,13 +21,7 @@
</p>
## ✨ Features
- 💬 Chat with LLM / Agent: Supports multiple LLMs, adapt to group chats and private chats; Supports multi-round conversations, tool calls, and multi-modal capabilities. Built-in RAG (knowledge base) implementation, and deeply integrates with [Dify](https://dify.ai).
- 🤖 Multi-platform Support: Currently supports QQ, QQ Channel, WeCom, personal WeChat, Lark, DingTalk, Discord, Telegram, etc.
- 🛠️ High Stability, Feature-rich: Native access control, rate limiting, sensitive word filtering, etc. mechanisms; Easy to use, supports multiple deployment methods. Supports multiple pipeline configurations, different bots can be used for different scenarios.
- 🧩 Plugin Extension, Active Community: Support event-driven, component extension, etc. plugin mechanisms; Integrate Anthropic [MCP protocol](https://modelcontextprotocol.io/); Currently has hundreds of plugins.
- 😻 [New] Web UI: Support management LangBot instance through the browser. No need to manually write configuration files.
LangBot is an open-source LLM native instant messaging robot development platform, aiming to provide out-of-the-box IM robot development experience, with Agent, RAG, MCP and other LLM application functions, adapting to global instant messaging platforms, and providing rich API interfaces, supporting custom development.
## 📦 Getting Started
@@ -61,23 +55,25 @@ Community contributed Zeabur template.
Directly use the released version to run, see the [Manual Deployment](https://docs.langbot.app/en/deploy/langbot/manual.html) documentation.
## 📸 Demo
## 😎 Stay Ahead
<img alt="bots" src="https://docs.langbot.app/webui/bot-page.png" width="400px"/>
Click the Star and Watch button in the upper right corner of the repository to get the latest updates.
<img alt="bots" src="https://docs.langbot.app/webui/create-model.png" width="400px"/>
![star gif](https://docs.langbot.app/star.gif)
<img alt="bots" src="https://docs.langbot.app/webui/edit-pipeline.png" width="400px"/>
## ✨ Features
<img alt="bots" src="https://docs.langbot.app/webui/plugin-market.png" width="400px"/>
- 💬 Chat with LLM / Agent: Supports multiple LLMs, adapt to group chats and private chats; Supports multi-round conversations, tool calls, and multi-modal capabilities. Built-in RAG (knowledge base) implementation, and deeply integrates with [Dify](https://dify.ai).
- 🤖 Multi-platform Support: Currently supports QQ, QQ Channel, WeCom, personal WeChat, Lark, DingTalk, Discord, Telegram, etc.
- 🛠️ High Stability, Feature-rich: Native access control, rate limiting, sensitive word filtering, etc. mechanisms; Easy to use, supports multiple deployment methods. Supports multiple pipeline configurations, different bots can be used for different scenarios.
- 🧩 Plugin Extension, Active Community: Support event-driven, component extension, etc. plugin mechanisms; Integrate Anthropic [MCP protocol](https://modelcontextprotocol.io/); Currently has hundreds of plugins.
- 😻 Web UI: Support management LangBot instance through the browser. No need to manually write configuration files.
<img alt="Reply Effect (with Internet Plugin)" src="https://docs.langbot.app/QChatGPT-0516.png" width="500px"/>
For more detailed specifications, please refer to the [documentation](https://docs.langbot.app/en/insight/features.html).
- WebUI Demo: https://demo.langbot.dev/
- Login information: Email: `demo@langbot.app` Password: `langbot123456`
- Note: Only the WebUI effect is shown, please do not fill in any sensitive information in the public environment.
## 🔌 Component Compatibility
Or visit the demo environment: https://demo.langbot.dev/
- Login information: Email: `demo@langbot.app` Password: `langbot123456`
- Note: For WebUI demo only, please do not fill in any sensitive information in the public environment.
### Message Platform
@@ -93,10 +89,6 @@ Directly use the released version to run, see the [Manual Deployment](https://do
| Discord | ✅ | |
| Telegram | ✅ | |
| Slack | ✅ | |
| LINE | 🚧 | |
| WhatsApp | 🚧 | |
🚧: In development
### LLMs
@@ -129,9 +121,3 @@ Thank you for the following [code contributors](https://github.com/langbot-app/L
<a href="https://github.com/langbot-app/LangBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langbot-app/LangBot" />
</a>
## 😎 Stay Ahead
Click the Star and Watch button in the upper right corner of the repository to get the latest updates.
![star gif](https://docs.langbot.app/star.gif)

View File

@@ -5,7 +5,7 @@
<div align="center">
[简体中文](README.md) / [English](README_EN.md) / 日本語 / (PR for your language)
[English](README_EN.md) / [简体中文](README.md) / [繁體中文](README_TW.md) / 日本語 / (PR for your language)
[![Discord](https://img.shields.io/discord/1335141740050649118?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/langbot-app/LangBot)
@@ -21,13 +21,7 @@
</p>
## ✨ 機能
- 💬 LLM / エージェントとのチャット: 複数のLLMをサポートし、グループチャットとプライベートチャットに対応。マルチラウンドの会話、ツールの呼び出し、マルチモーダル機能をサポート、RAG知識ベースを組み込み、[Dify](https://dify.ai) と深く統合。
- 🤖 多プラットフォーム対応: 現在、QQ、QQ チャンネル、WeChat、個人 WeChat、Lark、DingTalk、Discord、Telegram など、複数のプラットフォームをサポートしています。
- 🛠️ 高い安定性、豊富な機能: ネイティブのアクセス制御、レート制限、敏感な単語のフィルタリングなどのメカニズムをサポート。使いやすく、複数のデプロイ方法をサポート。複数のパイプライン設定をサポートし、異なるボットを異なる用途に使用できます。
- 🧩 プラグイン拡張、活発なコミュニティ: イベント駆動、コンポーネント拡張などのプラグインメカニズムをサポート。適配 Anthropic [MCP プロトコル](https://modelcontextprotocol.io/);豊富なエコシステム、現在数百のプラグインが存在。
- 😻 Web UI: ブラウザを通じてLangBotインスタンスを管理することをサポート。
LangBot は、エージェント、RAG、MCP などの LLM アプリケーション機能を備えた、オープンソースの LLM ネイティブのインスタントメッセージングロボット開発プラットフォームです。世界中のインスタントメッセージングプラットフォームに適応し、豊富な API インターフェースを提供し、カスタム開発をサポートします。
## 📦 始め方
@@ -43,7 +37,7 @@ http://localhost:5300 にアクセスして使用を開始します。
詳細なドキュメントは[Dockerデプロイ](https://docs.langbot.app/en/deploy/langbot/docker.html)を参照してください。
#### BTPanelでのワンクリックデプロイ
#### Panelでのワンクリックデプロイ
LangBotはBTPanelにリストされています。BTPanelをインストールしている場合は、[ドキュメント](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html)を使用して使用できます。
@@ -61,23 +55,25 @@ LangBotはBTPanelにリストされています。BTPanelをインストール
リリースバージョンを直接使用して実行します。[手動デプロイ](https://docs.langbot.app/en/deploy/langbot/manual.html)のドキュメントを参照してください。
## 📸 デモ
## 😎 最新情報を入手
<img alt="bots" src="https://docs.langbot.app/webui/bot-page.png" width="400px"/>
リポジトリの右上にある Star と Watch ボタンをクリックして、最新の更新を取得してください。
<img alt="bots" src="https://docs.langbot.app/webui/create-model.png" width="400px"/>
![star gif](https://docs.langbot.app/star.gif)
<img alt="bots" src="https://docs.langbot.app/webui/edit-pipeline.png" width="400px"/>
## ✨ 機能
<img alt="bots" src="https://docs.langbot.app/webui/plugin-market.png" width="400px"/>
- 💬 LLM / エージェントとのチャット: 複数のLLMをサポートし、グループチャットとプライベートチャットに対応。マルチラウンドの会話、ツールの呼び出し、マルチモーダル機能をサポート、RAG知識ベースを組み込み、[Dify](https://dify.ai) と深く統合。
- 🤖 多プラットフォーム対応: 現在、QQ、QQ チャンネル、WeChat、個人 WeChat、Lark、DingTalk、Discord、Telegram など、複数のプラットフォームをサポートしています。
- 🛠️ 高い安定性、豊富な機能: ネイティブのアクセス制御、レート制限、敏感な単語のフィルタリングなどのメカニズムをサポート。使いやすく、複数のデプロイ方法をサポート。複数のパイプライン設定をサポートし、異なるボットを異なる用途に使用できます。
- 🧩 プラグイン拡張、活発なコミュニティ: イベント駆動、コンポーネント拡張などのプラグインメカニズムをサポート。適配 Anthropic [MCP プロトコル](https://modelcontextprotocol.io/);豊富なエコシステム、現在数百のプラグインが存在。
- 😻 Web UI: ブラウザを通じてLangBotインスタンスを管理することをサポート。
<img alt="返信効果(インターネットプラグイン付き)" src="https://docs.langbot.app/QChatGPT-0516.png" width="500px"/>
詳細な仕様については、[ドキュメント](https://docs.langbot.app/en/insight/features.html)を参照してください。
- WebUIデモ: https://demo.langbot.dev/
- ログイン情報: メール: `demo@langbot.app` パスワード: `langbot123456`
- 注意: WebUIの効果のみを示しています。公開環境では機密情報を入力しないでください。
## 🔌 コンポーネントの互換性
または、デモ環境にアクセスしてください: https://demo.langbot.dev/
- ログイン情報: メール: `demo@langbot.app` パスワード: `langbot123456`
- 注意: WebUI のデモンストレーションのみの場合、公開環境では機密情報を入力しないでください。
### メッセージプラットフォーム
@@ -93,10 +89,6 @@ LangBotはBTPanelにリストされています。BTPanelをインストール
| Discord | ✅ | |
| Telegram | ✅ | |
| Slack | ✅ | |
| LINE | 🚧 | |
| WhatsApp | 🚧 | |
🚧: 開発中
### LLMs
@@ -129,9 +121,3 @@ LangBot への貢献に対して、以下の [コード貢献者](https://github
<a href="https://github.com/langbot-app/LangBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langbot-app/LangBot" />
</a>
## 😎 最新情報を入手
リポジトリの右上にある Star と Watch ボタンをクリックして、最新の更新を取得してください。
![star gif](https://docs.langbot.app/star.gif)

139
README_TW.md Normal file
View File

@@ -0,0 +1,139 @@
<p align="center">
<a href="https://langbot.app">
<img src="https://docs.langbot.app/social_zh.png" alt="LangBot"/>
</a>
<div align="center">
[English](README_EN.md) / [简体中文](README.md) / 繁體中文 / [日本語](README_JP.md) / (PR for your language)
[![Discord](https://img.shields.io/discord/1335141740050649118?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87)
[![QQ Group](https://img.shields.io/badge/%E7%A4%BE%E5%8C%BAQQ%E7%BE%A4-966235608-blue)](https://qm.qq.com/q/JLi38whHum)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/langbot-app/LangBot)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/langbot-app/LangBot)](https://github.com/langbot-app/LangBot/releases/latest)
<img src="https://img.shields.io/badge/python-3.10 ~ 3.13 -blue.svg" alt="python">
[![star](https://gitcode.com/RockChinQ/LangBot/star/badge.svg)](https://gitcode.com/RockChinQ/LangBot)
<a href="https://langbot.app">主頁</a>
<a href="https://docs.langbot.app/zh/insight/guide.html">部署文件</a>
<a href="https://docs.langbot.app/zh/plugin/plugin-intro.html">外掛介紹</a>
<a href="https://github.com/langbot-app/LangBot/issues/new?assignees=&labels=%E7%8B%AC%E7%AB%8B%E6%8F%92%E4%BB%B6&projects=&template=submit-plugin.yml&title=%5BPlugin%5D%3A+%E8%AF%B7%E6%B1%82%E7%99%BB%E8%AE%B0%E6%96%B0%E6%8F%92%E4%BB%B6">提交外掛</a>
</div>
</p>
LangBot 是一個開源的大語言模型原生即時通訊機器人開發平台,旨在提供開箱即用的 IM 機器人開發體驗,具有 Agent、RAG、MCP 等多種 LLM 應用功能,適配全球主流即時通訊平台,並提供豐富的 API 介面,支援自定義開發。
## 📦 開始使用
#### Docker Compose 部署
```bash
git clone https://github.com/langbot-app/LangBot
cd LangBot
docker compose up -d
```
訪問 http://localhost:5300 即可開始使用。
詳細文件[Docker 部署](https://docs.langbot.app/zh/deploy/langbot/docker.html)。
#### 寶塔面板部署
已上架寶塔面板,若您已安裝寶塔面板,可以根據[文件](https://docs.langbot.app/zh/deploy/langbot/one-click/bt.html)使用。
#### Zeabur 雲端部署
社群貢獻的 Zeabur 模板。
[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/zh-CN/templates/ZKTBDH)
#### Railway 雲端部署
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.app/template/yRrAyL?referralCode=vogKPF)
#### 手動部署
直接使用發行版運行,查看文件[手動部署](https://docs.langbot.app/zh/deploy/langbot/manual.html)。
## 😎 保持更新
點擊倉庫右上角 Star 和 Watch 按鈕,獲取最新動態。
![star gif](https://docs.langbot.app/star.gif)
## ✨ 特性
- 💬 大模型對話、Agent支援多種大模型適配群聊和私聊具有多輪對話、工具調用、多模態能力自帶 RAG知識庫實現並深度適配 [Dify](https://dify.ai)。
- 🤖 多平台支援:目前支援 QQ、QQ頻道、企業微信、個人微信、飛書、Discord、Telegram 等平台。
- 🛠️ 高穩定性、功能完備:原生支援訪問控制、限速、敏感詞過濾等機制;配置簡單,支援多種部署方式。支援多流水線配置,不同機器人用於不同應用場景。
- 🧩 外掛擴展、活躍社群:支援事件驅動、組件擴展等外掛機制;適配 Anthropic [MCP 協議](https://modelcontextprotocol.io/);目前已有數百個外掛。
- 😻 Web 管理面板:支援通過瀏覽器管理 LangBot 實例,不再需要手動編寫配置文件。
詳細規格特性請訪問[文件](https://docs.langbot.app/zh/insight/features.html)。
或訪問 demo 環境https://demo.langbot.dev/
- 登入資訊:郵箱:`demo@langbot.app` 密碼:`langbot123456`
- 注意:僅展示 WebUI 效果,公開環境,請不要在其中填入您的任何敏感資訊。
### 訊息平台
| 平台 | 狀態 | 備註 |
| --- | --- | --- |
| QQ 個人號 | ✅ | QQ 個人號私聊、群聊 |
| QQ 官方機器人 | ✅ | QQ 官方機器人,支援頻道、私聊、群聊 |
| 微信 | ✅ | |
| 企微對外客服 | ✅ | |
| 微信公眾號 | ✅ | |
| Lark | ✅ | |
| DingTalk | ✅ | |
| Discord | ✅ | |
| Telegram | ✅ | |
| Slack | ✅ | |
### 大模型能力
| 模型 | 狀態 | 備註 |
| --- | --- | --- |
| [OpenAI](https://platform.openai.com/) | ✅ | 可接入任何 OpenAI 介面格式模型 |
| [DeepSeek](https://www.deepseek.com/) | ✅ | |
| [Moonshot](https://www.moonshot.cn/) | ✅ | |
| [Anthropic](https://www.anthropic.com/) | ✅ | |
| [xAI](https://x.ai/) | ✅ | |
| [智譜AI](https://open.bigmodel.cn/) | ✅ | |
| [優雲智算](https://www.compshare.cn/?ytag=GPU_YY-gh_langbot) | ✅ | 大模型和 GPU 資源平台 |
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | 大模型和 GPU 資源平台 |
| [302.AI](https://share.302.ai/SuTG99) | ✅ | 大模型聚合平台 |
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
| [Dify](https://dify.ai) | ✅ | LLMOps 平台 |
| [Ollama](https://ollama.com/) | ✅ | 本地大模型運行平台 |
| [LMStudio](https://lmstudio.ai/) | ✅ | 本地大模型運行平台 |
| [GiteeAI](https://ai.gitee.com/) | ✅ | 大模型介面聚合平台 |
| [SiliconFlow](https://siliconflow.cn/) | ✅ | 大模型聚合平台 |
| [阿里雲百煉](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 |
| [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 |
| [ModelScope](https://modelscope.cn/docs/model-service/API-Inference/intro) | ✅ | 大模型聚合平台 |
| [MCP](https://modelcontextprotocol.io/) | ✅ | 支援通過 MCP 協議獲取工具 |
### TTS
| 平台/模型 | 備註 |
| --- | --- |
| [FishAudio](https://fish.audio/zh-CN/discovery/) | [外掛](https://github.com/the-lazy-me/NewChatVoice) |
| [海豚 AI](https://www.ttson.cn/?source=thelazy) | [外掛](https://github.com/the-lazy-me/NewChatVoice) |
| [AzureTTS](https://portal.azure.com/) | [外掛](https://github.com/Ingnaryk/LangBot_AzureTTS) |
### 文生圖
| 平台/模型 | 備註 |
| --- | --- |
| 阿里雲百煉 | [外掛](https://github.com/Thetail001/LangBot_BailianTextToImagePlugin)
## 😘 社群貢獻
感謝以下[程式碼貢獻者](https://github.com/langbot-app/LangBot/graphs/contributors)和社群裡其他成員對 LangBot 的貢獻:
<a href="https://github.com/langbot-app/LangBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langbot-app/LangBot" />
</a>

View File

@@ -11,7 +11,9 @@ class PipelinesRouterGroup(group.RouterGroup):
@self.route('', methods=['GET', 'POST'])
async def _() -> str:
if quart.request.method == 'GET':
return self.success(data={'pipelines': await self.ap.pipeline_service.get_pipelines()})
sort_by = quart.request.args.get('sort_by', 'created_at')
sort_order = quart.request.args.get('sort_order', 'DESC')
return self.success(data={'pipelines': await self.ap.pipeline_service.get_pipelines(sort_by, sort_order)})
elif quart.request.method == 'POST':
json_data = await quart.request.json

View File

@@ -38,9 +38,21 @@ class PipelineService:
self.ap.pipeline_config_meta_output.data,
]
async def get_pipelines(self) -> list[dict]:
result = await self.ap.persistence_mgr.execute_async(sqlalchemy.select(persistence_pipeline.LegacyPipeline))
async def get_pipelines(self, sort_by: str = 'created_at', sort_order: str = 'DESC') -> list[dict]:
query = sqlalchemy.select(persistence_pipeline.LegacyPipeline)
if sort_by == 'created_at':
if sort_order == 'DESC':
query = query.order_by(persistence_pipeline.LegacyPipeline.created_at.desc())
else:
query = query.order_by(persistence_pipeline.LegacyPipeline.created_at.asc())
elif sort_by == 'updated_at':
if sort_order == 'DESC':
query = query.order_by(persistence_pipeline.LegacyPipeline.updated_at.desc())
else:
query = query.order_by(persistence_pipeline.LegacyPipeline.updated_at.asc())
result = await self.ap.persistence_mgr.execute_async(query)
pipelines = result.all()
return [
self.ap.persistence_mgr.serialize_model(persistence_pipeline.LegacyPipeline, pipeline)

View File

@@ -40,8 +40,10 @@ class WeChatPadMessageConverter(adapter.MessageConverter):
content_list = []
for component in message_chain:
if isinstance(component, platform_message.At):
content_list.append({'type': 'at', 'target': component.target})
if isinstance(component, platform_message.AtAll):
content_list.append({"type": "at", "target": "all"})
elif isinstance(component, platform_message.At):
content_list.append({"type": "at", "target": component.target})
elif isinstance(component, platform_message.Plain):
content_list.append({'type': 'text', 'content': component.text})
elif isinstance(component, platform_message.Image):
@@ -577,19 +579,26 @@ class WeChatPadAdapter(adapter.MessagePlatformAdapter):
for msg in content_list:
# 文本消息处理@
if msg['type'] == 'text' and at_targets:
at_nick_name_list = []
for member in member_info:
if member['user_name'] in at_targets:
at_nick_name_list.append(f'@{member["nick_name"]}')
msg['content'] = f'{" ".join(at_nick_name_list)} {msg["content"]}'
if "all" in at_targets:
msg['content'] = f'@所有人 {msg["content"]}'
else:
at_nick_name_list = []
for member in member_info:
if member["user_name"] in at_targets:
at_nick_name_list.append(f'@{member["nick_name"]}')
msg['content'] = f'{" ".join(at_nick_name_list)} {msg["content"]}'
# 统一消息派发
handler_map = {
'text': lambda msg: self.bot.send_text_message(
to_wxid=target_id, message=msg['content'], ats=at_targets
to_wxid=target_id,
message=msg['content'],
ats= ["notify@all"] if "all" in at_targets else at_targets
),
'image': lambda msg: self.bot.send_image_message(
to_wxid=target_id, img_url=msg['image'], ats=at_targets
to_wxid=target_id,
img_url=msg["image"],
ats = ["notify@all"] if "all" in at_targets else at_targets
),
'WeChatEmoji': lambda msg: self.bot.send_emoji_message(
to_wxid=target_id, emoji_md5=msg['emoji_md5'], emoji_size=msg['emoji_size']

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./302aichatcmpl.py

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./giteeaichatcmpl.py

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./lmstudiochatcmpl.py

View File

@@ -14,7 +14,7 @@ spec:
zh_Hans: 基础 URL
type: string
required: true
default: "https://api.moonshot.com/v1"
default: "https://api.moonshot.ai/v1"
- name: timeout
label:
en_US: Timeout

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,17 @@
from __future__ import annotations
import typing
import openai
from . import chatcmpl
class NewAPIChatCompletions(chatcmpl.OpenAIChatCompletions):
"""New API ChatCompletion API 请求器"""
client: openai.AsyncClient
default_config: dict[str, typing.Any] = {
'base_url': 'http://localhost:3000/v1',
'timeout': 120,
}

View File

@@ -0,0 +1,31 @@
apiVersion: v1
kind: LLMAPIRequester
metadata:
name: new-api-chat-completions
label:
en_US: New API
zh_Hans: New API
icon: newapi.png
spec:
config:
- name: base_url
label:
en_US: Base URL
zh_Hans: 基础 URL
type: string
required: true
default: "http://localhost:3000/v1"
- name: timeout
label:
en_US: Timeout
zh_Hans: 超时时间
type: integer
required: true
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./newapichatcmpl.py
attr: NewAPIChatCompletions

View File

@@ -129,3 +129,15 @@ class OllamaChatCompletions(requester.ProviderAPIRequester):
)
except asyncio.TimeoutError:
raise errors.RequesterError('请求超时')
async def invoke_embedding(
self,
model: requester.RuntimeEmbeddingModel,
input_text: list[str],
extra_args: dict[str, typing.Any] = {},
) -> list[list[float]]:
return await self.client.embed(
model=model.model_entity.name,
input=input_text,
**extra_args,
)

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./ollamachat.py

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./openrouterchatcmpl.py

View File

@@ -31,6 +31,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./ppiochatcmpl.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,17 @@
from __future__ import annotations
import openai
import typing
from . import chatcmpl
class QHAIGCChatCompletions(chatcmpl.OpenAIChatCompletions):
"""启航 AI ChatCompletion API 请求器"""
client: openai.AsyncClient
default_config: dict[str, typing.Any] = {
'base_url': 'https://api.qhaigc.com/v1',
'timeout': 120,
}

View File

@@ -0,0 +1,38 @@
apiVersion: v1
kind: LLMAPIRequester
metadata:
name: qhaigc-chat-completions
label:
en_US: QH AI
zh_Hans: 启航 AI
icon: qhaigc.png
spec:
config:
- name: base_url
label:
en_US: Base URL
zh_Hans: 基础 URL
type: string
required: true
default: "https://api.qhaigc.net/v1"
- name: args
label:
en_US: Args
zh_Hans: 附加参数
type: object
required: true
default: {}
- name: timeout
label:
en_US: Timeout
zh_Hans: 超时时间
type: int
required: true
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./qhaigcchatcmpl.py
attr: QHAIGCChatCompletions

View File

@@ -24,6 +24,7 @@ spec:
default: 120
support_type:
- llm
- text-embedding
execution:
python:
path: ./siliconflowchatcmpl.py

View File

@@ -1,4 +1,4 @@
semantic_version = 'v4.1.0'
semantic_version = 'v4.1.2'
required_database_version = 4
"""Tag the version of the database schema, used to check if the database needs to be migrated"""

1
web/.env.example Normal file
View File

@@ -0,0 +1 @@
NEXT_PUBLIC_API_BASE_URL=http://localhost:5300

1
web/.gitignore vendored
View File

@@ -32,6 +32,7 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel

View File

@@ -1,36 +1,3 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Debug LangBot Frontend
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
Please refer to the [Development Guide](https://docs.langbot.app/en/develop/dev-config.html) for more information.

360
web/package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@hookform/resolvers": "^5.0.1",
"@radix-ui/react-checkbox": "^1.3.1",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-hover-card": "^1.1.13",
"@radix-ui/react-label": "^2.1.6",
"@radix-ui/react-popover": "^1.1.14",
@@ -1152,31 +1153,6 @@
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
@@ -1266,6 +1242,58 @@
}
}
},
"node_modules/@radix-ui/react-dropdown-menu": {
"version": "2.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz",
"integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-menu": "2.1.15",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
@@ -1282,13 +1310,13 @@
}
},
"node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz",
"integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==",
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
@@ -1306,6 +1334,29 @@
}
}
},
"node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-hover-card": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.13.tgz",
@@ -1378,6 +1429,232 @@
}
}
},
"node_modules/@radix-ui/react-menu": {
"version": "2.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz",
"integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.10",
"@radix-ui/react-focus-guards": "1.1.2",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.7",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.4",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-roving-focus": "1.1.10",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
"integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-escape-keydown": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-popper": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz",
"integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz",
@@ -1465,31 +1742,6 @@
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",

View File

@@ -4,8 +4,6 @@
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev:local": "NEXT_PUBLIC_API_BASE_URL=http://localhost:5300 next dev --turbopack",
"dev:local:win": "set NEXT_PUBLIC_API_BASE_URL=http://localhost:5300&&next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
@@ -17,6 +15,9 @@
"prettier --write"
]
},
"overrides": {
"@radix-ui/react-focus-scope": "1.1.7"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",

View File

@@ -144,6 +144,11 @@ export default function HomeSidebar({
'https://docs.langbot.app/zh/insight/guide.html',
'_blank',
);
} else if (language === 'zh-Hant') {
window.open(
'https://docs.langbot.app/zh/insight/guide.html',
'_blank',
);
} else {
window.open(
'https://docs.langbot.app/en/insight/guide.html',

View File

@@ -24,6 +24,7 @@ import { httpClient } from '@/app/infra/http/HttpClient';
// import { KnowledgeBase } from '@/app/infra/entities/api';
import KBForm from '@/app/home/knowledge/components/kb-form/KBForm';
import KBDoc from '@/app/home/knowledge/components/kb-docs/KBDoc';
import KBRetrieve from '@/app/home/knowledge/components/kb-retrieve/KBRetrieve';
interface KBDetailDialogProps {
open: boolean;
@@ -81,6 +82,19 @@ export default function KBDetailDialog({
</svg>
),
},
{
key: 'retrieve',
label: t('knowledge.retrieve'),
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z"></path>
</svg>
),
},
];
const confirmDelete = () => {
@@ -168,7 +182,9 @@ export default function KBDetailDialog({
<DialogTitle>
{activeMenu === 'metadata'
? t('knowledge.editKnowledgeBase')
: t('knowledge.editDocument')}
: activeMenu === 'documents'
? t('knowledge.editDocument')
: t('knowledge.retrieveTest')}
</DialogTitle>
</DialogHeader>
<div className="flex-1 overflow-y-auto px-6 pb-6">
@@ -180,6 +196,7 @@ export default function KBDetailDialog({
/>
)}
{activeMenu === 'documents' && <KBDoc kbId={kbId} />}
{activeMenu === 'retrieve' && <KBRetrieve kbId={kbId} />}
</div>
{activeMenu === 'metadata' && (
<DialogFooter className="px-6 py-4 border-t shrink-0">

View File

@@ -0,0 +1,99 @@
'use client';
import React, { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useTranslation } from 'react-i18next';
import { httpClient } from '@/app/infra/http/HttpClient';
import { RetrieveResult, KnowledgeBaseFile } from '@/app/infra/entities/api';
import { toast } from 'sonner';
interface KBRetrieveProps {
kbId: string;
}
export default function KBRetrieve({ kbId }: KBRetrieveProps) {
const { t } = useTranslation();
const [query, setQuery] = useState('');
const [results, setResults] = useState<RetrieveResult[]>([]);
const [files, setFiles] = useState<KnowledgeBaseFile[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const loadFiles = async () => {
try {
const response = await httpClient.getKnowledgeBaseFiles(kbId);
setFiles(response.files);
} catch (error) {
console.error('Failed to load files:', error);
}
};
loadFiles();
}, [kbId]);
const handleRetrieve = async () => {
if (!query.trim()) return;
setLoading(true);
try {
setResults([]);
const response = await httpClient.retrieveKnowledgeBase(kbId, query);
setResults(response.results);
} catch (error) {
console.error('Retrieve failed:', error);
toast.error(t('knowledge.retrieveError'));
} finally {
setLoading(false);
}
};
const getFileName = (fileId: string) => {
const file = files.find((f) => f.uuid === fileId);
return file?.file_name || fileId;
};
return (
<div className="space-y-4">
<div className="flex gap-2">
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={t('knowledge.queryPlaceholder')}
onKeyPress={(e) => e.key === 'Enter' && handleRetrieve()}
/>
<Button onClick={handleRetrieve} disabled={loading || !query.trim()}>
{t('knowledge.query')}
</Button>
</div>
<div className="space-y-3">
{results.length === 0 && !loading && (
<p className="text-muted-foreground">{t('knowledge.noResults')}</p>
)}
{loading ? (
<p className="text-muted-foreground">{t('common.loading')}</p>
) : (
results.map((result) => (
<Card key={result.id} className="w-full">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium flex justify-between items-center">
<span>{getFileName(result.metadata.file_id)}</span>
<span className="text-xs text-muted-foreground">
{t('knowledge.distance')}: {result.distance.toFixed(4)}
</span>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm whitespace-pre-wrap">
{result.metadata.text}
</p>
</CardContent>
</Card>
))
)}
</div>
</div>
);
}

View File

@@ -118,6 +118,7 @@ export default function EmbeddingForm({
string[]
>([]);
const [modelTesting, setModelTesting] = useState(false);
const [currentModelProvider, setCurrentModelProvider] = useState('');
useEffect(() => {
initEmbeddingModelFormComponent().then(() => {
@@ -125,7 +126,7 @@ export default function EmbeddingForm({
getEmbeddingConfig(initEmbeddingId).then((val) => {
form.setValue('name', val.name);
form.setValue('model_provider', val.model_provider);
// setCurrentModelProvider(val.model_provider);
setCurrentModelProvider(val.model_provider);
form.setValue('url', val.url);
form.setValue('api_key', val.api_key);
if (val.extra_args) {
@@ -393,7 +394,7 @@ export default function EmbeddingForm({
<Select
onValueChange={(value) => {
field.onChange(value);
// setCurrentModelProvider(value);
setCurrentModelProvider(value);
const index = requesterNameList.findIndex(
(item) => item.value === value,
);
@@ -441,22 +442,24 @@ export default function EmbeddingForm({
)}
/>
<FormField
control={form.control}
name="api_key"
render={({ field }) => (
<FormItem>
<FormLabel>
{t('models.apiKey')}
<span className="text-red-500">*</span>
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{!['ollama-chat'].includes(currentModelProvider) && (
<FormField
control={form.control}
name="api_key"
render={({ field }) => (
<FormItem>
<FormLabel>
{t('models.apiKey')}
<span className="text-red-500">*</span>
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormItem>
<FormLabel>{t('models.extraParameters')}</FormLabel>

View File

@@ -9,6 +9,13 @@ import styles from './pipelineConfig.module.css';
import { toast } from 'sonner';
import { useTranslation } from 'react-i18next';
import PipelineDialog from './PipelineDetailDialog';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
export default function PluginConfigPage() {
const { t } = useTranslation();
@@ -26,14 +33,19 @@ export default function PluginConfigPage() {
});
const [selectedPipelineIsDefault, setSelectedPipelineIsDefault] =
useState(false);
const [sortByValue, setSortByValue] = useState<string>('created_at');
const [sortOrderValue, setSortOrderValue] = useState<string>('DESC');
useEffect(() => {
getPipelines();
}, []);
function getPipelines() {
function getPipelines(
sortBy: string = sortByValue,
sortOrder: string = sortOrderValue,
) {
httpClient
.getPipelines()
.getPipelines(sortBy, sortOrder)
.then((value) => {
const currentTime = new Date();
const pipelineList = value.pipelines.map((pipeline) => {
@@ -106,6 +118,13 @@ export default function PluginConfigPage() {
setDialogOpen(true);
};
function handleSortChange(value: string) {
const [newSortBy, newSortOrder] = value.split(',').map((s) => s.trim());
setSortByValue(newSortBy);
setSortOrderValue(newSortOrder);
getPipelines(newSortBy, newSortOrder);
}
return (
<div className={styles.configPageContainer}>
<PipelineDialog
@@ -134,6 +153,27 @@ export default function PluginConfigPage() {
}}
/>
<div className="flex flex-row justify-between items-center mb-4 px-[0.8rem]">
<Select
value={`${sortByValue},${sortOrderValue}`}
onValueChange={handleSortChange}
>
<SelectTrigger className="w-[180px] cursor-pointer bg-white dark:bg-gray-800">
<SelectValue placeholder={t('pipelines.sortBy')} />
</SelectTrigger>
<SelectContent>
<SelectItem value="created_at,DESC">
{t('pipelines.newestCreated')}
</SelectItem>
<SelectItem value="updated_at,DESC">
{t('pipelines.recentlyEdited')}
</SelectItem>
<SelectItem value="updated_at,ASC">
{t('pipelines.earliestEdited')}
</SelectItem>
</SelectContent>
</Select>
</div>
<div className={styles.pipelineListContainer}>
<CreateCardComponent
width={'100%'}

View File

@@ -109,11 +109,32 @@ export default function PluginCardComponent({
/>
</div>
<div className="flex items-center justify-center">
{/* <Button variant="ghost">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.17071 18C6.58254 16.8348 7.69378 16 9 16C10.3062 16 11.4175 16.8348 11.8293 18H22V20H11.8293C11.4175 21.1652 10.3062 22 9 22C7.69378 22 6.58254 21.1652 6.17071 20H2V18H6.17071ZM12.1707 11C12.5825 9.83481 13.6938 9 15 9C16.3062 9 17.4175 9.83481 17.8293 11H22V13H17.8293C17.4175 14.1652 16.3062 15 15 15C13.6938 15 12.5825 14.1652 12.1707 13H2V11H12.1707ZM6.17071 4C6.58254 2.83481 7.69378 2 9 2C10.3062 2 11.4175 2.83481 11.8293 4H22V6H11.8293C11.4175 7.16519 10.3062 8 9 8C7.69378 8 6.58254 7.16519 6.17071 6H2V4H6.17071ZM9 6C9.55228 6 10 5.55228 10 5C10 4.44772 9.55228 4 9 4C8.44772 4 8 4.44772 8 5C8 5.55228 8.44772 6 9 6ZM15 13C15.5523 13 16 12.5523 16 12C16 11.4477 15.5523 11 15 11C14.4477 11 14 11.4477 14 12C14 12.5523 14.4477 13 15 13ZM9 20C9.55228 20 10 19.5523 10 19C10 18.4477 9.55228 18 9 18C8.44772 18 8 18.4477 8 19C8 19.5523 8.44772 20 9 20Z"></path></svg>
</Button> */}
</div>
{cardVO.repository &&
cardVO.repository.trim() &&
cardVO.repository.startsWith('http') && (
<div className="flex items-center justify-center gap-[0.4rem]">
<svg
className={`w-[1.4rem] h-[1.4rem] cursor-pointer ${
cardVO.repository ? 'text-black' : 'text-gray-400'
}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
onClick={(e) => {
e.stopPropagation(); // 阻止事件冒泡
if (
cardVO.repository &&
cardVO.repository.trim() &&
cardVO.repository.startsWith('http')
) {
window.open(cardVO.repository, '_blank');
}
}}
>
<path d="M12.001 2C6.47598 2 2.00098 6.475 2.00098 12C2.00098 16.425 4.86348 20.1625 8.83848 21.4875C9.33848 21.575 9.52598 21.275 9.52598 21.0125C9.52598 20.775 9.51348 19.9875 9.51348 19.15C7.00098 19.6125 6.35098 18.5375 6.15098 17.975C6.03848 17.6875 5.55098 16.8 5.12598 16.5625C4.77598 16.375 4.27598 15.9125 5.11348 15.9C5.90098 15.8875 6.46348 16.625 6.65098 16.925C7.55098 18.4375 8.98848 18.0125 9.56348 17.75C9.65098 17.1 9.91348 16.6625 10.201 16.4125C7.97598 16.1625 5.65098 15.3 5.65098 11.475C5.65098 10.3875 6.03848 9.4875 6.67598 8.7875C6.57598 8.5375 6.22598 7.5125 6.77598 6.1375C6.77598 6.1375 7.61348 5.875 9.52598 7.1625C10.326 6.9375 11.176 6.825 12.026 6.825C12.876 6.825 13.726 6.9375 14.526 7.1625C16.4385 5.8625 17.276 6.1375 17.276 6.1375C17.826 7.5125 17.476 8.5375 17.376 8.7875C18.0135 9.4875 18.401 10.375 18.401 11.475C18.401 15.3125 16.0635 16.1625 13.8385 16.4125C14.201 16.725 14.5135 17.325 14.5135 18.2625C14.5135 19.6 14.501 20.675 14.501 21.0125C14.501 21.275 14.6885 21.5875 15.1885 21.4875C19.259 20.1133 21.9999 16.2963 22.001 12C22.001 6.475 17.526 2 12.001 2Z"></path>
</svg>
</div>
)}
</div>
</div>
</div>

View File

@@ -296,3 +296,18 @@ export interface ApiRespWebChatMessage {
export interface ApiRespWebChatMessages {
messages: Message[];
}
export interface RetrieveResult {
id: string;
metadata: {
file_id: string;
text: string;
uuid: string;
[key: string]: unknown;
};
distance: number;
}
export interface ApiRespKnowledgeBaseRetrieve {
results: RetrieveResult[];
}

View File

@@ -38,6 +38,7 @@ import {
ApiRespKnowledgeBase,
KnowledgeBase,
ApiRespKnowledgeBaseFiles,
ApiRespKnowledgeBaseRetrieve,
} from '@/app/infra/entities/api';
import { GetBotLogsRequest } from '@/app/infra/http/requestParam/bots/GetBotLogsRequest';
import { GetBotLogsResponse } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
@@ -323,8 +324,15 @@ class HttpClient {
return this.get('/api/v1/pipelines/_/metadata');
}
public getPipelines(): Promise<ApiRespPipelines> {
return this.get('/api/v1/pipelines');
public getPipelines(
sortBy?: string,
sortOrder?: string,
): Promise<ApiRespPipelines> {
const params = new URLSearchParams();
if (sortBy) params.append('sort_by', sortBy);
if (sortOrder) params.append('sort_order', sortOrder);
const queryString = params.toString();
return this.get(`/api/v1/pipelines${queryString ? `?${queryString}` : ''}`);
}
public getPipeline(uuid: string): Promise<GetPipelineResponseData> {
@@ -492,6 +500,13 @@ class HttpClient {
return this.delete(`/api/v1/knowledge/bases/${uuid}`);
}
public retrieveKnowledgeBase(
uuid: string,
query: string,
): Promise<ApiRespKnowledgeBaseRetrieve> {
return this.post(`/api/v1/knowledge/bases/${uuid}/retrieve`, { query });
}
// ============ Plugins API ============
public getPlugins(): Promise<ApiRespPlugins> {
return this.get('/api/v1/plugins');

View File

@@ -65,6 +65,9 @@ export default function Login() {
if (i18n.language === 'zh-CN' || i18n.language === 'zh-Hans') {
setCurrentLanguage('zh-Hans');
localStorage.setItem('langbot_language', 'zh-Hans');
} else if (i18n.language === 'zh-TW' || i18n.language === 'zh-Hant') {
setCurrentLanguage('zh-Hant');
localStorage.setItem('langbot_language', 'zh-Hant');
} else if (i18n.language === 'ja' || i18n.language === 'ja-JP') {
setCurrentLanguage('ja-JP');
localStorage.setItem('langbot_language', 'ja-JP');
@@ -84,6 +87,8 @@ export default function Login() {
let lang = 'zh-Hans';
if (language === 'zh-CN') {
lang = 'zh-Hans';
} else if (language === 'zh-TW') {
lang = 'zh-Hant';
} else if (language === 'ja' || language === 'ja-JP') {
lang = 'ja-JP';
} else {
@@ -164,6 +169,7 @@ export default function Login() {
</SelectTrigger>
<SelectContent>
<SelectItem value="zh-Hans"></SelectItem>
<SelectItem value="zh-Hant"></SelectItem>
<SelectItem value="en-US">English</SelectItem>
<SelectItem value="ja-JP"></SelectItem>
</SelectContent>

View File

@@ -63,6 +63,9 @@ export default function Register() {
if (i18n.language === 'zh-CN' || i18n.language === 'zh-Hans') {
setCurrentLanguage('zh-Hans');
localStorage.setItem('langbot_language', 'zh-Hans');
} else if (i18n.language === 'zh-TW' || i18n.language === 'zh-Hant') {
setCurrentLanguage('zh-Hant');
localStorage.setItem('langbot_language', 'zh-Hant');
} else if (i18n.language === 'ja' || i18n.language === 'ja-JP') {
setCurrentLanguage('ja-JP');
localStorage.setItem('langbot_language', 'ja-JP');
@@ -82,6 +85,8 @@ export default function Register() {
let lang = 'zh-Hans';
if (language === 'zh-CN') {
lang = 'zh-Hans';
} else if (language === 'zh-TW') {
lang = 'zh-Hant';
} else if (language === 'ja' || language === 'ja-JP') {
lang = 'ja-JP';
} else {
@@ -148,6 +153,7 @@ export default function Register() {
</SelectTrigger>
<SelectContent>
<SelectItem value="zh-Hans"></SelectItem>
<SelectItem value="zh-Hant"></SelectItem>
<SelectItem value="en-US">English</SelectItem>
<SelectItem value="ja-JP"></SelectItem>
</SelectContent>

View File

@@ -6,6 +6,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import enUS from './locales/en-US';
import zhHans from './locales/zh-Hans';
import zhHant from './locales/zh-Hant';
import jaJP from './locales/ja-JP';
i18n
@@ -19,6 +20,9 @@ i18n
'zh-Hans': {
translation: zhHans,
},
'zh-Hant': {
translation: zhHant,
},
'ja-JP': {
translation: jaJP,
},

View File

@@ -40,6 +40,7 @@ const enUS = {
copySuccess: 'Copy Successfully',
test: 'Test',
forgotPassword: 'Forgot Password?',
loading: 'Loading...',
},
notFound: {
title: 'Page not found',
@@ -194,6 +195,10 @@ const enUS = {
today: 'Today',
updateTime: 'Updated ',
defaultBadge: 'Default',
sortBy: 'Sort by',
newestCreated: 'Newest Created',
recentlyEdited: 'Recently Edited',
earliestEdited: 'Earliest Edited',
basicInfo: 'Basic',
aiCapabilities: 'AI',
triggerConditions: 'Trigger',
@@ -278,6 +283,15 @@ const enUS = {
},
deleteKnowledgeBaseConfirmation:
'Are you sure you want to delete this knowledge base? All documents in this knowledge base will be deleted.',
retrieve: 'Retrieve Test',
retrieveTest: 'Retrieve Test',
query: 'Query',
queryPlaceholder: 'Enter query text...',
distance: 'Distance',
content: 'Content',
fileName: 'File Name',
noResults: 'No results',
retrieveError: 'Retrieve failed',
},
register: {
title: 'Initialize LangBot 👋',

View File

@@ -41,6 +41,7 @@ const jaJP = {
copySuccess: 'コピーに成功しました',
test: 'テスト',
forgotPassword: 'パスワードを忘れた?',
loading: '読み込み中...',
},
notFound: {
title: 'ページが見つかりません',
@@ -195,6 +196,10 @@ const jaJP = {
today: '今日',
updateTime: '更新日時',
defaultBadge: 'デフォルト',
sortBy: '並び順',
newestCreated: '最新作成',
recentlyEdited: '最近編集',
earliestEdited: '最古編集',
basicInfo: '基本情報',
aiCapabilities: 'AI機能',
triggerConditions: 'トリガー条件',
@@ -281,6 +286,15 @@ const jaJP = {
},
deleteKnowledgeBaseConfirmation:
'本当にこの知識ベースを削除しますか?この知識ベースに紐付けられたドキュメントは削除されます。',
retrieve: '検索テスト',
retrieveTest: '検索テスト',
query: '検索',
queryPlaceholder: '検索内容を入力...',
distance: '距離',
content: '内容',
fileName: 'ファイル名',
noResults: '検索結果がありません',
retrieveError: '検索に失敗しました',
},
register: {
title: 'LangBot を初期化 👋',

View File

@@ -40,6 +40,7 @@ const zhHans = {
copySuccess: '复制成功',
test: '测试',
forgotPassword: '忘记密码?',
loading: '加载中...',
},
notFound: {
title: '页面不存在',
@@ -189,6 +190,10 @@ const zhHans = {
today: '今天',
updateTime: '更新于',
defaultBadge: '默认',
sortBy: '排序方式',
newestCreated: '最新创建',
recentlyEdited: '最近编辑',
earliestEdited: '最早编辑',
basicInfo: '基础信息',
aiCapabilities: 'AI 能力',
triggerConditions: '触发条件',
@@ -270,6 +275,15 @@ const zhHans = {
},
deleteKnowledgeBaseConfirmation:
'你确定要删除这个知识库吗?此知识库下的所有文档将被删除。',
retrieve: '检索测试',
retrieveTest: '检索测试',
query: '查询',
queryPlaceholder: '输入查询内容...',
distance: '距离',
content: '内容',
fileName: '文件名',
noResults: '暂无结果',
retrieveError: '检索失败',
},
register: {
title: '初始化 LangBot 👋',

View File

@@ -0,0 +1,330 @@
const zhHant = {
common: {
login: '登入',
logout: '登出',
email: '電子郵件',
password: '密碼',
welcome: '歡迎回到 LangBot 👋',
continueToLogin: '登入以繼續',
loginSuccess: '登入成功',
loginFailed: '登入失敗,請檢查電子郵件和密碼是否正確',
enterEmail: '輸入電子郵件地址',
enterPassword: '輸入密碼',
invalidEmail: '請輸入有效的電子郵件地址',
emptyPassword: '請輸入密碼',
language: '語言',
helpDocs: '輔助說明',
create: '建立',
edit: '編輯',
delete: '刪除',
add: '新增',
select: '請選擇',
cancel: '取消',
submit: '提交',
error: '錯誤',
success: '成功',
save: '儲存',
saving: '儲存中...',
confirm: '確認',
confirmDelete: '確認刪除',
deleteConfirmation: '您確定要刪除這個嗎?',
selectOption: '選擇一個選項',
required: '必填',
enable: '是否啟用',
name: '名稱',
description: '描述',
close: '關閉',
deleteSuccess: '刪除成功',
deleteError: '刪除失敗:',
addRound: '新增回合',
copySuccess: '複製成功',
test: '測試',
forgotPassword: '忘記密碼?',
loading: '載入中...',
},
notFound: {
title: '頁面不存在',
description:
'您要查詢的頁面似乎不存在。請檢查您輸入的 URL 是否正確,或返回首頁。',
back: '上一級',
home: '返回主頁',
help: '查看說明文件',
},
models: {
title: '模型設定',
description: '設定和管理可在流程線中使用的模型',
createModel: '建立模型',
editModel: '編輯模型',
getModelListError: '取得模型清單失敗:',
modelName: '模型名稱',
modelProvider: '模型供應商',
modelBaseURL: '基礎 URL',
modelAbilities: '模型能力',
saveSuccess: '儲存成功',
saveError: '儲存失敗:',
createSuccess: '建立成功',
createError: '建立失敗:',
deleteSuccess: '刪除成功',
deleteError: '刪除失敗:',
deleteConfirmation: '您確定要刪除這個模型嗎?',
modelNameRequired: '模型名稱不能為空',
modelProviderRequired: '模型供應商不能為空',
requestURLRequired: '請求URL不能為空',
apiKeyRequired: 'API Key不能為空',
keyNameRequired: '鍵名不能為空',
mustBeValidNumber: '必須是有效的數字',
mustBeTrueOrFalse: '必須是 true 或 false',
requestURL: '請求URL',
apiKey: 'API Key',
abilities: '能力',
selectModelAbilities: '選擇模型能力',
visionAbility: '視覺能力',
functionCallAbility: '函數呼叫',
extraParameters: '額外參數',
addParameter: '新增參數',
keyName: '鍵名',
type: '類型',
value: '值',
string: '字串',
number: '數字',
boolean: '布林值',
selectModelProvider: '選擇模型供應商',
modelProviderDescription: '請填寫供應商向您提供的模型名稱',
selectModel: '請選擇模型',
testSuccess: '測試成功',
testError: '測試失敗,請檢查模型設定',
llmModels: '對話模型',
},
bots: {
title: '機器人',
description: '建立和管理機器人,這是 LangBot 與各個平台連接的入口',
createBot: '建立機器人',
editBot: '編輯機器人',
getBotListError: '取得機器人清單失敗:',
botName: '機器人名稱',
botDescription: '機器人描述',
botNameRequired: '機器人名稱不能為空',
botDescriptionRequired: '機器人描述不能為空',
adapterRequired: '適配器不能為空',
defaultDescription: '一個機器人',
getBotConfigError: '取得機器人設定失敗:',
saveSuccess: '儲存成功',
saveError: '儲存失敗:',
createSuccess: '建立成功 請啟用或修改綁定流程線',
createError: '建立失敗:',
deleteSuccess: '刪除成功',
deleteError: '刪除失敗:',
deleteConfirmation: '您確定要刪除這個機器人嗎?',
platformAdapter: '平台/適配器選擇',
selectAdapter: '選擇適配器',
adapterConfig: '適配器設定',
bindPipeline: '綁定流程線',
selectPipeline: '選擇流程線',
botLogTitle: '機器人日誌',
enableAutoRefresh: '開啟自動重新整理',
session: '對話',
yesterday: '昨天',
earlier: '更久之前',
dateFormat: '{{month}}月{{day}}日',
setBotEnableError: '設定機器人啟用狀態失敗',
log: '日誌',
configuration: '設定',
logs: '日誌',
},
plugins: {
title: '外掛管理',
description: '安裝和設定用於擴展 LangBot 功能的外掛',
createPlugin: '建立外掛',
editPlugin: '編輯外掛',
installed: '已安裝',
marketplace: 'Marketplace',
arrange: '編排',
install: '安裝',
installFromGithub: '從 GitHub 安裝外掛',
onlySupportGithub: '目前僅支援從 GitHub 安裝',
enterGithubLink: '請輸入外掛的Github連結',
installing: '正在安裝外掛...',
installSuccess: '外掛安裝成功',
installFailed: '外掛安裝失敗:',
searchPlugin: '搜尋外掛',
sortBy: '排序方式',
mostStars: '最多星標',
recentlyAdded: '最近新增',
recentlyUpdated: '最近更新',
noMatchingPlugins: '沒有找到符合的外掛',
loading: '載入中...',
getPluginListError: '取得外掛清單失敗:',
pluginConfig: '外掛設定',
noPluginInstalled: '暫未安裝任何外掛',
pluginSort: '外掛排序',
pluginSortDescription:
'外掛順序會影響同一事件內的處理順序,請拖曳外掛卡片排序',
pluginSortSuccess: '外掛排序成功',
pluginSortError: '外掛排序失敗:',
pluginNoConfig: '外掛沒有設定項目。',
deleting: '刪除中...',
deletePlugin: '刪除外掛',
cancel: '取消',
saveConfig: '儲存設定',
saving: '儲存中...',
confirmDeletePlugin: '您確定要刪除外掛({{author}}/{{name}})嗎?',
confirmDelete: '確認刪除',
deleteError: '刪除失敗:',
close: '關閉',
deleteConfirm: '刪除確認',
modifyFailed: '修改失敗:',
eventCount: '事件:{{count}}',
toolCount: '工具:{{count}}',
starCount: '星標:{{count}}',
},
pipelines: {
title: '流程線',
description: '流程線定義了對訊息事件的處理流程,用於綁定到機器人',
createPipeline: '建立流程線',
editPipeline: '編輯流程線',
chat: '對話',
configuration: '設定',
debugChat: '對話除錯',
getPipelineListError: '取得流程線清單失敗:',
daysAgo: '天前',
today: '今天',
updateTime: '更新於',
defaultBadge: '預設',
sortBy: '排序方式',
newestCreated: '最新建立',
recentlyEdited: '最近編輯',
earliestEdited: '最早編輯',
basicInfo: '基本資訊',
aiCapabilities: 'AI 能力',
triggerConditions: '觸發條件',
safetyControls: '安全控制',
outputProcessing: '輸出處理',
nameRequired: '名稱不能為空',
descriptionRequired: '描述不能為空',
createSuccess: '建立成功 請編輯流程線詳細參數',
createError: '建立失敗:',
saveSuccess: '儲存成功',
saveError: '儲存失敗:',
deleteConfirmation:
'您確定要刪除這個流程線嗎?已綁定此流程線的機器人將無法使用。',
defaultPipelineCannotDelete: '預設流程線不可刪除',
deleteSuccess: '刪除成功',
deleteError: '刪除失敗:',
debugDialog: {
title: '流程線對話',
selectPipeline: '選擇流程線',
sessionType: '對話類型',
privateChat: '私聊',
groupChat: '群聊',
send: '傳送',
reset: '重設對話',
inputPlaceholder: '傳送 {{type}} 訊息...',
noMessages: '暫無訊息',
userMessage: '使用者',
botMessage: '機器人',
sendFailed: '傳送失敗',
resetSuccess: '對話已重設',
resetFailed: '重設失敗',
loadMessagesFailed: '載入訊息失敗',
loadPipelinesFailed: '載入流程線失敗',
atTips: '提及機器人',
},
},
knowledge: {
title: '知識庫',
createKnowledgeBase: '建立知識庫',
editKnowledgeBase: '編輯知識庫',
selectKnowledgeBase: '選擇知識庫',
empty: '無',
editDocument: '文件',
description: '設定可用於提升模型回覆品質的知識庫',
metadata: '中繼資料',
documents: '文件',
kbNameRequired: '知識庫名稱不能為空',
kbDescriptionRequired: '知識庫描述不能為空',
embeddingModelUUIDRequired: '嵌入模型不能為空',
daysAgo: '天前',
today: '今天',
kbName: '知識庫名稱',
kbDescription: '知識庫描述',
defaultDescription: '一個知識庫',
embeddingModelUUID: '嵌入模型',
selectEmbeddingModel: '選擇嵌入模型',
embeddingModelDescription: '用於向量化文字,可在模型設定頁面設定',
updateTime: '更新於',
cannotChangeEmbeddingModel: '知識庫建立後不可修改嵌入模型',
updateKnowledgeBaseSuccess: '知識庫更新成功',
updateKnowledgeBaseFailed: '知識庫更新失敗',
documentsTab: {
name: '名稱',
status: '狀態',
noResults: '暫無文件',
dragAndDrop: '拖曳檔案到此處或點擊上傳',
uploading: '上傳中...',
supportedFormats: '支援 PDF、Word、TXT、Markdown 等文件格式',
uploadSuccess: '檔案上傳成功!',
uploadError: '檔案上傳失敗,請重試',
uploadingFile: '上傳檔案中...',
actions: '操作',
delete: '刪除檔案',
fileDeleteSuccess: '檔案刪除成功',
fileDeleteFailed: '檔案刪除失敗',
processing: '處理中',
completed: '完成',
failed: '失敗',
},
deleteKnowledgeBaseConfirmation:
'您確定要刪除這個知識庫嗎?此知識庫下的所有文件將被刪除。',
retrieve: '檢索測試',
retrieveTest: '檢索測試',
query: '查詢',
queryPlaceholder: '輸入查詢內容...',
distance: '距離',
content: '內容',
fileName: '檔案名稱',
noResults: '暫無結果',
retrieveError: '檢索失敗',
},
register: {
title: '初始化 LangBot 👋',
description: '這是您首次啟動 LangBot',
adminAccountNote: '您填寫的電子郵件和密碼將作為初始管理員帳號',
register: '註冊',
initSuccess: '初始化成功 請登入',
initFailed: '初始化失敗:',
},
resetPassword: {
title: '重設密碼 🔐',
description: '輸入恢復金鑰和新的密碼來重設您的帳戶密碼',
recoveryKey: '恢復金鑰',
recoveryKeyDescription:
'儲存在設定檔案`data/config.yaml`的`system.recovery_key`中',
newPassword: '新密碼',
enterRecoveryKey: '輸入恢復金鑰',
enterNewPassword: '輸入新密碼',
recoveryKeyRequired: '恢復金鑰不能為空',
newPasswordRequired: '新密碼不能為空',
resetPassword: '重設密碼',
resetting: '重設中...',
resetSuccess: '密碼重設成功,請登入',
resetFailed: '密碼重設失敗,請檢查電子郵件和恢復金鑰是否正確',
backToLogin: '返回登入',
},
embedding: {
description: '管理嵌入模型,用於向量化文字',
createModel: '建立嵌入模型',
editModel: '編輯嵌入模型',
getModelListError: '取得嵌入模型清單失敗:',
embeddingModels: '嵌入模型',
extraParametersDescription:
'將在請求時附加到請求體中,如 encoding_format, dimensions 等',
},
llm: {
llmModels: '對話模型',
description: '管理 LLM 模型,用於對話訊息產生',
extraParametersDescription:
'將在請求時附加到請求體中,如 max_tokens, temperature, top_p 等',
},
};
export default zhHant;