mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
Compare commits
12 Commits
pr/6mvp6/2
...
v4.9.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8aedd02b3 | ||
|
|
ea638cab80 | ||
|
|
7129dd536e | ||
|
|
1b1cc7769b | ||
|
|
44b8354dfd | ||
|
|
55ec9d11ae | ||
|
|
5b3d3801b5 | ||
|
|
9f1ea75d09 | ||
|
|
6e37aae636 | ||
|
|
921d12f596 | ||
|
|
6bf6deaefd | ||
|
|
1201949f2c |
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: 漏洞反馈
|
||||
description: 【供中文用户】报错或漏洞请使用这个模板创建,不使用此模板创建的异常、漏洞相关issue将被直接关闭。由于自己操作不当/不甚了解所用技术栈引起的网络连接问题恕无法解决,请勿提 issue。容器间网络连接问题,参考文档 https://docs.langbot.app/zh/workshop/network-details.html
|
||||
description: 【供中文用户】报错或漏洞请使用这个模板创建,不使用此模板创建的异常、漏洞相关issue将被直接关闭。由于自己操作不当/不甚了解所用技术栈引起的网络连接问题恕无法解决,请勿提 issue。容器间网络连接问题,参考文档 https://link.langbot.app/zh/docs/network
|
||||
title: "[Bug]: "
|
||||
labels: ["bug?"]
|
||||
body:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug-report_en.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report_en.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug report
|
||||
description: Report bugs or vulnerabilities using this template. For container network connection issues, refer to the documentation https://docs.langbot.app/en/workshop/network-details.html
|
||||
description: Report bugs or vulnerabilities using this template. For container network connection issues, refer to the documentation https://link.langbot.app/en/docs/network
|
||||
title: "[Bug]: "
|
||||
labels: ["bug?"]
|
||||
body:
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -52,11 +52,3 @@ src/langbot/web/
|
||||
/dist
|
||||
/build
|
||||
*.egg-info
|
||||
|
||||
# Docker 部署产生的本地文件
|
||||
docker/data/
|
||||
docker/docker-compose.override.yaml
|
||||
|
||||
# 备份目录
|
||||
LangBot_backup_*/
|
||||
*.bak
|
||||
|
||||
@@ -10,18 +10,14 @@ FROM python:3.12.7-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Use Chinese mirror for faster and more reliable package downloads
|
||||
RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \
|
||||
sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list 2>/dev/null || true
|
||||
|
||||
COPY . .
|
||||
|
||||
COPY --from=node /app/web/out ./web/out
|
||||
|
||||
RUN apt update \
|
||||
&& apt install gcc -y \
|
||||
&& python -m pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple uv \
|
||||
&& uv sync --index-url https://pypi.tuna.tsinghua.edu.cn/simple \
|
||||
&& python -m pip install --no-cache-dir uv \
|
||||
&& uv sync \
|
||||
&& touch /.dockerenv
|
||||
|
||||
CMD [ "uv", "run", "--no-sync", "main.py" ]
|
||||
12
README.md
12
README.md
@@ -19,9 +19,9 @@ English / [简体中文](README_CN.md) / [繁體中文](README_TW.md) / [日本
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">Website</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features">Features</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide">Docs</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">Features</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">Docs</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app/cloud">Cloud</a> |
|
||||
<a href="https://space.langbot.app">Plugin Market</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">Roadmap</a>
|
||||
@@ -45,7 +45,7 @@ LangBot is an **open-source, production-grade platform** for building AI-powered
|
||||
- **Web Management Panel** — Configure, manage, and monitor your bots through an intuitive browser interface. No YAML editing required.
|
||||
- **Multi-Pipeline Architecture** — Different bots for different scenarios, with comprehensive monitoring and exception handling.
|
||||
|
||||
[→ Learn more about all features](https://docs.langbot.app/en/insight/features)
|
||||
[→ Learn more about all features](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -76,7 +76,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**More options:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker) · [Manual](https://docs.langbot.app/en/deploy/langbot/manual) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt) · [Kubernetes](./docker/README_K8S.md)
|
||||
**More options:** [Docker](https://link.langbot.app/en/docs/docker) · [Manual](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -124,7 +124,7 @@ docker compose up -d
|
||||
| [接口 AI](https://jiekou.ai/) | Gateway | ✅ |
|
||||
| [302.AI](https://share.302.ai/SuTG99) | Gateway | ✅ |
|
||||
|
||||
[→ View all integrations](https://docs.langbot.app/en/insight/features)
|
||||
[→ View all integrations](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_CN.md
12
README_CN.md
@@ -21,9 +21,9 @@
|
||||
[](https://gitcode.com/RockChinQ/LangBot)
|
||||
|
||||
<a href="https://langbot.app">官网</a> |
|
||||
<a href="https://docs.langbot.app/zh/insight/features.html">特性</a> |
|
||||
<a href="https://docs.langbot.app/zh/insight/guide.html">文档</a> |
|
||||
<a href="https://docs.langbot.app/zh/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/features">特性</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/guide">文档</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app/cloud">Cloud</a> |
|
||||
<a href="https://space.langbot.app">插件市场</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">路线图</a>
|
||||
@@ -45,7 +45,7 @@ LangBot 是一个**开源的生产级平台**,用于构建 AI 驱动的即时
|
||||
- **Web 管理面板** — 通过浏览器直观地配置、管理和监控机器人,无需手动编辑配置文件。
|
||||
- **多流水线架构** — 不同机器人用于不同场景,具备全面的监控和异常处理能力。
|
||||
|
||||
[→ 了解更多功能特性](https://docs.langbot.app/zh/insight/features.html)
|
||||
[→ 了解更多功能特性](https://link.langbot.app/zh/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -76,7 +76,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/zh-CN/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**更多方式:** [Docker](https://docs.langbot.app/zh/deploy/langbot/docker.html) · [手动部署](https://docs.langbot.app/zh/deploy/langbot/manual.html) · [宝塔面板](https://docs.langbot.app/zh/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**更多方式:** [Docker](https://link.langbot.app/zh/docs/docker) · [手动部署](https://link.langbot.app/zh/docs/manual-deploy) · [宝塔面板](https://link.langbot.app/zh/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -125,7 +125,7 @@ docker compose up -d
|
||||
| [小马算力](https://www.tokenpony.cn/453z1) | 聚合平台 | ✅ |
|
||||
| [百宝箱Tbox](https://www.tbox.cn/open) | 智能体平台 | ✅ |
|
||||
|
||||
[→ 查看完整集成列表](https://docs.langbot.app/zh/insight/features.html)
|
||||
[→ 查看完整集成列表](https://link.langbot.app/zh/docs/features)
|
||||
|
||||
### TTS(语音合成)
|
||||
|
||||
|
||||
12
README_ES.md
12
README_ES.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">Inicio</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features.html">Características</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide.html">Documentación</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">Características</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">Documentación</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">Mercado de Plugins</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">Hoja de Ruta</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot es una **plataforma de código abierto y grado de producción** para con
|
||||
- **Panel de Gestión Web** — Configure, gestione y monitoree sus bots a través de una interfaz de navegador intuitiva. Sin necesidad de editar YAML.
|
||||
- **Arquitectura Multi-Pipeline** — Diferentes bots para diferentes escenarios, con monitoreo completo y manejo de excepciones.
|
||||
|
||||
[→ Conocer más sobre todas las funcionalidades](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Conocer más sobre todas las funcionalidades](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**Más opciones:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [Manual](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**Más opciones:** [Docker](https://link.langbot.app/en/docs/docker) · [Manual](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [接口 AI](https://jiekou.ai/) | Pasarela | ✅ |
|
||||
| [302.AI](https://share.302.ai/SuTG99) | Pasarela | ✅ |
|
||||
|
||||
[→ Ver todas las integraciones](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Ver todas las integraciones](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_FR.md
12
README_FR.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">Accueil</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features.html">Fonctionnalités</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide.html">Documentation</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">Fonctionnalités</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">Documentation</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">Marché des Plugins</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">Feuille de Route</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot est une **plateforme open-source de niveau production** pour créer des
|
||||
- **Panneau de Gestion Web** — Configurez, gérez et surveillez vos bots via une interface navigateur intuitive. Aucune édition de YAML requise.
|
||||
- **Architecture Multi-Pipeline** — Différents bots pour différents scénarios, avec surveillance complète et gestion des exceptions.
|
||||
|
||||
[→ En savoir plus sur toutes les fonctionnalités](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ En savoir plus sur toutes les fonctionnalités](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**Plus d'options :** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [Manuel](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**Plus d'options :** [Docker](https://link.langbot.app/en/docs/docker) · [Manuel](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | Plateforme GPU | ✅ |
|
||||
| [ShengSuanYun](https://www.shengsuanyun.com/?from=CH_KYIPP758) | Plateforme GPU | ✅ |
|
||||
|
||||
[→ Voir toutes les intégrations](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Voir toutes les intégrations](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_JP.md
12
README_JP.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">ホーム</a> |
|
||||
<a href="https://docs.langbot.app/ja/insight/features.html">機能</a> |
|
||||
<a href="https://docs.langbot.app/ja/insight/guide.html">ドキュメント</a> |
|
||||
<a href="https://docs.langbot.app/ja/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/ja/docs/features">機能</a> |
|
||||
<a href="https://link.langbot.app/ja/docs/guide">ドキュメント</a> |
|
||||
<a href="https://link.langbot.app/ja/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">プラグインマーケット</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">ロードマップ</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot は、AI搭載のインスタントメッセージングボットを構
|
||||
- **Web管理パネル** — 直感的なブラウザインターフェースからボットの設定、管理、監視が可能。YAML編集は不要。
|
||||
- **マルチパイプラインアーキテクチャ** — 異なるシナリオに異なるボットを配置し、包括的な監視と例外処理を実現。
|
||||
|
||||
[→ すべての機能について詳しく見る](https://docs.langbot.app/ja/insight/features.html)
|
||||
[→ すべての機能について詳しく見る](https://link.langbot.app/ja/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**その他:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [手動デプロイ](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**その他:** [Docker](https://link.langbot.app/en/docs/docker) · [手動デプロイ](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [接口 AI](https://jiekou.ai/) | ゲートウェイ | ✅ |
|
||||
| [302.AI](https://share.302.ai/SuTG99) | ゲートウェイ | ✅ |
|
||||
|
||||
[→ すべての統合を表示](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ すべての統合を表示](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_KO.md
12
README_KO.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">홈</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features.html">기능</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide.html">문서</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">기능</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">문서</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">플러그인 마켓</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">로드맵</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot은 AI 기반 인스턴트 메시징 봇을 구축하기 위한 **오픈
|
||||
- **웹 관리 패널** — 직관적인 브라우저 인터페이스로 봇을 구성, 관리 및 모니터링. YAML 편집 불필요.
|
||||
- **멀티 파이프라인 아키텍처** — 다양한 시나리오에 맞는 다양한 봇 구성, 종합 모니터링 및 예외 처리.
|
||||
|
||||
[→ 모든 기능 자세히 보기](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ 모든 기능 자세히 보기](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**더 많은 옵션:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [수동 배포](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**더 많은 옵션:** [Docker](https://link.langbot.app/en/docs/docker) · [수동 배포](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [接口 AI](https://jiekou.ai/) | 게이트웨이 | ✅ |
|
||||
| [302.AI](https://share.302.ai/SuTG99) | 게이트웨이 | ✅ |
|
||||
|
||||
[→ 모든 통합 보기](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ 모든 통합 보기](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_RU.md
12
README_RU.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">Главная</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features.html">Возможности</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide.html">Документация</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">Возможности</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">Документация</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">Магазин плагинов</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">Дорожная карта</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot — это **платформа с открытым исходным к
|
||||
- **Веб-панель управления** — Настраивайте, управляйте и мониторьте ваших ботов через интуитивный браузерный интерфейс. Ручное редактирование YAML не требуется.
|
||||
- **Мультиконвейерная архитектура** — Разные боты для разных сценариев с комплексным мониторингом и обработкой исключений.
|
||||
|
||||
[→ Подробнее обо всех возможностях](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Подробнее обо всех возможностях](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**Другие варианты:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [Ручная установка](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**Другие варианты:** [Docker](https://link.langbot.app/en/docs/docker) · [Ручная установка](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | Платформа GPU | ✅ |
|
||||
| [ShengSuanYun](https://www.shengsuanyun.com/?from=CH_KYIPP758) | Платформа GPU | ✅ |
|
||||
|
||||
[→ Смотреть все интеграции](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Смотреть все интеграции](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_TW.md
12
README_TW.md
@@ -21,9 +21,9 @@
|
||||
[](https://gitcode.com/RockChinQ/LangBot)
|
||||
|
||||
<a href="https://langbot.app">官網</a> |
|
||||
<a href="https://docs.langbot.app/zh/insight/features.html">特性</a> |
|
||||
<a href="https://docs.langbot.app/zh/insight/guide.html">文件</a> |
|
||||
<a href="https://docs.langbot.app/zh/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/features">特性</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/guide">文件</a> |
|
||||
<a href="https://link.langbot.app/zh/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">外掛市場</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">路線圖</a>
|
||||
|
||||
@@ -46,7 +46,7 @@ LangBot 是一個**開源的生產級平台**,用於建構 AI 驅動的即時
|
||||
- **Web 管理面板** — 透過瀏覽器直觀地配置、管理和監控機器人,無需手動編輯設定檔。
|
||||
- **多流水線架構** — 不同機器人用於不同場景,具備全面的監控和異常處理能力。
|
||||
|
||||
[→ 了解更多功能特性](https://docs.langbot.app/zh/insight/features.html)
|
||||
[→ 了解更多功能特性](https://link.langbot.app/zh/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/zh-CN/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**更多方式:** [Docker](https://docs.langbot.app/zh/deploy/langbot/docker.html) · [手動部署](https://docs.langbot.app/zh/deploy/langbot/manual.html) · [寶塔面板](https://docs.langbot.app/zh/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**更多方式:** [Docker](https://link.langbot.app/zh/docs/docker) · [手動部署](https://link.langbot.app/zh/docs/manual-deploy) · [寶塔面板](https://link.langbot.app/zh/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -139,7 +139,7 @@ docker compose up -d
|
||||
|-----------|------|
|
||||
| 阿里雲百煉 | [外掛](https://github.com/Thetail001/LangBot_BailianTextToImagePlugin) |
|
||||
|
||||
[→ 查看完整整合列表](https://docs.langbot.app/zh/insight/features.html)
|
||||
[→ 查看完整整合列表](https://link.langbot.app/zh/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
README_VI.md
12
README_VI.md
@@ -19,9 +19,9 @@
|
||||
[](https://github.com/langbot-app/LangBot/stargazers)
|
||||
|
||||
<a href="https://langbot.app">Trang chủ</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/features.html">Tính năng</a> |
|
||||
<a href="https://docs.langbot.app/en/insight/guide.html">Tài liệu</a> |
|
||||
<a href="https://docs.langbot.app/en/tags/readme.html">API</a> |
|
||||
<a href="https://link.langbot.app/en/docs/features">Tính năng</a> |
|
||||
<a href="https://link.langbot.app/en/docs/guide">Tài liệu</a> |
|
||||
<a href="https://link.langbot.app/en/docs/api">API</a> |
|
||||
<a href="https://space.langbot.app">Chợ Plugin</a> |
|
||||
<a href="https://langbot.featurebase.app/roadmap">Lộ trình</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ LangBot là một **nền tảng mã nguồn mở, cấp sản xuất** để x
|
||||
- **Bảng quản lý Web** — Cấu hình, quản lý và giám sát bot thông qua giao diện trình duyệt trực quan. Không cần chỉnh sửa YAML.
|
||||
- **Kiến trúc đa Pipeline** — Các bot khác nhau cho các kịch bản khác nhau, với giám sát toàn diện và xử lý ngoại lệ.
|
||||
|
||||
[→ Tìm hiểu thêm về tất cả tính năng](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Tìm hiểu thêm về tất cả tính năng](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
@@ -75,7 +75,7 @@ docker compose up -d
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
[](https://railway.app/template/yRrAyL?referralCode=vogKPF)
|
||||
|
||||
**Thêm tùy chọn:** [Docker](https://docs.langbot.app/en/deploy/langbot/docker.html) · [Thủ công](https://docs.langbot.app/en/deploy/langbot/manual.html) · [BTPanel](https://docs.langbot.app/en/deploy/langbot/one-click/bt.html) · [Kubernetes](./docker/README_K8S.md)
|
||||
**Thêm tùy chọn:** [Docker](https://link.langbot.app/en/docs/docker) · [Thủ công](https://link.langbot.app/en/docs/manual-deploy) · [BTPanel](https://link.langbot.app/en/docs/bt-panel) · [Kubernetes](./docker/README_K8S.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose up -d
|
||||
| [接口 AI](https://jiekou.ai/) | Cổng | ✅ |
|
||||
| [302.AI](https://share.302.ai/SuTG99) | Cổng | ✅ |
|
||||
|
||||
[→ Xem tất cả tích hợp](https://docs.langbot.app/en/insight/features.html)
|
||||
[→ Xem tất cả tích hợp](https://link.langbot.app/en/docs/features)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ spec:
|
||||
### 参考资源
|
||||
|
||||
- [LangBot 官方文档](https://docs.langbot.app)
|
||||
- [Docker 部署文档](https://docs.langbot.app/zh/deploy/langbot/docker.html)
|
||||
- [Docker 部署文档](https://link.langbot.app/zh/docs/docker)
|
||||
- [Kubernetes 官方文档](https://kubernetes.io/docs/)
|
||||
|
||||
---
|
||||
@@ -625,5 +625,5 @@ spec:
|
||||
### References
|
||||
|
||||
- [LangBot Official Documentation](https://docs.langbot.app)
|
||||
- [Docker Deployment Guide](https://docs.langbot.app/zh/deploy/langbot/docker.html)
|
||||
- [Docker Deployment Guide](https://link.langbot.app/zh/docs/docker)
|
||||
- [Kubernetes Official Documentation](https://kubernetes.io/docs/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "langbot"
|
||||
version = "4.9.4"
|
||||
version = "4.9.5"
|
||||
description = "Production-grade platform for building agentic IM bots"
|
||||
readme = "README.md"
|
||||
license-files = ["LICENSE"]
|
||||
@@ -64,7 +64,7 @@ dependencies = [
|
||||
"chromadb>=1.0.0,<2.0.0",
|
||||
"qdrant-client (>=1.15.1,<2.0.0)",
|
||||
"pyseekdb==1.1.0.post3",
|
||||
"langbot-plugin==0.3.5",
|
||||
"langbot-plugin==0.3.6",
|
||||
"asyncpg>=0.30.0",
|
||||
"line-bot-sdk>=3.19.0",
|
||||
"tboxsdk>=0.0.10",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""LangBot - Production-grade platform for building agentic IM bots"""
|
||||
|
||||
__version__ = '4.9.4'
|
||||
__version__ = '4.9.5'
|
||||
|
||||
@@ -900,8 +900,7 @@ class WecomBotClient:
|
||||
session = self.stream_sessions.get_session_by_feedback_id(feedback_id)
|
||||
if session:
|
||||
await self.logger.info(
|
||||
f'反馈关联到会话: stream_id={session.stream_id}, msg_id={session.msg_id}, '
|
||||
f'user_id={session.user_id}'
|
||||
f'反馈关联到会话: stream_id={session.stream_id}, msg_id={session.msg_id}, user_id={session.user_id}'
|
||||
)
|
||||
for handler in self._message_handlers.get('feedback', []):
|
||||
try:
|
||||
|
||||
@@ -1301,28 +1301,19 @@ class MonitoringService:
|
||||
satisfaction_rate = (total_likes / total_feedback * 100) if total_feedback > 0 else 0
|
||||
|
||||
# Get feedback by bot
|
||||
bot_stats_query = (
|
||||
sqlalchemy.select(
|
||||
persistence_monitoring.MonitoringFeedback.bot_id,
|
||||
persistence_monitoring.MonitoringFeedback.bot_name,
|
||||
sqlalchemy.func.count(persistence_monitoring.MonitoringFeedback.id).label('total'),
|
||||
sqlalchemy.func.sum(
|
||||
sqlalchemy.case(
|
||||
(persistence_monitoring.MonitoringFeedback.feedback_type == 1, 1),
|
||||
else_=0
|
||||
)
|
||||
).label('likes'),
|
||||
sqlalchemy.func.sum(
|
||||
sqlalchemy.case(
|
||||
(persistence_monitoring.MonitoringFeedback.feedback_type == 2, 1),
|
||||
else_=0
|
||||
)
|
||||
).label('dislikes'),
|
||||
)
|
||||
.group_by(
|
||||
persistence_monitoring.MonitoringFeedback.bot_id,
|
||||
persistence_monitoring.MonitoringFeedback.bot_name,
|
||||
)
|
||||
bot_stats_query = sqlalchemy.select(
|
||||
persistence_monitoring.MonitoringFeedback.bot_id,
|
||||
persistence_monitoring.MonitoringFeedback.bot_name,
|
||||
sqlalchemy.func.count(persistence_monitoring.MonitoringFeedback.id).label('total'),
|
||||
sqlalchemy.func.sum(
|
||||
sqlalchemy.case((persistence_monitoring.MonitoringFeedback.feedback_type == 1, 1), else_=0)
|
||||
).label('likes'),
|
||||
sqlalchemy.func.sum(
|
||||
sqlalchemy.case((persistence_monitoring.MonitoringFeedback.feedback_type == 2, 1), else_=0)
|
||||
).label('dislikes'),
|
||||
).group_by(
|
||||
persistence_monitoring.MonitoringFeedback.bot_id,
|
||||
persistence_monitoring.MonitoringFeedback.bot_name,
|
||||
)
|
||||
if conditions:
|
||||
bot_stats_query = bot_stats_query.where(sqlalchemy.and_(*conditions))
|
||||
@@ -1433,7 +1424,9 @@ class MonitoringService:
|
||||
'id': row[0].id if isinstance(row, tuple) else row.id,
|
||||
'timestamp': self._format_timestamp(row[0].timestamp if isinstance(row, tuple) else row.timestamp),
|
||||
'feedback_id': row[0].feedback_id if isinstance(row, tuple) else row.feedback_id,
|
||||
'feedback_type': 'like' if (row[0].feedback_type if isinstance(row, tuple) else row.feedback_type) == 1 else 'dislike',
|
||||
'feedback_type': 'like'
|
||||
if (row[0].feedback_type if isinstance(row, tuple) else row.feedback_type) == 1
|
||||
else 'dislike',
|
||||
'feedback_content': row[0].feedback_content if isinstance(row, tuple) else row.feedback_content,
|
||||
'inaccurate_reasons': row[0].inaccurate_reasons if isinstance(row, tuple) else row.inaccurate_reasons,
|
||||
'bot_id': row[0].bot_id if isinstance(row, tuple) else row.bot_id,
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import sqlalchemy
|
||||
from .. import migration
|
||||
|
||||
|
||||
@migration.migration_class(25)
|
||||
class DBMigrateFeedbackStats(migration.DBMigration):
|
||||
"""Add monitoring_feedback table for storing user feedback from AI Bot conversations"""
|
||||
|
||||
async def _table_exists(self, table_name: str) -> bool:
|
||||
"""Check if a table exists (works for both SQLite and PostgreSQL)."""
|
||||
if self.ap.persistence_mgr.db.name == 'postgresql':
|
||||
result = await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.text(
|
||||
'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = :table_name);'
|
||||
).bindparams(table_name=table_name)
|
||||
)
|
||||
return bool(result.scalar())
|
||||
else:
|
||||
result = await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.text("SELECT name FROM sqlite_master WHERE type='table' AND name=:table_name;").bindparams(
|
||||
table_name=table_name
|
||||
)
|
||||
)
|
||||
return result.first() is not None
|
||||
|
||||
async def upgrade(self):
|
||||
"""Create monitoring_feedback table."""
|
||||
if await self._table_exists('monitoring_feedback'):
|
||||
self.ap.logger.debug('monitoring_feedback table already exists, skipping migration.')
|
||||
return
|
||||
|
||||
# Create monitoring_feedback table with all columns
|
||||
create_table_sql = '''
|
||||
CREATE TABLE monitoring_feedback (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
timestamp DATETIME NOT NULL,
|
||||
feedback_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
feedback_type INTEGER NOT NULL,
|
||||
feedback_content TEXT,
|
||||
inaccurate_reasons TEXT,
|
||||
bot_id VARCHAR(255),
|
||||
bot_name VARCHAR(255),
|
||||
pipeline_id VARCHAR(255),
|
||||
pipeline_name VARCHAR(255),
|
||||
session_id VARCHAR(255),
|
||||
message_id VARCHAR(255),
|
||||
stream_id VARCHAR(255),
|
||||
user_id VARCHAR(255),
|
||||
platform VARCHAR(255)
|
||||
)
|
||||
'''
|
||||
await self.ap.persistence_mgr.execute_async(sqlalchemy.text(create_table_sql))
|
||||
|
||||
# Create indexes
|
||||
indexes = [
|
||||
'CREATE INDEX ix_monitoring_feedback_timestamp ON monitoring_feedback (timestamp)',
|
||||
'CREATE UNIQUE INDEX ix_monitoring_feedback_feedback_id ON monitoring_feedback (feedback_id)',
|
||||
'CREATE INDEX ix_monitoring_feedback_bot_id ON monitoring_feedback (bot_id)',
|
||||
'CREATE INDEX ix_monitoring_feedback_pipeline_id ON monitoring_feedback (pipeline_id)',
|
||||
'CREATE INDEX ix_monitoring_feedback_session_id ON monitoring_feedback (session_id)',
|
||||
'CREATE INDEX ix_monitoring_feedback_message_id ON monitoring_feedback (message_id)',
|
||||
'CREATE INDEX ix_monitoring_feedback_stream_id ON monitoring_feedback (stream_id)',
|
||||
]
|
||||
for index_sql in indexes:
|
||||
await self.ap.persistence_mgr.execute_async(sqlalchemy.text(index_sql))
|
||||
|
||||
self.ap.logger.info('Created monitoring_feedback table with indexes.')
|
||||
|
||||
async def downgrade(self):
|
||||
"""Drop monitoring_feedback table."""
|
||||
if await self._table_exists('monitoring_feedback'):
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.text('DROP TABLE monitoring_feedback')
|
||||
)
|
||||
self.ap.logger.info('Dropped monitoring_feedback table.')
|
||||
@@ -353,62 +353,3 @@ class LLMCallMonitor:
|
||||
)
|
||||
|
||||
return False # Don't suppress exceptions
|
||||
|
||||
|
||||
class FeedbackMonitor:
|
||||
"""Helper for recording user feedback from AI Bot conversations"""
|
||||
|
||||
@staticmethod
|
||||
async def record_feedback(
|
||||
ap: app.Application,
|
||||
feedback_id: str,
|
||||
feedback_type: int,
|
||||
feedback_content: str | None = None,
|
||||
inaccurate_reasons: list[str] | None = None,
|
||||
bot_id: str | None = None,
|
||||
bot_name: str | None = None,
|
||||
pipeline_id: str | None = None,
|
||||
pipeline_name: str | None = None,
|
||||
session_id: str | None = None,
|
||||
message_id: str | None = None,
|
||||
stream_id: str | None = None,
|
||||
user_id: str | None = None,
|
||||
platform: str = 'wecom',
|
||||
):
|
||||
"""Record user feedback (like/dislike) from AI Bot conversation.
|
||||
|
||||
Args:
|
||||
ap: Application instance
|
||||
feedback_id: Unique feedback identifier from platform
|
||||
feedback_type: 1 = like, 2 = dislike
|
||||
feedback_content: Optional user feedback text
|
||||
inaccurate_reasons: List of reasons for inaccurate response
|
||||
bot_id: Bot UUID
|
||||
bot_name: Bot name
|
||||
pipeline_id: Pipeline UUID
|
||||
pipeline_name: Pipeline name
|
||||
session_id: Session ID
|
||||
message_id: Message ID
|
||||
stream_id: Stream ID
|
||||
user_id: User ID
|
||||
platform: Platform name (default: wecom)
|
||||
"""
|
||||
try:
|
||||
await ap.monitoring_service.record_feedback(
|
||||
feedback_id=feedback_id,
|
||||
feedback_type=feedback_type,
|
||||
feedback_content=feedback_content,
|
||||
inaccurate_reasons=inaccurate_reasons,
|
||||
bot_id=bot_id,
|
||||
bot_name=bot_name,
|
||||
pipeline_id=pipeline_id,
|
||||
pipeline_name=pipeline_name,
|
||||
session_id=session_id,
|
||||
message_id=message_id,
|
||||
stream_id=stream_id,
|
||||
user_id=user_id,
|
||||
platform=platform,
|
||||
)
|
||||
ap.logger.info(f'Recorded feedback: feedback_id={feedback_id}, type={feedback_type}')
|
||||
except Exception as e:
|
||||
ap.logger.error(f'Failed to record feedback: {e}')
|
||||
|
||||
@@ -142,6 +142,50 @@ class RuntimeBot:
|
||||
self.adapter.register_listener(platform_events.FriendMessage, on_friend_message)
|
||||
self.adapter.register_listener(platform_events.GroupMessage, on_group_message)
|
||||
|
||||
# Register feedback listener (only effective on adapters that support it)
|
||||
async def on_feedback(
|
||||
event: platform_events.FeedbackEvent,
|
||||
adapter: abstract_platform_adapter.AbstractMessagePlatformAdapter,
|
||||
):
|
||||
try:
|
||||
# Resolve pipeline name
|
||||
pipeline_name = ''
|
||||
if self.bot_entity.use_pipeline_uuid:
|
||||
try:
|
||||
pipeline_result = await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.select(persistence_pipeline.LegacyPipeline.name).where(
|
||||
persistence_pipeline.LegacyPipeline.uuid == self.bot_entity.use_pipeline_uuid
|
||||
)
|
||||
)
|
||||
pipeline_row = pipeline_result.first()
|
||||
if pipeline_row:
|
||||
pipeline_name = pipeline_row[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await self.ap.monitoring_service.record_feedback(
|
||||
feedback_id=event.feedback_id,
|
||||
feedback_type=event.feedback_type,
|
||||
feedback_content=event.feedback_content,
|
||||
inaccurate_reasons=event.inaccurate_reasons,
|
||||
bot_id=self.bot_entity.uuid,
|
||||
bot_name=self.bot_entity.name,
|
||||
pipeline_id=self.bot_entity.use_pipeline_uuid or '',
|
||||
pipeline_name=pipeline_name,
|
||||
session_id=event.session_id,
|
||||
message_id=event.message_id,
|
||||
stream_id=event.stream_id,
|
||||
user_id=event.user_id,
|
||||
platform=adapter.__class__.__name__,
|
||||
)
|
||||
await self.logger.info(
|
||||
f'Recorded feedback: feedback_id={event.feedback_id}, type={event.feedback_type}'
|
||||
)
|
||||
except Exception:
|
||||
await self.logger.error(f'Failed to record feedback: {traceback.format_exc()}')
|
||||
|
||||
self.adapter.register_listener(platform_events.FeedbackEvent, on_feedback)
|
||||
|
||||
async def run(self):
|
||||
async def exception_wrapper():
|
||||
try:
|
||||
@@ -268,35 +312,12 @@ class PlatformManager:
|
||||
adapter_inst = self.adapter_dict[bot_entity.adapter](
|
||||
bot_entity.adapter_config,
|
||||
logger,
|
||||
ap=self.ap,
|
||||
)
|
||||
|
||||
# 如果 adapter 支持 set_bot_uuid 方法,设置 bot_uuid(用于统一 webhook)
|
||||
if hasattr(adapter_inst, 'set_bot_uuid'):
|
||||
adapter_inst.set_bot_uuid(bot_entity.uuid)
|
||||
|
||||
# 如果 adapter 支持 set_bot_info 方法,设置 bot 信息(用于监控记录)
|
||||
if hasattr(adapter_inst, 'set_bot_info'):
|
||||
pipeline_name = ''
|
||||
if bot_entity.use_pipeline_uuid:
|
||||
try:
|
||||
pipeline_result = await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.select(persistence_pipeline.LegacyPipeline.name).where(
|
||||
persistence_pipeline.LegacyPipeline.uuid == bot_entity.use_pipeline_uuid
|
||||
)
|
||||
)
|
||||
pipeline_row = pipeline_result.first()
|
||||
if pipeline_row:
|
||||
pipeline_name = pipeline_row[0]
|
||||
except Exception:
|
||||
pass
|
||||
adapter_inst.set_bot_info(
|
||||
bot_id=bot_entity.uuid,
|
||||
bot_name=bot_entity.name,
|
||||
pipeline_id=bot_entity.use_pipeline_uuid or '',
|
||||
pipeline_name=pipeline_name,
|
||||
)
|
||||
|
||||
runtime_bot = RuntimeBot(ap=self.ap, bot_entity=bot_entity, adapter=adapter_inst, logger=logger)
|
||||
|
||||
await runtime_bot.initialize()
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- protocol
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/aiocqhttp
|
||||
en: https://link.langbot.app/en/platforms/aiocqhttp
|
||||
ja: https://link.langbot.app/ja/platforms/aiocqhttp
|
||||
config:
|
||||
- name: host
|
||||
label:
|
||||
|
||||
@@ -139,7 +139,7 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
dict # 回复卡片消息字典,key为消息id,value为回复卡片实例id,用于在流式消息时判断是否发送到指定卡片
|
||||
)
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
required_keys = [
|
||||
'client_id',
|
||||
'client_secret',
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/dingtalk
|
||||
en: https://link.langbot.app/en/platforms/dingtalk
|
||||
ja: https://link.langbot.app/ja/platforms/dingtalk
|
||||
config:
|
||||
- name: client_id
|
||||
label:
|
||||
|
||||
@@ -23,6 +23,10 @@ spec:
|
||||
categories:
|
||||
- popular
|
||||
- global
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/discord
|
||||
en: https://link.langbot.app/en/platforms/discord
|
||||
ja: https://link.langbot.app/ja/platforms/discord
|
||||
config:
|
||||
- name: client_id
|
||||
label:
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/kook
|
||||
en: https://link.langbot.app/en/platforms/kook
|
||||
ja: https://link.langbot.app/ja/platforms/kook
|
||||
config:
|
||||
- name: token
|
||||
label:
|
||||
|
||||
@@ -18,6 +18,10 @@ spec:
|
||||
- popular
|
||||
- china
|
||||
- global
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/lark
|
||||
en: https://link.langbot.app/en/platforms/lark
|
||||
ja: https://link.langbot.app/ja/platforms/lark
|
||||
config:
|
||||
- name: app_id
|
||||
label:
|
||||
|
||||
@@ -136,7 +136,7 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
|
||||
seq: int # 用于在发送卡片消息中识别消息顺序,直接以seq作为标识
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
configuration = Configuration(access_token=config['channel_access_token'])
|
||||
line_webhook = WebhookHandler(config['channel_secret'])
|
||||
parser = WebhookParser(config['channel_secret'])
|
||||
|
||||
@@ -21,6 +21,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- global
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/line
|
||||
en: https://link.langbot.app/en/platforms/line
|
||||
ja: https://link.langbot.app/ja/platforms/line
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -60,7 +60,7 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
|
||||
bot: typing.Union[OAClient, OAClientForLongerResponse] = pydantic.Field(exclude=True)
|
||||
bot_uuid: str = None
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
# 校验必填项
|
||||
required_keys = ['token', 'EncodingAESKey', 'AppSecret', 'AppID', 'Mode']
|
||||
missing_keys = [k for k in required_keys if k not in config]
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/officialaccount
|
||||
en: https://link.langbot.app/en/platforms/officialaccount
|
||||
ja: https://link.langbot.app/ja/platforms/officialaccount
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -15,6 +15,10 @@ spec:
|
||||
categories:
|
||||
- popular
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/openclaw_weixin
|
||||
en: https://link.langbot.app/en/platforms/openclaw_weixin
|
||||
ja: https://link.langbot.app/ja/platforms/openclaw_weixin
|
||||
config:
|
||||
- name: base_url
|
||||
label:
|
||||
|
||||
@@ -132,7 +132,7 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter
|
||||
message_converter: QQOfficialMessageConverter = QQOfficialMessageConverter()
|
||||
event_converter: QQOfficialEventConverter = QQOfficialEventConverter()
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
bot = QQOfficialClient(
|
||||
app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger, unified_mode=True
|
||||
)
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/qqofficial
|
||||
en: https://link.langbot.app/en/platforms/qqofficial
|
||||
ja: https://link.langbot.app/ja/platforms/qqofficial
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -20,6 +20,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- protocol
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/satori
|
||||
en: https://link.langbot.app/en/platforms/satori
|
||||
ja: https://link.langbot.app/ja/platforms/satori
|
||||
config:
|
||||
- name: platform
|
||||
label:
|
||||
|
||||
@@ -99,7 +99,7 @@ class SlackAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
event_converter: SlackEventConverter = SlackEventConverter()
|
||||
config: dict
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
required_keys = [
|
||||
'bot_token',
|
||||
'signing_secret',
|
||||
|
||||
@@ -23,6 +23,10 @@ spec:
|
||||
categories:
|
||||
- popular
|
||||
- global
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/slack
|
||||
en: https://link.langbot.app/en/platforms/slack
|
||||
ja: https://link.langbot.app/ja/platforms/slack
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -23,6 +23,10 @@ spec:
|
||||
categories:
|
||||
- popular
|
||||
- global
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/telegram
|
||||
en: https://link.langbot.app/en/platforms/telegram
|
||||
ja: https://link.langbot.app/ja/platforms/telegram
|
||||
config:
|
||||
- name: token
|
||||
label:
|
||||
|
||||
@@ -539,7 +539,7 @@ class WeChatPadAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
|
||||
typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
|
||||
] = {}
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
quart_app = quart.Quart(__name__)
|
||||
|
||||
message_converter = WeChatPadMessageConverter(config, logger)
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/wechatpad
|
||||
en: https://link.langbot.app/en/platforms/wechatpad
|
||||
ja: https://link.langbot.app/ja/platforms/wechatpad
|
||||
config:
|
||||
- name: wechatpad_url
|
||||
label:
|
||||
|
||||
@@ -206,7 +206,7 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
config: dict
|
||||
bot_uuid: str = None
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap=None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
# 校验必填项
|
||||
required_keys = [
|
||||
'corpid',
|
||||
|
||||
@@ -15,6 +15,10 @@ spec:
|
||||
categories:
|
||||
- popular
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/wecom
|
||||
en: https://link.langbot.app/en/platforms/wecom
|
||||
ja: https://link.langbot.app/ja/platforms/wecom
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -10,10 +10,8 @@ import langbot_plugin.api.entities.builtin.platform.events as platform_events
|
||||
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
|
||||
from ..logger import EventLogger
|
||||
from langbot.libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent
|
||||
from langbot.libs.wecom_ai_bot_api.api import WecomBotClient, StreamSession
|
||||
from langbot.libs.wecom_ai_bot_api.api import WecomBotClient
|
||||
from langbot.libs.wecom_ai_bot_api.ws_client import WecomBotWsClient
|
||||
from ...core import app as langbot_app
|
||||
from ...pipeline.monitoring_helper import FeedbackMonitor
|
||||
|
||||
|
||||
class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
|
||||
@@ -194,10 +192,8 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
_ws_mode: bool = False
|
||||
bot_name: str = ''
|
||||
listeners: dict = {}
|
||||
ap: langbot_app.Application = None # Application reference for monitoring
|
||||
_bot_info: dict = None # Bot info for monitoring (bot_id, bot_name, pipeline_id, pipeline_name)
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger, ap: langbot_app.Application = None, **kwargs):
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
enable_webhook = config.get('enable-webhook', False)
|
||||
bot_name = config.get('robot_name', '')
|
||||
|
||||
@@ -232,14 +228,8 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
bot_account_id=bot_account_id,
|
||||
bot_name=bot_name,
|
||||
event_converter=event_converter,
|
||||
**kwargs,
|
||||
)
|
||||
self.listeners = {}
|
||||
object.__setattr__(self, '_ws_mode', ws_mode)
|
||||
object.__setattr__(self, 'ap', ap)
|
||||
|
||||
# Register feedback handler for monitoring
|
||||
self._register_feedback_handler()
|
||||
|
||||
async def reply_message(
|
||||
self,
|
||||
@@ -321,6 +311,9 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
self.bot.on_message('single')(self.on_message)
|
||||
elif event_type == platform_events.GroupMessage:
|
||||
self.bot.on_message('group')(self.on_message)
|
||||
elif event_type == platform_events.FeedbackEvent:
|
||||
if hasattr(self.bot, 'on_feedback'):
|
||||
self.bot.on_feedback()(self._on_feedback)
|
||||
except Exception:
|
||||
print(traceback.format_exc())
|
||||
|
||||
@@ -328,65 +321,44 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
||||
"""设置 bot UUID(用于生成 webhook URL)"""
|
||||
self.bot_uuid = bot_uuid
|
||||
|
||||
def set_bot_info(
|
||||
self,
|
||||
bot_id: str,
|
||||
bot_name: str,
|
||||
pipeline_id: str,
|
||||
pipeline_name: str,
|
||||
):
|
||||
"""设置 bot 信息(用于监控记录)"""
|
||||
self._bot_info = {
|
||||
'bot_id': bot_id,
|
||||
'bot_name': bot_name,
|
||||
'pipeline_id': pipeline_id,
|
||||
'pipeline_name': pipeline_name,
|
||||
}
|
||||
async def _on_feedback(self, **kwargs):
|
||||
"""Handle feedback event from WeChat Work AI Bot SDK and dispatch as FeedbackEvent."""
|
||||
try:
|
||||
feedback_id = kwargs.get('feedback_id', '')
|
||||
feedback_type = kwargs.get('feedback_type', 0)
|
||||
feedback_content = kwargs.get('feedback_content', '') or None
|
||||
inaccurate_reasons = kwargs.get('inaccurate_reasons', []) or None
|
||||
session = kwargs.get('session')
|
||||
|
||||
def _register_feedback_handler(self):
|
||||
"""注册用户反馈处理器,用于持久化反馈数据到监控服务"""
|
||||
|
||||
async def handle_feedback(
|
||||
feedback_id: str,
|
||||
feedback_type: int,
|
||||
feedback_content: str,
|
||||
inaccurate_reasons: list[str],
|
||||
session: StreamSession,
|
||||
):
|
||||
"""处理用户反馈事件,持久化到监控服务"""
|
||||
if not self.ap or not self._bot_info:
|
||||
return
|
||||
|
||||
try:
|
||||
# Build session_id from session info
|
||||
session_id = None
|
||||
session_id = None
|
||||
user_id = None
|
||||
message_id = None
|
||||
stream_id = None
|
||||
if session:
|
||||
if session.chat_id:
|
||||
session_id = f'group_{session.chat_id}'
|
||||
elif session.user_id:
|
||||
session_id = f'person_{session.user_id}'
|
||||
user_id = session.user_id
|
||||
message_id = session.msg_id
|
||||
stream_id = session.stream_id
|
||||
|
||||
await FeedbackMonitor.record_feedback(
|
||||
ap=self.ap,
|
||||
feedback_id=feedback_id,
|
||||
feedback_type=feedback_type,
|
||||
feedback_content=feedback_content if feedback_content else None,
|
||||
inaccurate_reasons=inaccurate_reasons if inaccurate_reasons else None,
|
||||
bot_id=self._bot_info['bot_id'],
|
||||
bot_name=self._bot_info['bot_name'],
|
||||
pipeline_id=self._bot_info['pipeline_id'],
|
||||
pipeline_name=self._bot_info['pipeline_name'],
|
||||
session_id=session_id,
|
||||
message_id=session.msg_id if session else None,
|
||||
stream_id=session.stream_id if session else None,
|
||||
user_id=session.user_id if session else None,
|
||||
platform='wecom',
|
||||
)
|
||||
except Exception:
|
||||
await self.logger.error(f'Failed to record feedback: {traceback.format_exc()}')
|
||||
event = platform_events.FeedbackEvent(
|
||||
feedback_id=feedback_id,
|
||||
feedback_type=feedback_type,
|
||||
feedback_content=feedback_content,
|
||||
inaccurate_reasons=inaccurate_reasons,
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
message_id=message_id,
|
||||
stream_id=stream_id,
|
||||
source_platform_object=session,
|
||||
)
|
||||
|
||||
# Register the feedback handler with the bot client
|
||||
if hasattr(self.bot, 'on_feedback'):
|
||||
self.bot.on_feedback()(handle_feedback)
|
||||
if platform_events.FeedbackEvent in self.listeners:
|
||||
await self.listeners[platform_events.FeedbackEvent](event, self)
|
||||
except Exception:
|
||||
await self.logger.error(f'Error in wecombot feedback callback: {traceback.format_exc()}')
|
||||
|
||||
async def handle_unified_webhook(self, bot_uuid: str, path: str, request):
|
||||
_ws_mode = not self.config.get('enable-webhook', False)
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/wecombot
|
||||
en: https://link.langbot.app/en/platforms/wecombot
|
||||
ja: https://link.langbot.app/ja/platforms/wecombot
|
||||
config:
|
||||
- name: BotId
|
||||
label:
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
spec:
|
||||
categories:
|
||||
- china
|
||||
help_links:
|
||||
zh: https://link.langbot.app/zh/platforms/wecomcs
|
||||
en: https://link.langbot.app/en/platforms/wecomcs
|
||||
ja: https://link.langbot.app/ja/platforms/wecomcs
|
||||
config:
|
||||
- name: webhook_url
|
||||
label:
|
||||
|
||||
@@ -2,7 +2,7 @@ import langbot
|
||||
|
||||
semantic_version = f'v{langbot.__version__}'
|
||||
|
||||
required_database_version = 25
|
||||
required_database_version = 24
|
||||
"""Tag the version of the database schema, used to check if the database needs to be migrated"""
|
||||
|
||||
debug_mode = False
|
||||
|
||||
@@ -203,7 +203,7 @@ class VersionManager:
|
||||
try:
|
||||
if await self.ap.ver_mgr.is_new_version_available():
|
||||
return (
|
||||
'New version available:\n有新版本可用,根据文档更新: \nhttps://docs.langbot.app/zh/deploy/update.html',
|
||||
'New version available:\n有新版本可用,根据文档更新: \nhttps://link.langbot.app/zh/docs/update',
|
||||
logging.INFO,
|
||||
)
|
||||
|
||||
|
||||
10
uv.lock
generated
10
uv.lock
generated
@@ -1832,7 +1832,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langbot"
|
||||
version = "4.9.4"
|
||||
version = "4.9.5"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiocqhttp" },
|
||||
@@ -1937,7 +1937,7 @@ requires-dist = [
|
||||
{ name = "ebooklib", specifier = ">=0.18" },
|
||||
{ name = "gewechat-client", specifier = ">=0.1.5" },
|
||||
{ name = "html2text", specifier = ">=2024.2.26" },
|
||||
{ name = "langbot-plugin", specifier = "==0.3.5" },
|
||||
{ name = "langbot-plugin", specifier = "==0.3.6" },
|
||||
{ name = "langchain", specifier = ">=0.2.0" },
|
||||
{ name = "langchain-text-splitters", specifier = ">=0.0.1" },
|
||||
{ name = "lark-oapi", specifier = ">=1.4.15" },
|
||||
@@ -1993,7 +1993,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langbot-plugin"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
@@ -2011,9 +2011,9 @@ dependencies = [
|
||||
{ name = "watchdog" },
|
||||
{ name = "websockets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/8f/0a22e4461b0893ac2afb1b6aaebafe04c921df6dbbf4b8bd6c83cf6a97b2/langbot_plugin-0.3.5.tar.gz", hash = "sha256:79c7feb08f788f480435de8cdefc3cfed4de2dfb03978a460251b8c9d1c271d3", size = 171927, upload-time = "2026-03-25T13:53:18.334Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ff/f0/e5561bd1ebda0b9345ad6b98718b5f002bb3ca79b5ec294dc77cc10957b9/langbot_plugin-0.3.6.tar.gz", hash = "sha256:20db981e416a640f22246e54517abc2a095d8ccf5e69e06c2674fb8a443f5dbe", size = 179266, upload-time = "2026-03-30T15:58:58.523Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/93/fdd4eb54434a358a3917aec74190e2e1b64351a5bb955677f634d29fc4fd/langbot_plugin-0.3.5-py3-none-any.whl", hash = "sha256:4d31f92338e1e2dc343ae00982e4facbe7abae84f4d1c4e1375cdcac9d7155d7", size = 146575, upload-time = "2026-03-25T13:53:16.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/f5/ac424c2620e1be98a54a0b8ec0ed256a9c06cea7cd32a30732a1aea5fdc5/langbot_plugin-0.3.6-py3-none-any.whl", hash = "sha256:3238448436c41d50a0a0cf37438d845f0a1371159d440af3411a984e3d4e9eb7", size = 156752, upload-time = "2026-03-30T15:59:00.229Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1 +1 @@
|
||||
NEXT_PUBLIC_API_BASE_URL=http://192.168.1.97:5300
|
||||
NEXT_PUBLIC_API_BASE_URL=http://localhost:5300
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Debug LangBot Frontend
|
||||
|
||||
Please refer to the [Development Guide](https://docs.langbot.app/en/develop/dev-config.html) for more information.
|
||||
Please refer to the [Development Guide](https://link.langbot.app/en/docs/dev-config) for more information.
|
||||
|
||||
4375
web/pnpm-lock.yaml
generated
4375
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import i18n from 'i18next';
|
||||
import {
|
||||
IChooseAdapterEntity,
|
||||
IPipelineEntity,
|
||||
@@ -13,6 +14,8 @@ import { UUID } from 'uuidjs';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { Bot } from '@/app/infra/entities/api';
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@@ -102,6 +105,9 @@ export default function BotForm({
|
||||
const [adapterDescriptionList, setAdapterDescriptionList] = useState<
|
||||
Record<string, string>
|
||||
>({});
|
||||
const [adapterHelpLinks, setAdapterHelpLinks] = useState<
|
||||
Record<string, Record<string, string>>
|
||||
>({});
|
||||
|
||||
const [pipelineNameList, setPipelineNameList] = useState<IPipelineEntity[]>(
|
||||
[],
|
||||
@@ -209,6 +215,18 @@ export default function BotForm({
|
||||
),
|
||||
);
|
||||
|
||||
setAdapterHelpLinks(
|
||||
adaptersRes.adapters.reduce(
|
||||
(acc, item) => {
|
||||
if (item.spec.help_links) {
|
||||
acc[item.name] = item.spec.help_links;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, Record<string, string>>,
|
||||
),
|
||||
);
|
||||
|
||||
adaptersRes.adapters.forEach((rawAdapter) => {
|
||||
adapterNameToDynamicConfigMap.set(
|
||||
rawAdapter.name,
|
||||
@@ -469,59 +487,81 @@ export default function BotForm({
|
||||
<span className="text-destructive">*</span>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
handleAdapterSelect(value);
|
||||
}}
|
||||
value={field.value}
|
||||
>
|
||||
<SelectTrigger className="w-[240px]">
|
||||
{field.value ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={httpClient.getAdapterIconURL(field.value)}
|
||||
alt=""
|
||||
className="h-5 w-5 rounded"
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
handleAdapterSelect(value);
|
||||
}}
|
||||
value={field.value}
|
||||
>
|
||||
<SelectTrigger className="w-[240px]">
|
||||
{field.value ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={httpClient.getAdapterIconURL(field.value)}
|
||||
alt=""
|
||||
className="h-5 w-5 rounded"
|
||||
/>
|
||||
<span>
|
||||
{adapterNameList.find(
|
||||
(a) => a.value === field.value,
|
||||
)?.label ?? field.value}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue
|
||||
placeholder={t('bots.selectAdapter')}
|
||||
/>
|
||||
<span>
|
||||
{adapterNameList.find(
|
||||
(a) => a.value === field.value,
|
||||
)?.label ?? field.value}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue placeholder={t('bots.selectAdapter')} />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{groupedAdapters.map((group) => (
|
||||
<SelectGroup
|
||||
key={group.categoryId ?? 'uncategorized'}
|
||||
>
|
||||
{group.categoryId && (
|
||||
<SelectLabel>
|
||||
{getCategoryLabel(t, group.categoryId)}
|
||||
</SelectLabel>
|
||||
)}
|
||||
{group.items.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={httpClient.getAdapterIconURL(
|
||||
item.value,
|
||||
)}
|
||||
alt=""
|
||||
className="h-5 w-5 rounded"
|
||||
/>
|
||||
<span>{item.label}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{groupedAdapters.map((group) => (
|
||||
<SelectGroup
|
||||
key={group.categoryId ?? 'uncategorized'}
|
||||
>
|
||||
{group.categoryId && (
|
||||
<SelectLabel>
|
||||
{getCategoryLabel(t, group.categoryId)}
|
||||
</SelectLabel>
|
||||
)}
|
||||
{group.items.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={httpClient.getAdapterIconURL(
|
||||
item.value,
|
||||
)}
|
||||
alt=""
|
||||
className="h-5 w-5 rounded"
|
||||
/>
|
||||
<span>{item.label}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{currentAdapter &&
|
||||
(() => {
|
||||
const docUrl = getAdapterDocUrl(
|
||||
adapterHelpLinks[currentAdapter],
|
||||
i18n.language,
|
||||
);
|
||||
return docUrl ? (
|
||||
<a
|
||||
href={docUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex shrink-0 items-center gap-1 text-xs text-primary hover:underline"
|
||||
>
|
||||
{t('bots.viewAdapterDocs')}
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
) : null;
|
||||
})()}
|
||||
</div>
|
||||
</FormControl>
|
||||
{currentAdapter && adapterDescriptionList[currentAdapter] && (
|
||||
<FormDescription>
|
||||
|
||||
@@ -1423,12 +1423,12 @@ export default function HomeSidebar({
|
||||
localStorage.getItem('langbot_language');
|
||||
if (language === 'zh-Hans' || language === 'zh-Hant') {
|
||||
window.open(
|
||||
'https://docs.langbot.app/zh/insight/guide',
|
||||
'https://link.langbot.app/zh/docs/guide',
|
||||
'_blank',
|
||||
);
|
||||
} else {
|
||||
window.open(
|
||||
'https://docs.langbot.app/en/insight/guide',
|
||||
'https://link.langbot.app/en/docs/guide',
|
||||
'_blank',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ export const sidebarConfigList = [
|
||||
route: '/home/bots',
|
||||
description: t('bots.description'),
|
||||
helpLink: {
|
||||
en_US: 'https://docs.langbot.app/en/usage/platforms/readme',
|
||||
zh_Hans: 'https://docs.langbot.app/zh/usage/platforms/readme',
|
||||
ja_JP: 'https://docs.langbot.app/ja/usage/platforms/readme',
|
||||
en_US: 'https://link.langbot.app/en/docs/platforms',
|
||||
zh_Hans: 'https://link.langbot.app/zh/docs/platforms',
|
||||
ja_JP: 'https://link.langbot.app/ja/docs/platforms',
|
||||
},
|
||||
section: 'home',
|
||||
}),
|
||||
@@ -89,9 +89,9 @@ export const sidebarConfigList = [
|
||||
route: '/home/pipelines',
|
||||
description: t('pipelines.description'),
|
||||
helpLink: {
|
||||
en_US: 'https://docs.langbot.app/en/usage/pipelines/readme',
|
||||
zh_Hans: 'https://docs.langbot.app/zh/usage/pipelines/readme',
|
||||
ja_JP: 'https://docs.langbot.app/ja/usage/pipelines/readme',
|
||||
en_US: 'https://link.langbot.app/en/docs/pipelines',
|
||||
zh_Hans: 'https://link.langbot.app/zh/docs/pipelines',
|
||||
ja_JP: 'https://link.langbot.app/ja/docs/pipelines',
|
||||
},
|
||||
section: 'home',
|
||||
}),
|
||||
@@ -111,9 +111,9 @@ export const sidebarConfigList = [
|
||||
route: '/home/knowledge',
|
||||
description: t('knowledge.description'),
|
||||
helpLink: {
|
||||
en_US: 'https://docs.langbot.app/en/usage/knowledge/readme',
|
||||
zh_Hans: 'https://docs.langbot.app/zh/usage/knowledge/readme',
|
||||
ja_JP: 'https://docs.langbot.app/ja/usage/knowledge/readme',
|
||||
en_US: 'https://link.langbot.app/en/docs/knowledge',
|
||||
zh_Hans: 'https://link.langbot.app/zh/docs/knowledge',
|
||||
ja_JP: 'https://link.langbot.app/ja/docs/knowledge',
|
||||
},
|
||||
section: 'home',
|
||||
}),
|
||||
@@ -135,9 +135,9 @@ export const sidebarConfigList = [
|
||||
route: '/home/plugins',
|
||||
description: t('plugins.description'),
|
||||
helpLink: {
|
||||
en_US: 'https://docs.langbot.app/en/usage/plugin/plugin-intro',
|
||||
zh_Hans: 'https://docs.langbot.app/zh/usage/plugin/plugin-intro',
|
||||
ja_JP: 'https://docs.langbot.app/ja/usage/plugin/plugin-intro',
|
||||
en_US: 'https://link.langbot.app/en/docs/plugins',
|
||||
zh_Hans: 'https://link.langbot.app/zh/docs/plugins',
|
||||
ja_JP: 'https://link.langbot.app/ja/docs/plugins',
|
||||
},
|
||||
section: 'extensions',
|
||||
}),
|
||||
@@ -157,9 +157,9 @@ export const sidebarConfigList = [
|
||||
route: '/home/market',
|
||||
description: t('plugins.description'),
|
||||
helpLink: {
|
||||
en_US: 'https://docs.langbot.app/en/usage/plugin/plugin-intro',
|
||||
zh_Hans: 'https://docs.langbot.app/zh/usage/plugin/plugin-intro',
|
||||
ja_JP: 'https://docs.langbot.app/ja/usage/plugin/plugin-intro',
|
||||
en_US: 'https://link.langbot.app/en/docs/plugins',
|
||||
zh_Hans: 'https://link.langbot.app/zh/docs/plugins',
|
||||
ja_JP: 'https://link.langbot.app/ja/docs/plugins',
|
||||
},
|
||||
section: 'extensions',
|
||||
}),
|
||||
|
||||
@@ -36,11 +36,11 @@ export default function NewVersionDialog({
|
||||
const getUpdateDocsUrl = () => {
|
||||
const language = i18n.language;
|
||||
if (language === 'zh-Hans' || language === 'zh-Hant') {
|
||||
return 'https://docs.langbot.app/zh/deploy/update';
|
||||
return 'https://link.langbot.app/zh/docs/update';
|
||||
} else if (language === 'ja-JP') {
|
||||
return 'https://docs.langbot.app/ja/deploy/update';
|
||||
return 'https://link.langbot.app/ja/docs/update';
|
||||
} else {
|
||||
return 'https://docs.langbot.app/en/deploy/update';
|
||||
return 'https://link.langbot.app/en/docs/update';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ThumbsUp, ThumbsDown, TrendingUp, TrendingDown, Minus } from 'lucide-react';
|
||||
import {
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Minus,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface FeedbackCardProps {
|
||||
title: string;
|
||||
@@ -28,8 +34,10 @@ export function FeedbackCard({
|
||||
}: FeedbackCardProps) {
|
||||
const variantStyles = {
|
||||
default: 'bg-white dark:bg-[#2a2a2e] border-gray-200 dark:border-gray-700',
|
||||
success: 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800',
|
||||
warning: 'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800',
|
||||
success:
|
||||
'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800',
|
||||
warning:
|
||||
'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800',
|
||||
danger: 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800',
|
||||
};
|
||||
|
||||
@@ -64,7 +72,9 @@ export function FeedbackCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`p-6 rounded-xl border shadow-sm ${variantStyles[variant]}`}>
|
||||
<div
|
||||
className={`p-6 rounded-xl border shadow-sm ${variantStyles[variant]}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
@@ -79,15 +89,28 @@ export function FeedbackCard({
|
||||
</p>
|
||||
)}
|
||||
{trend && (
|
||||
<div className={`flex items-center mt-2 text-sm ${trendStyles[trend.direction]}`}>
|
||||
{trend.direction === 'up' && <TrendingUp className="w-4 h-4 mr-1" />}
|
||||
{trend.direction === 'down' && <TrendingDown className="w-4 h-4 mr-1" />}
|
||||
{trend.direction === 'neutral' && <Minus className="w-4 h-4 mr-1" />}
|
||||
<span>{trend.value > 0 ? '+' : ''}{trend.value}%</span>
|
||||
<div
|
||||
className={`flex items-center mt-2 text-sm ${trendStyles[trend.direction]}`}
|
||||
>
|
||||
{trend.direction === 'up' && (
|
||||
<TrendingUp className="w-4 h-4 mr-1" />
|
||||
)}
|
||||
{trend.direction === 'down' && (
|
||||
<TrendingDown className="w-4 h-4 mr-1" />
|
||||
)}
|
||||
{trend.direction === 'neutral' && (
|
||||
<Minus className="w-4 h-4 mr-1" />
|
||||
)}
|
||||
<span>
|
||||
{trend.value > 0 ? '+' : ''}
|
||||
{trend.value}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={`p-3 rounded-lg bg-gray-100 dark:bg-gray-800 ${iconStyles[variant]}`}>
|
||||
<div
|
||||
className={`p-3 rounded-lg bg-gray-100 dark:bg-gray-800 ${iconStyles[variant]}`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +162,11 @@ export function FeedbackStatsCards({ stats, loading }: FeedbackStatsProps) {
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" />
|
||||
</svg>
|
||||
),
|
||||
variant: (stats && stats.satisfactionRate >= 80 ? 'success' : stats && stats.satisfactionRate >= 50 ? 'warning' : 'danger') as 'default' | 'success' | 'warning' | 'danger',
|
||||
variant: (stats && stats.satisfactionRate >= 80
|
||||
? 'success'
|
||||
: stats && stats.satisfactionRate >= 50
|
||||
? 'warning'
|
||||
: 'danger') as 'default' | 'success' | 'warning' | 'danger',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ThumbsUp, ThumbsDown, ChevronRight, ChevronDown, ExternalLink } from 'lucide-react';
|
||||
import {
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
ChevronRight,
|
||||
ChevronDown,
|
||||
ExternalLink,
|
||||
} from 'lucide-react';
|
||||
import { FeedbackRecord } from '../types/monitoring';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { LoadingSpinner } from '@/components/ui/loading-spinner';
|
||||
@@ -13,7 +19,11 @@ interface FeedbackListProps {
|
||||
onViewMessage?: (messageId: string) => void;
|
||||
}
|
||||
|
||||
export function FeedbackList({ feedback, loading, onViewMessage }: FeedbackListProps) {
|
||||
export function FeedbackList({
|
||||
feedback,
|
||||
loading,
|
||||
onViewMessage,
|
||||
}: FeedbackListProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expandedId, setExpandedId] = React.useState<string | null>(null);
|
||||
|
||||
@@ -80,9 +90,13 @@ export function FeedbackList({ feedback, loading, onViewMessage }: FeedbackListP
|
||||
{/* Expand Icon */}
|
||||
<div className="mr-3 mt-0.5">
|
||||
{expandedId === item.id ? (
|
||||
<ChevronDown className={`w-5 h-5 ${item.feedbackType === 'like' ? 'text-green-500' : 'text-red-500'}`} />
|
||||
<ChevronDown
|
||||
className={`w-5 h-5 ${item.feedbackType === 'like' ? 'text-green-500' : 'text-red-500'}`}
|
||||
/>
|
||||
) : (
|
||||
<ChevronRight className={`w-5 h-5 ${item.feedbackType === 'like' ? 'text-green-500' : 'text-red-500'}`} />
|
||||
<ChevronRight
|
||||
className={`w-5 h-5 ${item.feedbackType === 'like' ? 'text-green-500' : 'text-red-500'}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -95,8 +109,12 @@ export function FeedbackList({ feedback, loading, onViewMessage }: FeedbackListP
|
||||
) : (
|
||||
<ThumbsDown className="w-5 h-5 text-red-500" />
|
||||
)}
|
||||
<span className={`text-sm font-medium ${item.feedbackType === 'like' ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}>
|
||||
{item.feedbackType === 'like' ? t('monitoring.feedback.like') : t('monitoring.feedback.dislike')}
|
||||
<span
|
||||
className={`text-sm font-medium ${item.feedbackType === 'like' ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}
|
||||
>
|
||||
{item.feedbackType === 'like'
|
||||
? t('monitoring.feedback.like')
|
||||
: t('monitoring.feedback.dislike')}
|
||||
</span>
|
||||
{item.botName && (
|
||||
<>
|
||||
@@ -119,18 +137,19 @@ export function FeedbackList({ feedback, loading, onViewMessage }: FeedbackListP
|
||||
</p>
|
||||
)}
|
||||
|
||||
{item.inaccurateReasons && item.inaccurateReasons.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-2">
|
||||
{item.inaccurateReasons.map((reason, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="text-xs px-2 py-0.5 rounded bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400"
|
||||
>
|
||||
{reason}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{item.inaccurateReasons &&
|
||||
item.inaccurateReasons.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-2">
|
||||
{item.inaccurateReasons.map((reason, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="text-xs px-2 py-0.5 rounded bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400"
|
||||
>
|
||||
{reason}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -145,9 +164,13 @@ export function FeedbackList({ feedback, loading, onViewMessage }: FeedbackListP
|
||||
|
||||
{/* Expanded Details */}
|
||||
{expandedId === item.id && (
|
||||
<div className={`border-t p-5 bg-white dark:bg-gray-900 ${
|
||||
item.feedbackType === 'like' ? 'border-green-200 dark:border-green-900' : 'border-red-200 dark:border-red-900'
|
||||
}`}>
|
||||
<div
|
||||
className={`border-t p-5 bg-white dark:bg-gray-900 ${
|
||||
item.feedbackType === 'like'
|
||||
? 'border-green-200 dark:border-green-900'
|
||||
: 'border-red-200 dark:border-red-900'
|
||||
}`}
|
||||
>
|
||||
<div className="space-y-4 pl-8 border-l-2 border-gray-200 dark:border-gray-700 ml-4">
|
||||
{/* Context Info */}
|
||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
|
||||
|
||||
@@ -54,10 +54,7 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
const paramsStr = useMemo(
|
||||
() => JSON.stringify(params),
|
||||
[params],
|
||||
);
|
||||
const paramsStr = useMemo(() => JSON.stringify(params), [params]);
|
||||
|
||||
const fetchStats = useCallback(async () => {
|
||||
try {
|
||||
@@ -66,7 +63,9 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
params.botIds.forEach((id) => queryParams.append('botId', id));
|
||||
}
|
||||
if (params.pipelineIds) {
|
||||
params.pipelineIds.forEach((id) => queryParams.append('pipelineId', id));
|
||||
params.pipelineIds.forEach((id) =>
|
||||
queryParams.append('pipelineId', id),
|
||||
);
|
||||
}
|
||||
if (params.startTime) {
|
||||
queryParams.append('startTime', params.startTime);
|
||||
@@ -91,7 +90,8 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
totalFeedback: bot.total,
|
||||
totalLikes: bot.likes,
|
||||
totalDislikes: bot.dislikes,
|
||||
satisfactionRate: bot.total > 0 ? Math.round((bot.likes / bot.total) * 100) : 0,
|
||||
satisfactionRate:
|
||||
bot.total > 0 ? Math.round((bot.likes / bot.total) * 100) : 0,
|
||||
})),
|
||||
});
|
||||
}
|
||||
@@ -110,7 +110,9 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
params.botIds.forEach((id) => queryParams.append('botId', id));
|
||||
}
|
||||
if (params.pipelineIds) {
|
||||
params.pipelineIds.forEach((id) => queryParams.append('pipelineId', id));
|
||||
params.pipelineIds.forEach((id) =>
|
||||
queryParams.append('pipelineId', id),
|
||||
);
|
||||
}
|
||||
if (params.startTime) {
|
||||
queryParams.append('startTime', params.startTime);
|
||||
@@ -119,7 +121,10 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
queryParams.append('endTime', params.endTime);
|
||||
}
|
||||
if (params.feedbackType) {
|
||||
queryParams.append('feedbackType', params.feedbackType === 'like' ? '1' : '2');
|
||||
queryParams.append(
|
||||
'feedbackType',
|
||||
params.feedbackType === 'like' ? '1' : '2',
|
||||
);
|
||||
}
|
||||
if (params.limit) {
|
||||
queryParams.append('limit', params.limit.toString());
|
||||
@@ -134,25 +139,27 @@ export function useFeedbackData(params: UseFeedbackDataParams = {}) {
|
||||
}>(`/api/v1/monitoring/feedback?${queryParams.toString()}`);
|
||||
|
||||
if (result) {
|
||||
const transformedFeedback: FeedbackRecord[] = result.feedback.map((item) => ({
|
||||
id: item.id,
|
||||
timestamp: new Date(item.timestamp),
|
||||
feedbackId: item.feedback_id,
|
||||
feedbackType: item.feedback_type === 1 ? 'like' : 'dislike',
|
||||
feedbackContent: item.feedback_content,
|
||||
inaccurateReasons: item.inaccurate_reasons
|
||||
? JSON.parse(item.inaccurate_reasons)
|
||||
: undefined,
|
||||
botId: item.bot_id,
|
||||
botName: item.bot_name,
|
||||
pipelineId: item.pipeline_id,
|
||||
pipelineName: item.pipeline_name,
|
||||
sessionId: item.session_id,
|
||||
messageId: item.message_id,
|
||||
streamId: item.stream_id,
|
||||
userId: item.user_id,
|
||||
platform: item.platform,
|
||||
}));
|
||||
const transformedFeedback: FeedbackRecord[] = result.feedback.map(
|
||||
(item) => ({
|
||||
id: item.id,
|
||||
timestamp: new Date(item.timestamp),
|
||||
feedbackId: item.feedback_id,
|
||||
feedbackType: item.feedback_type === 1 ? 'like' : 'dislike',
|
||||
feedbackContent: item.feedback_content,
|
||||
inaccurateReasons: item.inaccurate_reasons
|
||||
? JSON.parse(item.inaccurate_reasons)
|
||||
: undefined,
|
||||
botId: item.bot_id,
|
||||
botName: item.bot_name,
|
||||
pipelineId: item.pipeline_id,
|
||||
pipelineName: item.pipeline_name,
|
||||
sessionId: item.session_id,
|
||||
messageId: item.message_id,
|
||||
streamId: item.stream_id,
|
||||
userId: item.user_id,
|
||||
platform: item.platform,
|
||||
}),
|
||||
);
|
||||
|
||||
setFeedback(transformedFeedback);
|
||||
setTotal(result.total);
|
||||
|
||||
@@ -5,8 +5,6 @@ import {
|
||||
ModelCall,
|
||||
LLMCall,
|
||||
EmbeddingCall,
|
||||
FeedbackRecord,
|
||||
FeedbackStats,
|
||||
} from '../types/monitoring';
|
||||
import { backendClient } from '@/app/infra/http';
|
||||
import { parseUTCTimestamp } from '../utils/dateUtils';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { Suspense, useState, useMemo, useCallback } from 'react';
|
||||
import React, { Suspense, useState, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -114,12 +114,16 @@ function MonitoringPageContent() {
|
||||
const {
|
||||
feedback: feedbackList,
|
||||
stats: feedbackStats,
|
||||
total: feedbackTotal,
|
||||
loading: feedbackLoading,
|
||||
refetch: refetchFeedback,
|
||||
} = useFeedbackData({
|
||||
botIds: filterState.selectedBots.length > 0 ? filterState.selectedBots : undefined,
|
||||
pipelineIds: filterState.selectedPipelines.length > 0 ? filterState.selectedPipelines : undefined,
|
||||
botIds:
|
||||
filterState.selectedBots.length > 0
|
||||
? filterState.selectedBots
|
||||
: undefined,
|
||||
pipelineIds:
|
||||
filterState.selectedPipelines.length > 0
|
||||
? filterState.selectedPipelines
|
||||
: undefined,
|
||||
startTime: feedbackTimeRange.startTime,
|
||||
endTime: feedbackTimeRange.endTime,
|
||||
limit: 50,
|
||||
|
||||
23
web/src/app/infra/entities/adapter-docs.ts
Normal file
23
web/src/app/infra/entities/adapter-docs.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Resolves the documentation URL for a given adapter from its
|
||||
* spec.help_links map, selecting the best match for the current locale
|
||||
* with a fallback to English.
|
||||
*/
|
||||
export function getAdapterDocUrl(
|
||||
helpLinks: Record<string, string> | undefined,
|
||||
locale: string,
|
||||
): string | null {
|
||||
if (!helpLinks) return null;
|
||||
|
||||
// Map locale to simplified language key
|
||||
let lang: string;
|
||||
if (locale.startsWith('zh')) {
|
||||
lang = 'zh';
|
||||
} else if (locale.startsWith('ja')) {
|
||||
lang = 'ja';
|
||||
} else {
|
||||
lang = 'en';
|
||||
}
|
||||
|
||||
return helpLinks[lang] ?? helpLinks['en'] ?? null;
|
||||
}
|
||||
@@ -118,6 +118,7 @@ export interface Adapter {
|
||||
icon?: string;
|
||||
spec: {
|
||||
categories?: string[];
|
||||
help_links?: Record<string, string>;
|
||||
config: IDynamicFormItemSchema[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
PartyPopper,
|
||||
Loader2,
|
||||
X,
|
||||
ExternalLink,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
@@ -45,6 +46,8 @@ import {
|
||||
groupByCategory,
|
||||
getCategoryLabel,
|
||||
} from '@/app/infra/entities/adapter-categories';
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@@ -798,6 +801,24 @@ function StepPlatform({
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
||||
{extractI18nObject(adapter.description)}
|
||||
</p>
|
||||
{(() => {
|
||||
const docUrl = getAdapterDocUrl(
|
||||
adapter.spec.help_links,
|
||||
i18n.language,
|
||||
);
|
||||
return docUrl ? (
|
||||
<a
|
||||
href={docUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-2 inline-flex items-center text-xs text-primary hover:underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ExternalLink className="mr-1 h-3 w-3" />
|
||||
{t('bots.viewAdapterDocs')}
|
||||
</a>
|
||||
) : null;
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
@@ -867,11 +888,34 @@ function StepBotConfig({
|
||||
{adapterConfigItems.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2">
|
||||
<CardTitle className="text-base">
|
||||
{t('wizard.config.platformConfig', {
|
||||
platform: adapterLabel,
|
||||
})}
|
||||
</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<CardTitle className="text-base">
|
||||
{t('wizard.config.platformConfig', {
|
||||
platform: adapterLabel,
|
||||
})}
|
||||
</CardTitle>
|
||||
{selectedAdapterName &&
|
||||
(() => {
|
||||
const selectedAdapter = adapters.find(
|
||||
(a) => a.name === selectedAdapterName,
|
||||
);
|
||||
const docUrl = getAdapterDocUrl(
|
||||
selectedAdapter?.spec.help_links,
|
||||
i18n.language,
|
||||
);
|
||||
return docUrl ? (
|
||||
<a
|
||||
href={docUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center text-xs text-primary hover:underline"
|
||||
>
|
||||
<ExternalLink className="mr-1 h-3 w-3" />
|
||||
{t('bots.viewAdapterDocs')}
|
||||
</a>
|
||||
) : null;
|
||||
})()}
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={onSaveBot}
|
||||
|
||||
@@ -65,8 +65,7 @@ const enUS = {
|
||||
privacyPolicy: 'Privacy Policy',
|
||||
and: 'and',
|
||||
dataCollectionPolicy: 'Data Collection Policy',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/en/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/en/docs/data-policy',
|
||||
loading: 'Loading...',
|
||||
fieldRequired: 'This field is required',
|
||||
or: 'or',
|
||||
@@ -289,6 +288,7 @@ const enUS = {
|
||||
platformAdapter: 'Platform/Adapter Selection',
|
||||
selectAdapter: 'Select Adapter',
|
||||
adapterConfig: 'Adapter Configuration',
|
||||
viewAdapterDocs: 'View Docs',
|
||||
bindPipeline: 'Bind Pipeline',
|
||||
selectPipeline: 'Select Pipeline',
|
||||
selectBot: 'Select Bot',
|
||||
|
||||
@@ -67,8 +67,7 @@ const esES = {
|
||||
privacyPolicy: 'Política de privacidad',
|
||||
and: 'y',
|
||||
dataCollectionPolicy: 'Política de recopilación de datos',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/en/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/en/docs/data-policy',
|
||||
loading: 'Cargando...',
|
||||
fieldRequired: 'Este campo es obligatorio',
|
||||
or: 'o',
|
||||
@@ -298,6 +297,7 @@ const esES = {
|
||||
platformAdapter: 'Selección de plataforma/adaptador',
|
||||
selectAdapter: 'Seleccionar adaptador',
|
||||
adapterConfig: 'Configuración del adaptador',
|
||||
viewAdapterDocs: 'Ver documentación',
|
||||
bindPipeline: 'Vincular Pipeline',
|
||||
selectPipeline: 'Seleccionar Pipeline',
|
||||
selectBot: 'Seleccionar Bot',
|
||||
@@ -1062,6 +1062,7 @@ const esES = {
|
||||
embeddingCalls: 'Llamadas Embedding',
|
||||
modelCalls: 'Llamadas a modelos',
|
||||
sessions: 'Análisis de sesiones',
|
||||
feedback: 'Comentarios de usuarios',
|
||||
errors: 'Registros de errores',
|
||||
},
|
||||
messageList: {
|
||||
@@ -1141,6 +1142,26 @@ const esES = {
|
||||
noErrors: 'No se encontraron errores',
|
||||
stackTrace: 'Traza de pila',
|
||||
},
|
||||
feedback: {
|
||||
title: 'Comentarios de usuarios',
|
||||
totalFeedback: 'Total de comentarios',
|
||||
totalLikes: 'Me gusta',
|
||||
totalDislikes: 'No me gusta',
|
||||
satisfactionRate: 'Tasa de satisfacción',
|
||||
like: 'Me gusta',
|
||||
dislike: 'No me gusta',
|
||||
noFeedback: 'Aún no hay comentarios',
|
||||
noFeedbackDescription: 'Los comentarios de los usuarios aparecerán aquí',
|
||||
feedbackList: 'Lista de comentarios',
|
||||
feedbackContent: 'Contenido del comentario',
|
||||
contextInfo: 'Información de contexto',
|
||||
userId: 'ID de usuario',
|
||||
messageId: 'ID de mensaje',
|
||||
streamId: 'ID de flujo',
|
||||
inaccurateReasons: 'Razones de inexactitud',
|
||||
platform: 'Plataforma',
|
||||
exportFeedback: 'Exportar comentarios',
|
||||
},
|
||||
queries: {
|
||||
title: 'Consultas',
|
||||
},
|
||||
|
||||
@@ -66,8 +66,7 @@
|
||||
privacyPolicy: 'プライバシーポリシー',
|
||||
and: 'および',
|
||||
dataCollectionPolicy: 'データ収集ポリシー',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/ja/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/ja/docs/data-policy',
|
||||
loading: '読み込み中...',
|
||||
fieldRequired: 'この項目は必須です',
|
||||
or: 'または',
|
||||
@@ -294,6 +293,7 @@
|
||||
platformAdapter: 'プラットフォーム/アダプター選択',
|
||||
selectAdapter: 'アダプターを選択',
|
||||
adapterConfig: 'アダプター設定',
|
||||
viewAdapterDocs: 'ドキュメントを見る',
|
||||
bindPipeline: 'パイプラインを紐付け',
|
||||
selectPipeline: 'パイプラインを選択',
|
||||
selectBot: 'ボットを選択してください',
|
||||
@@ -1024,6 +1024,7 @@
|
||||
embeddingCalls: 'Embedding呼び出し',
|
||||
modelCalls: 'モデル呼び出し',
|
||||
sessions: 'セッション分析',
|
||||
feedback: 'ユーザーフィードバック',
|
||||
errors: 'エラーログ',
|
||||
},
|
||||
messageList: {
|
||||
@@ -1093,6 +1094,26 @@
|
||||
stackTrace: 'スタックトレース',
|
||||
title: 'エラー',
|
||||
},
|
||||
feedback: {
|
||||
title: 'ユーザーフィードバック',
|
||||
totalFeedback: 'フィードバック合計',
|
||||
totalLikes: 'いいね数',
|
||||
totalDislikes: 'よくないね数',
|
||||
satisfactionRate: '満足度',
|
||||
like: 'いいね',
|
||||
dislike: 'よくないね',
|
||||
noFeedback: 'フィードバックはまだありません',
|
||||
noFeedbackDescription: 'ユーザーフィードバックがここに表示されます',
|
||||
feedbackList: 'フィードバック一覧',
|
||||
feedbackContent: 'フィードバック内容',
|
||||
contextInfo: 'コンテキスト情報',
|
||||
userId: 'ユーザーID',
|
||||
messageId: 'メッセージID',
|
||||
streamId: 'ストリームID',
|
||||
inaccurateReasons: '不正確な理由',
|
||||
platform: 'プラットフォーム',
|
||||
exportFeedback: 'フィードバックをエクスポート',
|
||||
},
|
||||
messageDetails: {
|
||||
noData: 'このクエリにはLLM呼び出しやエラーがありません',
|
||||
},
|
||||
|
||||
@@ -65,8 +65,7 @@ const thTH = {
|
||||
privacyPolicy: 'นโยบายความเป็นส่วนตัว',
|
||||
and: 'และ',
|
||||
dataCollectionPolicy: 'นโยบายการเก็บรวบรวมข้อมูล',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/en/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/en/docs/data-policy',
|
||||
loading: 'กำลังโหลด...',
|
||||
fieldRequired: 'ช่องนี้จำเป็นต้องกรอก',
|
||||
or: 'หรือ',
|
||||
@@ -284,6 +283,7 @@ const thTH = {
|
||||
platformAdapter: 'การเลือกแพลตฟอร์ม/อะแดปเตอร์',
|
||||
selectAdapter: 'เลือกอะแดปเตอร์',
|
||||
adapterConfig: 'การกำหนดค่าอะแดปเตอร์',
|
||||
viewAdapterDocs: 'ดูเอกสาร',
|
||||
bindPipeline: 'ผูก Pipeline',
|
||||
selectPipeline: 'เลือก Pipeline',
|
||||
selectBot: 'เลือก Bot',
|
||||
@@ -1011,6 +1011,7 @@ const thTH = {
|
||||
embeddingCalls: 'การเรียก Embedding',
|
||||
modelCalls: 'การเรียกโมเดล',
|
||||
sessions: 'การวิเคราะห์เซสชัน',
|
||||
feedback: 'ความคิดเห็นผู้ใช้',
|
||||
errors: 'บันทึกข้อผิดพลาด',
|
||||
},
|
||||
messageList: {
|
||||
@@ -1089,6 +1090,26 @@ const thTH = {
|
||||
noErrors: 'ไม่พบข้อผิดพลาด',
|
||||
stackTrace: 'Stack Trace',
|
||||
},
|
||||
feedback: {
|
||||
title: 'ความคิดเห็นผู้ใช้',
|
||||
totalFeedback: 'ความคิดเห็นทั้งหมด',
|
||||
totalLikes: 'ถูกใจ',
|
||||
totalDislikes: 'ไม่ถูกใจ',
|
||||
satisfactionRate: 'อัตราความพึงพอใจ',
|
||||
like: 'ถูกใจ',
|
||||
dislike: 'ไม่ถูกใจ',
|
||||
noFeedback: 'ยังไม่มีความคิดเห็น',
|
||||
noFeedbackDescription: 'ความคิดเห็นของผู้ใช้จะแสดงที่นี่',
|
||||
feedbackList: 'รายการความคิดเห็น',
|
||||
feedbackContent: 'เนื้อหาความคิดเห็น',
|
||||
contextInfo: 'ข้อมูลบริบท',
|
||||
userId: 'ID ผู้ใช้',
|
||||
messageId: 'ID ข้อความ',
|
||||
streamId: 'ID สตรีม',
|
||||
inaccurateReasons: 'เหตุผลที่ไม่ถูกต้อง',
|
||||
platform: 'แพลตฟอร์ม',
|
||||
exportFeedback: 'ส่งออกความคิดเห็น',
|
||||
},
|
||||
queries: {
|
||||
title: 'คำค้นหา',
|
||||
},
|
||||
|
||||
@@ -65,8 +65,7 @@ const viVN = {
|
||||
privacyPolicy: 'Chính sách bảo mật',
|
||||
and: 'và',
|
||||
dataCollectionPolicy: 'Chính sách thu thập dữ liệu',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/en/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/en/docs/data-policy',
|
||||
loading: 'Đang tải...',
|
||||
fieldRequired: 'Trường này là bắt buộc',
|
||||
or: 'hoặc',
|
||||
@@ -293,6 +292,7 @@ const viVN = {
|
||||
platformAdapter: 'Nền tảng/Lựa chọn Adapter',
|
||||
selectAdapter: 'Chọn Adapter',
|
||||
adapterConfig: 'Cấu hình Adapter',
|
||||
viewAdapterDocs: 'Xem tài liệu',
|
||||
bindPipeline: 'Liên kết Pipeline',
|
||||
selectPipeline: 'Chọn Pipeline',
|
||||
selectBot: 'Chọn Bot',
|
||||
@@ -1032,6 +1032,7 @@ const viVN = {
|
||||
embeddingCalls: 'Cuộc gọi Embedding',
|
||||
modelCalls: 'Cuộc gọi mô hình',
|
||||
sessions: 'Phân tích phiên',
|
||||
feedback: 'Phản hồi người dùng',
|
||||
errors: 'Nhật ký lỗi',
|
||||
},
|
||||
messageList: {
|
||||
@@ -1110,6 +1111,26 @@ const viVN = {
|
||||
noErrors: 'Không tìm thấy lỗi',
|
||||
stackTrace: 'Stack Trace',
|
||||
},
|
||||
feedback: {
|
||||
title: 'Phản hồi người dùng',
|
||||
totalFeedback: 'Tổng phản hồi',
|
||||
totalLikes: 'Lượt thích',
|
||||
totalDislikes: 'Lượt không thích',
|
||||
satisfactionRate: 'Tỷ lệ hài lòng',
|
||||
like: 'Thích',
|
||||
dislike: 'Không thích',
|
||||
noFeedback: 'Chưa có phản hồi',
|
||||
noFeedbackDescription: 'Phản hồi của người dùng sẽ hiển thị tại đây',
|
||||
feedbackList: 'Danh sách phản hồi',
|
||||
feedbackContent: 'Nội dung phản hồi',
|
||||
contextInfo: 'Thông tin ngữ cảnh',
|
||||
userId: 'ID người dùng',
|
||||
messageId: 'ID tin nhắn',
|
||||
streamId: 'ID luồng',
|
||||
inaccurateReasons: 'Lý do không chính xác',
|
||||
platform: 'Nền tảng',
|
||||
exportFeedback: 'Xuất phản hồi',
|
||||
},
|
||||
queries: {
|
||||
title: 'Truy vấn',
|
||||
},
|
||||
|
||||
@@ -64,8 +64,7 @@ const zhHans = {
|
||||
privacyPolicy: '隐私政策',
|
||||
and: '和',
|
||||
dataCollectionPolicy: '数据收集政策',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/zh/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/zh/docs/data-policy',
|
||||
loading: '加载中...',
|
||||
fieldRequired: '此字段为必填项',
|
||||
or: '或',
|
||||
@@ -277,6 +276,7 @@ const zhHans = {
|
||||
platformAdapter: '平台/适配器选择',
|
||||
selectAdapter: '选择适配器',
|
||||
adapterConfig: '适配器配置',
|
||||
viewAdapterDocs: '查看文档',
|
||||
bindPipeline: '绑定流水线',
|
||||
selectPipeline: '选择流水线',
|
||||
selectBot: '请选择机器人',
|
||||
|
||||
@@ -64,8 +64,7 @@ const zhHant = {
|
||||
privacyPolicy: '隱私政策',
|
||||
and: '和',
|
||||
dataCollectionPolicy: '數據收集政策',
|
||||
dataCollectionPolicyUrl:
|
||||
'https://docs.langbot.app/zh/insight/data-collection-policy',
|
||||
dataCollectionPolicyUrl: 'https://link.langbot.app/zh/docs/data-policy',
|
||||
loading: '載入中...',
|
||||
fieldRequired: '此欄位為必填',
|
||||
or: '或',
|
||||
@@ -276,6 +275,7 @@ const zhHant = {
|
||||
platformAdapter: '平台/適配器選擇',
|
||||
selectAdapter: '選擇適配器',
|
||||
adapterConfig: '適配器設定',
|
||||
viewAdapterDocs: '查看文檔',
|
||||
bindPipeline: '綁定流程線',
|
||||
selectPipeline: '選擇流程線',
|
||||
selectBot: '請選擇機器人',
|
||||
@@ -963,6 +963,7 @@ const zhHant = {
|
||||
embeddingCalls: 'Embedding調用',
|
||||
modelCalls: '模型調用',
|
||||
sessions: '會話分析',
|
||||
feedback: '使用者反饋',
|
||||
errors: '錯誤日誌',
|
||||
},
|
||||
messageList: {
|
||||
@@ -1032,6 +1033,26 @@ const zhHant = {
|
||||
stackTrace: '堆疊追蹤',
|
||||
title: '錯誤',
|
||||
},
|
||||
feedback: {
|
||||
title: '使用者反饋',
|
||||
totalFeedback: '總反饋數',
|
||||
totalLikes: '按讚數',
|
||||
totalDislikes: '按倒讚數',
|
||||
satisfactionRate: '滿意度',
|
||||
like: '按讚',
|
||||
dislike: '按倒讚',
|
||||
noFeedback: '暫無反饋',
|
||||
noFeedbackDescription: '使用者反饋將在此顯示',
|
||||
feedbackList: '反饋列表',
|
||||
feedbackContent: '反饋內容',
|
||||
contextInfo: '上下文資訊',
|
||||
userId: '使用者ID',
|
||||
messageId: '訊息ID',
|
||||
streamId: '串流ID',
|
||||
inaccurateReasons: '不準確原因',
|
||||
platform: '平台',
|
||||
exportFeedback: '匯出反饋',
|
||||
},
|
||||
messageDetails: {
|
||||
noData: '此查詢沒有LLM調用或錯誤記錄',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user