mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-04 03:25:30 +08:00
Compare commits
13 Commits
a0d630365c
...
v0.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
| e6cb74b27f | |||
| 00f54c207e | |||
| d0dc56166c | |||
| e15e39f183 | |||
| 33f3e05b75 | |||
| b8bfbac2e5 | |||
| d5729e598f | |||
| f2c5ad170d | |||
| 0aa3c4cdae | |||
| f123587538 | |||
| a41a646909 | |||
| 787e0c6786 | |||
| 05ee1be1e9 |
@ -22,7 +22,7 @@
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Latest%20Release" alt="Latest Release">
|
||||
@ -190,7 +190,7 @@ releases! 🌟
|
||||
> All Docker images are built for x86 platforms. We don't currently offer Docker images for ARM64.
|
||||
> If you are on an ARM64 platform, follow [this guide](https://ragflow.io/docs/dev/build_docker_image) to build a Docker image compatible with your system.
|
||||
|
||||
> The command below downloads the `v0.20.1-slim` edition of the RAGFlow Docker image. See the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.1-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1` for the full edition `v0.20.1`.
|
||||
> The command below downloads the `v0.20.2-slim` edition of the RAGFlow Docker image. See the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.2-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2` for the full edition `v0.20.2`.
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -203,8 +203,8 @@ releases! 🌟
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
|-------------------|-----------------|-----------------------|--------------------------|
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Lencana Daring" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Rilis%20Terbaru" alt="Rilis Terbaru">
|
||||
@ -181,7 +181,7 @@ Coba demo kami di [https://demo.ragflow.io](https://demo.ragflow.io).
|
||||
> Semua gambar Docker dibangun untuk platform x86. Saat ini, kami tidak menawarkan gambar Docker untuk ARM64.
|
||||
> Jika Anda menggunakan platform ARM64, [silakan gunakan panduan ini untuk membangun gambar Docker yang kompatibel dengan sistem Anda](https://ragflow.io/docs/dev/build_docker_image).
|
||||
|
||||
> Perintah di bawah ini mengunduh edisi v0.20.1-slim dari gambar Docker RAGFlow. Silakan merujuk ke tabel berikut untuk deskripsi berbagai edisi RAGFlow. Untuk mengunduh edisi RAGFlow yang berbeda dari v0.20.1-slim, perbarui variabel RAGFLOW_IMAGE di docker/.env sebelum menggunakan docker compose untuk memulai server. Misalnya, atur RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1 untuk edisi lengkap v0.20.1.
|
||||
> Perintah di bawah ini mengunduh edisi v0.20.2-slim dari gambar Docker RAGFlow. Silakan merujuk ke tabel berikut untuk deskripsi berbagai edisi RAGFlow. Untuk mengunduh edisi RAGFlow yang berbeda dari v0.20.2-slim, perbarui variabel RAGFLOW_IMAGE di docker/.env sebelum menggunakan docker compose untuk memulai server. Misalnya, atur RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2 untuk edisi lengkap v0.20.2.
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -194,8 +194,8 @@ $ docker compose -f docker-compose.yml up -d
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
| ----------------- | --------------- | --------------------- | ------------------------ |
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Latest%20Release" alt="Latest Release">
|
||||
@ -160,7 +160,7 @@
|
||||
> 現在、公式に提供されているすべての Docker イメージは x86 アーキテクチャ向けにビルドされており、ARM64 用の Docker イメージは提供されていません。
|
||||
> ARM64 アーキテクチャのオペレーティングシステムを使用している場合は、[このドキュメント](https://ragflow.io/docs/dev/build_docker_image)を参照して Docker イメージを自分でビルドしてください。
|
||||
|
||||
> 以下のコマンドは、RAGFlow Docker イメージの v0.20.1-slim エディションをダウンロードします。異なる RAGFlow エディションの説明については、以下の表を参照してください。v0.20.1-slim とは異なるエディションをダウンロードするには、docker/.env ファイルの RAGFLOW_IMAGE 変数を適宜更新し、docker compose を使用してサーバーを起動してください。例えば、完全版 v0.20.1 をダウンロードするには、RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1 と設定します。
|
||||
> 以下のコマンドは、RAGFlow Docker イメージの v0.20.2-slim エディションをダウンロードします。異なる RAGFlow エディションの説明については、以下の表を参照してください。v0.20.2-slim とは異なるエディションをダウンロードするには、docker/.env ファイルの RAGFLOW_IMAGE 変数を適宜更新し、docker compose を使用してサーバーを起動してください。例えば、完全版 v0.20.2 をダウンロードするには、RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2 と設定します。
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -173,8 +173,8 @@
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
| ----------------- | --------------- | --------------------- | ------------------------ |
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Latest%20Release" alt="Latest Release">
|
||||
@ -160,7 +160,7 @@
|
||||
> 모든 Docker 이미지는 x86 플랫폼을 위해 빌드되었습니다. 우리는 현재 ARM64 플랫폼을 위한 Docker 이미지를 제공하지 않습니다.
|
||||
> ARM64 플랫폼을 사용 중이라면, [시스템과 호환되는 Docker 이미지를 빌드하려면 이 가이드를 사용해 주세요](https://ragflow.io/docs/dev/build_docker_image).
|
||||
|
||||
> 아래 명령어는 RAGFlow Docker 이미지의 v0.20.1-slim 버전을 다운로드합니다. 다양한 RAGFlow 버전에 대한 설명은 다음 표를 참조하십시오. v0.20.1-slim과 다른 RAGFlow 버전을 다운로드하려면, docker/.env 파일에서 RAGFLOW_IMAGE 변수를 적절히 업데이트한 후 docker compose를 사용하여 서버를 시작하십시오. 예를 들어, 전체 버전인 v0.20.1을 다운로드하려면 RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1로 설정합니다.
|
||||
> 아래 명령어는 RAGFlow Docker 이미지의 v0.20.2-slim 버전을 다운로드합니다. 다양한 RAGFlow 버전에 대한 설명은 다음 표를 참조하십시오. v0.20.2-slim과 다른 RAGFlow 버전을 다운로드하려면, docker/.env 파일에서 RAGFLOW_IMAGE 변수를 적절히 업데이트한 후 docker compose를 사용하여 서버를 시작하십시오. 예를 들어, 전체 버전인 v0.20.2을 다운로드하려면 RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2로 설정합니다.
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -173,8 +173,8 @@
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
| ----------------- | --------------- | --------------------- | ------------------------ |
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Badge Estático" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Última%20Relese" alt="Última Versão">
|
||||
@ -180,7 +180,7 @@ Experimente nossa demo em [https://demo.ragflow.io](https://demo.ragflow.io).
|
||||
> Todas as imagens Docker são construídas para plataformas x86. Atualmente, não oferecemos imagens Docker para ARM64.
|
||||
> Se você estiver usando uma plataforma ARM64, por favor, utilize [este guia](https://ragflow.io/docs/dev/build_docker_image) para construir uma imagem Docker compatível com o seu sistema.
|
||||
|
||||
> O comando abaixo baixa a edição `v0.20.1-slim` da imagem Docker do RAGFlow. Consulte a tabela a seguir para descrições de diferentes edições do RAGFlow. Para baixar uma edição do RAGFlow diferente da `v0.20.1-slim`, atualize a variável `RAGFLOW_IMAGE` conforme necessário no **docker/.env** antes de usar `docker compose` para iniciar o servidor. Por exemplo: defina `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1` para a edição completa `v0.20.1`.
|
||||
> O comando abaixo baixa a edição `v0.20.2-slim` da imagem Docker do RAGFlow. Consulte a tabela a seguir para descrições de diferentes edições do RAGFlow. Para baixar uma edição do RAGFlow diferente da `v0.20.2-slim`, atualize a variável `RAGFLOW_IMAGE` conforme necessário no **docker/.env** antes de usar `docker compose` para iniciar o servidor. Por exemplo: defina `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2` para a edição completa `v0.20.2`.
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -193,8 +193,8 @@ Experimente nossa demo em [https://demo.ragflow.io](https://demo.ragflow.io).
|
||||
|
||||
| Tag da imagem RAGFlow | Tamanho da imagem (GB) | Possui modelos de incorporação? | Estável? |
|
||||
| --------------------- | ---------------------- | ------------------------------- | ------------------------ |
|
||||
| v0.20.1 | ~9 | :heavy_check_mark: | Lançamento estável |
|
||||
| v0.20.1-slim | ~2 | ❌ | Lançamento estável |
|
||||
| v0.20.2 | ~9 | :heavy_check_mark: | Lançamento estável |
|
||||
| v0.20.2-slim | ~2 | ❌ | Lançamento estável |
|
||||
| nightly | ~9 | :heavy_check_mark: | _Instável_ build noturno |
|
||||
| nightly-slim | ~2 | ❌ | _Instável_ build noturno |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Latest%20Release" alt="Latest Release">
|
||||
@ -183,7 +183,7 @@
|
||||
> 所有 Docker 映像檔都是為 x86 平台建置的。目前,我們不提供 ARM64 平台的 Docker 映像檔。
|
||||
> 如果您使用的是 ARM64 平台,請使用 [這份指南](https://ragflow.io/docs/dev/build_docker_image) 來建置適合您系統的 Docker 映像檔。
|
||||
|
||||
> 執行以下指令會自動下載 RAGFlow slim Docker 映像 `v0.20.1-slim`。請參考下表查看不同 Docker 發行版的說明。如需下載不同於 `v0.20.1-slim` 的 Docker 映像,請在執行 `docker compose` 啟動服務之前先更新 **docker/.env** 檔案內的 `RAGFLOW_IMAGE` 變數。例如,你可以透過設定 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1` 來下載 RAGFlow 鏡像的 `v0.20.1` 完整發行版。
|
||||
> 執行以下指令會自動下載 RAGFlow slim Docker 映像 `v0.20.2-slim`。請參考下表查看不同 Docker 發行版的說明。如需下載不同於 `v0.20.2-slim` 的 Docker 映像,請在執行 `docker compose` 啟動服務之前先更新 **docker/.env** 檔案內的 `RAGFLOW_IMAGE` 變數。例如,你可以透過設定 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2` 來下載 RAGFlow 鏡像的 `v0.20.2` 完整發行版。
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -196,8 +196,8 @@
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
| ----------------- | --------------- | --------------------- | ------------------------ |
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Online-Demo-4e6b99">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.1">
|
||||
<img src="https://img.shields.io/docker/pulls/infiniflow/ragflow?label=Docker%20Pulls&color=0db7ed&logo=docker&logoColor=white&style=flat-square" alt="docker pull infiniflow/ragflow:v0.20.2">
|
||||
</a>
|
||||
<a href="https://github.com/infiniflow/ragflow/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/infiniflow/ragflow?color=blue&label=Latest%20Release" alt="Latest Release">
|
||||
@ -183,7 +183,7 @@
|
||||
> 请注意,目前官方提供的所有 Docker 镜像均基于 x86 架构构建,并不提供基于 ARM64 的 Docker 镜像。
|
||||
> 如果你的操作系统是 ARM64 架构,请参考[这篇文档](https://ragflow.io/docs/dev/build_docker_image)自行构建 Docker 镜像。
|
||||
|
||||
> 运行以下命令会自动下载 RAGFlow slim Docker 镜像 `v0.20.1-slim`。请参考下表查看不同 Docker 发行版的描述。如需下载不同于 `v0.20.1-slim` 的 Docker 镜像,请在运行 `docker compose` 启动服务之前先更新 **docker/.env** 文件内的 `RAGFLOW_IMAGE` 变量。比如,你可以通过设置 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1` 来下载 RAGFlow 镜像的 `v0.20.1` 完整发行版。
|
||||
> 运行以下命令会自动下载 RAGFlow slim Docker 镜像 `v0.20.2-slim`。请参考下表查看不同 Docker 发行版的描述。如需下载不同于 `v0.20.2-slim` 的 Docker 镜像,请在运行 `docker compose` 启动服务之前先更新 **docker/.env** 文件内的 `RAGFLOW_IMAGE` 变量。比如,你可以通过设置 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2` 来下载 RAGFlow 镜像的 `v0.20.2` 完整发行版。
|
||||
|
||||
```bash
|
||||
$ cd ragflow/docker
|
||||
@ -196,8 +196,8 @@
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? |
|
||||
| ----------------- | --------------- | --------------------- | ------------------------ |
|
||||
| v0.20.1 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.1-slim | ≈2 | ❌ | Stable release |
|
||||
| v0.20.2 | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| v0.20.2-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build |
|
||||
| nightly-slim | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
|
||||
|
||||
@ -479,7 +479,7 @@ class ComponentBase(ABC):
|
||||
|
||||
def get_input_elements_from_text(self, txt: str) -> dict[str, dict[str, str]]:
|
||||
res = {}
|
||||
for r in re.finditer(self.variable_ref_patt, txt, flags=re.IGNORECASE):
|
||||
for r in re.finditer(self.variable_ref_patt, txt, flags=re.IGNORECASE|re.DOTALL):
|
||||
exp = r.group(1)
|
||||
cpn_id, var_nm = exp.split("@") if exp.find("@")>0 else ("", exp)
|
||||
res[exp] = {
|
||||
|
||||
@ -54,6 +54,8 @@ class Message(ComponentBase):
|
||||
if k in kwargs:
|
||||
continue
|
||||
v = v["value"]
|
||||
if not v:
|
||||
v = ""
|
||||
ans = ""
|
||||
if isinstance(v, partial):
|
||||
for t in v():
|
||||
@ -94,6 +96,8 @@ class Message(ComponentBase):
|
||||
continue
|
||||
|
||||
v = self._canvas.get_variable_value(exp)
|
||||
if not v:
|
||||
v = ""
|
||||
if isinstance(v, partial):
|
||||
cnt = ""
|
||||
for t in v():
|
||||
|
||||
@ -115,6 +115,12 @@ def getsse(canvas_id):
|
||||
if not objs:
|
||||
return get_data_error_result(message='Authentication error: API key is invalid!"')
|
||||
tenant_id = objs[0].tenant_id
|
||||
if not UserCanvasService.query(user_id=tenant_id, id=canvas_id):
|
||||
return get_json_result(
|
||||
data=False,
|
||||
message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR
|
||||
)
|
||||
e, c = UserCanvasService.get_by_id(canvas_id)
|
||||
if not e or c.user_id != tenant_id:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
|
||||
@ -23,15 +23,18 @@ from flask_login import current_user, login_required
|
||||
|
||||
from api import settings
|
||||
from api.db import LLMType, ParserType
|
||||
from api.db.services.dialog_service import meta_filter
|
||||
from api.db.services.document_service import DocumentService
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from api.db.services.search_service import SearchService
|
||||
from api.db.services.user_service import UserTenantService
|
||||
from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request
|
||||
from rag.app.qa import beAdoc, rmPrefix
|
||||
from rag.app.tag import label_question
|
||||
from rag.nlp import rag_tokenizer, search
|
||||
from rag.prompts import cross_languages, keyword_extraction
|
||||
from rag.prompts.prompts import gen_meta_filter
|
||||
from rag.settings import PAGERANK_FLD
|
||||
from rag.utils import rmSpace
|
||||
|
||||
@ -288,13 +291,26 @@ def retrieval_test():
|
||||
if isinstance(kb_ids, str):
|
||||
kb_ids = [kb_ids]
|
||||
doc_ids = req.get("doc_ids", [])
|
||||
similarity_threshold = float(req.get("similarity_threshold", 0.0))
|
||||
vector_similarity_weight = float(req.get("vector_similarity_weight", 0.3))
|
||||
use_kg = req.get("use_kg", False)
|
||||
top = int(req.get("top_k", 1024))
|
||||
langs = req.get("cross_languages", [])
|
||||
tenant_ids = []
|
||||
|
||||
if req.get("search_id", ""):
|
||||
search_config = SearchService.get_detail(req.get("search_id", "")).get("search_config", {})
|
||||
meta_data_filter = search_config.get("meta_data_filter", {})
|
||||
metas = DocumentService.get_meta_by_kbs(kb_ids)
|
||||
if meta_data_filter.get("method") == "auto":
|
||||
chat_mdl = LLMBundle(current_user.id, LLMType.CHAT, llm_name=search_config.get("chat_id", ""))
|
||||
filters = gen_meta_filter(chat_mdl, metas, question)
|
||||
doc_ids.extend(meta_filter(metas, filters))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
elif meta_data_filter.get("method") == "manual":
|
||||
doc_ids.extend(meta_filter(metas, meta_data_filter["manual"]))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
|
||||
try:
|
||||
tenants = UserTenantService.query(user_id=current_user.id)
|
||||
for kb_id in kb_ids:
|
||||
@ -327,7 +343,9 @@ def retrieval_test():
|
||||
|
||||
labels = label_question(question, [kb])
|
||||
ranks = settings.retrievaler.retrieval(question, embd_mdl, tenant_ids, kb_ids, page, size,
|
||||
similarity_threshold, vector_similarity_weight, top,
|
||||
float(req.get("similarity_threshold", 0.0)),
|
||||
float(req.get("vector_similarity_weight", 0.3)),
|
||||
top,
|
||||
doc_ids, rerank_mdl=rerank_mdl, highlight=req.get("highlight"),
|
||||
rank_feature=labels
|
||||
)
|
||||
|
||||
@ -17,24 +17,18 @@ import json
|
||||
import re
|
||||
import traceback
|
||||
from copy import deepcopy
|
||||
|
||||
import trio
|
||||
from flask import Response, request
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
from api import settings
|
||||
from api.db import LLMType
|
||||
from api.db.db_models import APIToken
|
||||
from api.db.services.conversation_service import ConversationService, structure_answer
|
||||
from api.db.services.dialog_service import DialogService, ask, chat
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.dialog_service import DialogService, ask, chat, gen_mindmap
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from api.db.services.search_service import SearchService
|
||||
from api.db.services.tenant_llm_service import TenantLLMService
|
||||
from api.db.services.user_service import TenantService, UserTenantService
|
||||
from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request
|
||||
from graphrag.general.mind_map_extractor import MindMapExtractor
|
||||
from rag.app.tag import label_question
|
||||
from rag.prompts.prompt_template import load_prompt
|
||||
from rag.prompts.prompts import chunks_format
|
||||
|
||||
@ -375,71 +369,14 @@ def ask_about():
|
||||
@validate_request("question", "kb_ids")
|
||||
def mindmap():
|
||||
req = request.json
|
||||
|
||||
search_id = req.get("search_id", "")
|
||||
search_app = None
|
||||
search_config = {}
|
||||
if search_id:
|
||||
search_app = SearchService.get_detail(search_id)
|
||||
if search_app:
|
||||
search_config = search_app.get("search_config", {})
|
||||
search_app = SearchService.get_detail(search_id) if search_id else {}
|
||||
search_config = search_app.get("search_config", {}) if search_app else {}
|
||||
kb_ids = search_config.get("kb_ids", [])
|
||||
kb_ids.extend(req["kb_ids"])
|
||||
kb_ids = list(set(kb_ids))
|
||||
|
||||
kb_ids = req["kb_ids"]
|
||||
if search_config.get("kb_ids", []):
|
||||
kb_ids = search_config.get("kb_ids", [])
|
||||
e, kb = KnowledgebaseService.get_by_id(kb_ids[0])
|
||||
if not e:
|
||||
return get_data_error_result(message="Knowledgebase not found!")
|
||||
|
||||
chat_id = ""
|
||||
similarity_threshold = 0.3,
|
||||
vector_similarity_weight = 0.3,
|
||||
top = 1024,
|
||||
doc_ids = []
|
||||
rerank_id = ""
|
||||
rerank_mdl = None
|
||||
|
||||
if search_config:
|
||||
if search_config.get("chat_id", ""):
|
||||
chat_id = search_config.get("chat_id", "")
|
||||
if search_config.get("similarity_threshold", 0.2):
|
||||
similarity_threshold = search_config.get("similarity_threshold", 0.2)
|
||||
if search_config.get("vector_similarity_weight", 0.3):
|
||||
vector_similarity_weight = search_config.get("vector_similarity_weight", 0.3)
|
||||
if search_config.get("top_k", 1024):
|
||||
top = search_config.get("top_k", 1024)
|
||||
if search_config.get("doc_ids", []):
|
||||
doc_ids = search_config.get("doc_ids", [])
|
||||
if search_config.get("rerank_id", ""):
|
||||
rerank_id = search_config.get("rerank_id", "")
|
||||
|
||||
tenant_id = kb.tenant_id
|
||||
if search_app and search_app.get("tenant_id", ""):
|
||||
tenant_id = search_app.get("tenant_id", "")
|
||||
|
||||
embd_mdl = LLMBundle(tenant_id, LLMType.EMBEDDING, llm_name=kb.embd_id)
|
||||
chat_mdl = LLMBundle(tenant_id, LLMType.CHAT, llm_name=chat_id)
|
||||
if rerank_id:
|
||||
rerank_mdl = LLMBundle(tenant_id, LLMType.RERANK, rerank_id)
|
||||
question = req["question"]
|
||||
ranks = settings.retrievaler.retrieval(
|
||||
question=question,
|
||||
embd_mdl=embd_mdl,
|
||||
tenant_ids=tenant_id,
|
||||
kb_ids=kb_ids,
|
||||
page=1,
|
||||
page_size=12,
|
||||
similarity_threshold=similarity_threshold,
|
||||
vector_similarity_weight=vector_similarity_weight,
|
||||
top=top,
|
||||
doc_ids=doc_ids,
|
||||
aggs=False,
|
||||
rerank_mdl=rerank_mdl,
|
||||
rank_feature=label_question(question, [kb]),
|
||||
)
|
||||
mindmap = MindMapExtractor(chat_mdl)
|
||||
mind_map = trio.run(mindmap, [c["content_with_weight"] for c in ranks["chunks"]])
|
||||
mind_map = mind_map.output
|
||||
mind_map = gen_mindmap(req["question"], kb_ids, search_app.get("tenant_id", current_user.id), search_config)
|
||||
if "error" in mind_map:
|
||||
return server_error_response(Exception(mind_map["error"]))
|
||||
return get_json_result(data=mind_map)
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
from flask import request
|
||||
from flask_login import login_required, current_user
|
||||
from api.db.services import duplicate_name
|
||||
from api.db.services.dialog_service import DialogService
|
||||
from api.db import StatusEnum
|
||||
from api.db.services.tenant_llm_service import TenantLLMService
|
||||
@ -41,6 +42,15 @@ def set_dialog():
|
||||
return get_data_error_result(message="Dialog name can't be empty.")
|
||||
if len(name.encode("utf-8")) > 255:
|
||||
return get_data_error_result(message=f"Dialog name length is {len(name)} which is larger than 255")
|
||||
|
||||
if is_create and DialogService.query(tenant_id=current_user.id, name=name.strip()):
|
||||
name = name.strip()
|
||||
name = duplicate_name(
|
||||
DialogService.query,
|
||||
name=name,
|
||||
tenant_id=current_user.id,
|
||||
status=StatusEnum.VALID.value)
|
||||
|
||||
description = req.get("description", "A helpful dialog")
|
||||
icon = req.get("icon", "")
|
||||
top_n = req.get("top_n", 6)
|
||||
|
||||
@ -18,7 +18,6 @@ import re
|
||||
import time
|
||||
import tiktoken
|
||||
from flask import Response, jsonify, request
|
||||
import trio
|
||||
from agent.canvas import Canvas
|
||||
from api import settings
|
||||
from api.db import LLMType, StatusEnum
|
||||
@ -28,14 +27,13 @@ from api.db.services.canvas_service import UserCanvasService, completionOpenAI
|
||||
from api.db.services.canvas_service import completion as agent_completion
|
||||
from api.db.services.conversation_service import ConversationService, iframe_completion
|
||||
from api.db.services.conversation_service import completion as rag_completion
|
||||
from api.db.services.dialog_service import DialogService, ask, chat
|
||||
from api.db.services.dialog_service import DialogService, ask, chat, gen_mindmap
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from api.db.services.search_service import SearchService
|
||||
from api.db.services.user_service import UserTenantService
|
||||
from api.utils import get_uuid
|
||||
from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_json_result, get_result, server_error_response, token_required, validate_request
|
||||
from graphrag.general.mind_map_extractor import MindMapExtractor
|
||||
from rag.app.tag import label_question
|
||||
from rag.prompts import chunks_format
|
||||
from rag.prompts.prompt_template import load_prompt
|
||||
@ -1102,63 +1100,9 @@ def mindmap():
|
||||
req = request.json
|
||||
|
||||
search_id = req.get("search_id", "")
|
||||
search_config = {}
|
||||
if search_id:
|
||||
if search_app := SearchService.get_detail(search_id):
|
||||
search_config = search_app.get("search_config", {})
|
||||
search_app = SearchService.get_detail(search_id) if search_id else {}
|
||||
|
||||
kb_ids = req["kb_ids"]
|
||||
if search_config.get("kb_ids", []):
|
||||
kb_ids = search_config.get("kb_ids", [])
|
||||
e, kb = KnowledgebaseService.get_by_id(kb_ids[0])
|
||||
if not e:
|
||||
return get_error_data_result(message="Knowledgebase not found!")
|
||||
|
||||
chat_id = ""
|
||||
similarity_threshold = 0.3,
|
||||
vector_similarity_weight = 0.3,
|
||||
top = 1024,
|
||||
doc_ids = []
|
||||
rerank_id = ""
|
||||
rerank_mdl = None
|
||||
|
||||
if search_config:
|
||||
if search_config.get("chat_id", ""):
|
||||
chat_id = search_config.get("chat_id", "")
|
||||
if search_config.get("similarity_threshold", 0.2):
|
||||
similarity_threshold = search_config.get("similarity_threshold", 0.2)
|
||||
if search_config.get("vector_similarity_weight", 0.3):
|
||||
vector_similarity_weight = search_config.get("vector_similarity_weight", 0.3)
|
||||
if search_config.get("top_k", 1024):
|
||||
top = search_config.get("top_k", 1024)
|
||||
if search_config.get("doc_ids", []):
|
||||
doc_ids = search_config.get("doc_ids", [])
|
||||
if search_config.get("rerank_id", ""):
|
||||
rerank_id = search_config.get("rerank_id", "")
|
||||
|
||||
embd_mdl = LLMBundle(tenant_id, LLMType.EMBEDDING, llm_name=kb.embd_id)
|
||||
chat_mdl = LLMBundle(tenant_id, LLMType.CHAT, llm_name=chat_id)
|
||||
if rerank_id:
|
||||
rerank_mdl = LLMBundle(tenant_id, LLMType.RERANK, rerank_id)
|
||||
question = req["question"]
|
||||
ranks = settings.retrievaler.retrieval(
|
||||
question=question,
|
||||
embd_mdl=embd_mdl,
|
||||
tenant_ids=tenant_id,
|
||||
kb_ids=kb_ids,
|
||||
page=1,
|
||||
page_size=12,
|
||||
similarity_threshold=similarity_threshold,
|
||||
vector_similarity_weight=vector_similarity_weight,
|
||||
top=top,
|
||||
doc_ids=doc_ids,
|
||||
aggs=False,
|
||||
rerank_mdl=rerank_mdl,
|
||||
rank_feature=label_question(question, [kb]),
|
||||
)
|
||||
mindmap = MindMapExtractor(chat_mdl)
|
||||
mind_map = trio.run(mindmap, [c["content_with_weight"] for c in ranks["chunks"]])
|
||||
mind_map = mind_map.output
|
||||
mind_map = gen_mindmap(req["question"], req["kb_ids"], tenant_id, search_app.get("search_config", {}))
|
||||
if "error" in mind_map:
|
||||
return server_error_response(Exception(mind_map["error"]))
|
||||
return get_json_result(data=mind_map)
|
||||
|
||||
@ -22,6 +22,7 @@ from datetime import datetime
|
||||
from functools import partial
|
||||
from timeit import default_timer as timer
|
||||
|
||||
import trio
|
||||
from langfuse import Langfuse
|
||||
from peewee import fn
|
||||
|
||||
@ -36,6 +37,7 @@ from api.db.services.langfuse_service import TenantLangfuseService
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from api.db.services.tenant_llm_service import TenantLLMService
|
||||
from api.utils import current_timestamp, datetime_format
|
||||
from graphrag.general.mind_map_extractor import MindMapExtractor
|
||||
from rag.app.resume import forbidden_select_fields4resume
|
||||
from rag.app.tag import label_question
|
||||
from rag.nlp.search import index_name
|
||||
@ -688,28 +690,12 @@ def tts(tts_mdl, text):
|
||||
|
||||
|
||||
def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
|
||||
similarity_threshold = 0.1,
|
||||
vector_similarity_weight = 0.3,
|
||||
top = 1024,
|
||||
doc_ids = []
|
||||
rerank_id = ""
|
||||
doc_ids = search_config.get("doc_ids", [])
|
||||
rerank_mdl = None
|
||||
|
||||
if search_config:
|
||||
if search_config.get("kb_ids", []):
|
||||
kb_ids = search_config.get("kb_ids", [])
|
||||
if search_config.get("chat_id", ""):
|
||||
chat_llm_name = search_config.get("chat_id", "")
|
||||
if search_config.get("similarity_threshold", 0.1):
|
||||
similarity_threshold = search_config.get("similarity_threshold", 0.1)
|
||||
if search_config.get("vector_similarity_weight", 0.3):
|
||||
vector_similarity_weight = search_config.get("vector_similarity_weight", 0.3)
|
||||
if search_config.get("top_k", 1024):
|
||||
top = search_config.get("top_k", 1024)
|
||||
if search_config.get("doc_ids", []):
|
||||
doc_ids = search_config.get("doc_ids", [])
|
||||
if search_config.get("rerank_id", ""):
|
||||
rerank_id = search_config.get("rerank_id", "")
|
||||
kb_ids = search_config.get("kb_ids", kb_ids)
|
||||
chat_llm_name = search_config.get("chat_id", chat_llm_name)
|
||||
rerank_id = search_config.get("rerank_id", "")
|
||||
meta_data_filter = search_config.get("meta_data_filter")
|
||||
|
||||
kbs = KnowledgebaseService.get_by_ids(kb_ids)
|
||||
embedding_list = list(set([kb.embd_id for kb in kbs]))
|
||||
@ -724,6 +710,18 @@ def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
|
||||
max_tokens = chat_mdl.max_length
|
||||
tenant_ids = list(set([kb.tenant_id for kb in kbs]))
|
||||
|
||||
if meta_data_filter:
|
||||
metas = DocumentService.get_meta_by_kbs(kb_ids)
|
||||
if meta_data_filter.get("method") == "auto":
|
||||
filters = gen_meta_filter(chat_mdl, metas, question)
|
||||
doc_ids.extend(meta_filter(metas, filters))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
elif meta_data_filter.get("method") == "manual":
|
||||
doc_ids.extend(meta_filter(metas, meta_data_filter["manual"]))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
|
||||
kbinfos = retriever.retrieval(
|
||||
question = question,
|
||||
embd_mdl=embd_mdl,
|
||||
@ -731,9 +729,9 @@ def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
|
||||
kb_ids=kb_ids,
|
||||
page=1,
|
||||
page_size=12,
|
||||
similarity_threshold=similarity_threshold,
|
||||
vector_similarity_weight=vector_similarity_weight,
|
||||
top=top,
|
||||
similarity_threshold=search_config.get("similarity_threshold", 0.1),
|
||||
vector_similarity_weight=search_config.get("vector_similarity_weight", 0.3),
|
||||
top=search_config.get("top_k", 1024),
|
||||
doc_ids=doc_ids,
|
||||
aggs=False,
|
||||
rerank_mdl=rerank_mdl,
|
||||
@ -768,3 +766,51 @@ def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
|
||||
answer = ans
|
||||
yield {"answer": answer, "reference": {}}
|
||||
yield decorate_answer(answer)
|
||||
|
||||
|
||||
def gen_mindmap(question, kb_ids, tenant_id, search_config={}):
|
||||
meta_data_filter = search_config.get("meta_data_filter", {})
|
||||
doc_ids = search_config.get("doc_ids", [])
|
||||
rerank_id = search_config.get("rerank_id", "")
|
||||
rerank_mdl = None
|
||||
kbs = KnowledgebaseService.get_by_ids(kb_ids)
|
||||
if not kbs:
|
||||
return {"error": "No KB selected"}
|
||||
embedding_list = list(set([kb.embd_id for kb in kbs]))
|
||||
tenant_ids = list(set([kb.tenant_id for kb in kbs]))
|
||||
|
||||
embd_mdl = LLMBundle(tenant_id, LLMType.EMBEDDING, llm_name=embedding_list[0])
|
||||
chat_mdl = LLMBundle(tenant_id, LLMType.CHAT, llm_name=search_config.get("chat_id", ""))
|
||||
if rerank_id:
|
||||
rerank_mdl = LLMBundle(tenant_id, LLMType.RERANK, rerank_id)
|
||||
|
||||
if meta_data_filter:
|
||||
metas = DocumentService.get_meta_by_kbs(kb_ids)
|
||||
if meta_data_filter.get("method") == "auto":
|
||||
filters = gen_meta_filter(chat_mdl, metas, question)
|
||||
doc_ids.extend(meta_filter(metas, filters))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
elif meta_data_filter.get("method") == "manual":
|
||||
doc_ids.extend(meta_filter(metas, meta_data_filter["manual"]))
|
||||
if not doc_ids:
|
||||
doc_ids = None
|
||||
|
||||
ranks = settings.retrievaler.retrieval(
|
||||
question=question,
|
||||
embd_mdl=embd_mdl,
|
||||
tenant_ids=tenant_ids,
|
||||
kb_ids=kb_ids,
|
||||
page=1,
|
||||
page_size=12,
|
||||
similarity_threshold=search_config.get("similarity_threshold", 0.2),
|
||||
vector_similarity_weight=search_config.get("vector_similarity_weight", 0.3),
|
||||
top=search_config.get("top_k", 1024),
|
||||
doc_ids=doc_ids,
|
||||
aggs=False,
|
||||
rerank_mdl=rerank_mdl,
|
||||
rank_feature=label_question(question, kbs),
|
||||
)
|
||||
mindmap = MindMapExtractor(chat_mdl)
|
||||
mind_map = trio.run(mindmap, [c["content_with_weight"] for c in ranks["chunks"]])
|
||||
return mind_map.output
|
||||
@ -71,6 +71,8 @@ class SearchService(CommonService):
|
||||
.first()
|
||||
.to_dict()
|
||||
)
|
||||
if not search:
|
||||
return {}
|
||||
return search
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -505,6 +505,24 @@
|
||||
"tags": "RE-RANK,4k",
|
||||
"max_tokens": 4000,
|
||||
"model_type": "rerank"
|
||||
},
|
||||
{
|
||||
"llm_name": "qwen-audio-asr",
|
||||
"tags": "SPEECH2TEXT,8k",
|
||||
"max_tokens": 8000,
|
||||
"model_type": "speech2text"
|
||||
},
|
||||
{
|
||||
"llm_name": "qwen-audio-asr-latest",
|
||||
"tags": "SPEECH2TEXT,8k",
|
||||
"max_tokens": 8000,
|
||||
"model_type": "speech2text"
|
||||
},
|
||||
{
|
||||
"llm_name": "qwen-audio-asr-1204",
|
||||
"tags": "SPEECH2TEXT,8k",
|
||||
"max_tokens": 8000,
|
||||
"model_type": "speech2text"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -94,7 +94,7 @@ SVR_HTTP_PORT=9380
|
||||
|
||||
# The RAGFlow Docker image to download.
|
||||
# Defaults to the v0.20.1-slim edition, which is the RAGFlow Docker image without embedding models.
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1-slim
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2-slim
|
||||
#
|
||||
# To download the RAGFlow Docker image with embedding models, uncomment the following line instead:
|
||||
# RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1
|
||||
|
||||
@ -79,8 +79,8 @@ The [.env](./.env) file contains important environment variables for Docker.
|
||||
- `RAGFLOW-IMAGE`
|
||||
The Docker image edition. Available editions:
|
||||
|
||||
- `infiniflow/ragflow:v0.20.1-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.1`: The RAGFlow Docker image with embedding models including:
|
||||
- `infiniflow/ragflow:v0.20.2-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.2`: The RAGFlow Docker image with embedding models including:
|
||||
- Built-in embedding models:
|
||||
- `BAAI/bge-large-zh-v1.5`
|
||||
- `maidalun1020/bce-embedding-base_v1`
|
||||
|
||||
@ -6,3 +6,7 @@ proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_buffer_size 1024k;
|
||||
proxy_buffers 16 1024k;
|
||||
proxy_busy_buffers_size 2048k;
|
||||
proxy_temp_file_write_size 2048k;
|
||||
@ -99,8 +99,8 @@ RAGFlow utilizes MinIO as its object storage solution, leveraging its scalabilit
|
||||
- `RAGFLOW-IMAGE`
|
||||
The Docker image edition. Available editions:
|
||||
|
||||
- `infiniflow/ragflow:v0.20.1-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.1`: The RAGFlow Docker image with embedding models including:
|
||||
- `infiniflow/ragflow:v0.20.2-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.2`: The RAGFlow Docker image with embedding models including:
|
||||
- Built-in embedding models:
|
||||
- `BAAI/bge-large-zh-v1.5`
|
||||
- `maidalun1020/bce-embedding-base_v1`
|
||||
|
||||
@ -77,7 +77,7 @@ After building the infiniflow/ragflow:nightly-slim image, you are ready to launc
|
||||
|
||||
1. Edit Docker Compose Configuration
|
||||
|
||||
Open the `docker/.env` file. Find the `RAGFLOW_IMAGE` setting and change the image reference from `infiniflow/ragflow:v0.20.1-slim` to `infiniflow/ragflow:nightly-slim` to use the pre-built image.
|
||||
Open the `docker/.env` file. Find the `RAGFLOW_IMAGE` setting and change the image reference from `infiniflow/ragflow:v0.20.2-slim` to `infiniflow/ragflow:nightly-slim` to use the pre-built image.
|
||||
|
||||
|
||||
2. Launch the Service
|
||||
|
||||
10
docs/faq.mdx
10
docs/faq.mdx
@ -30,17 +30,17 @@ The "garbage in garbage out" status quo remains unchanged despite the fact that
|
||||
|
||||
Each RAGFlow release is available in two editions:
|
||||
|
||||
- **Slim edition**: excludes built-in embedding models and is identified by a **-slim** suffix added to the version name. Example: `infiniflow/ragflow:v0.20.1-slim`
|
||||
- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.20.1`
|
||||
- **Slim edition**: excludes built-in embedding models and is identified by a **-slim** suffix added to the version name. Example: `infiniflow/ragflow:v0.20.2-slim`
|
||||
- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.20.2`
|
||||
|
||||
---
|
||||
|
||||
### Which embedding models can be deployed locally?
|
||||
|
||||
RAGFlow offers two Docker image editions, `v0.20.1-slim` and `v0.20.1`:
|
||||
RAGFlow offers two Docker image editions, `v0.20.2-slim` and `v0.20.2`:
|
||||
|
||||
- `infiniflow/ragflow:v0.20.1-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.1`: The RAGFlow Docker image with embedding models including:
|
||||
- `infiniflow/ragflow:v0.20.2-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.20.2`: The RAGFlow Docker image with embedding models including:
|
||||
- Built-in embedding models:
|
||||
- `BAAI/bge-large-zh-v1.5`
|
||||
- `maidalun1020/bce-embedding-base_v1`
|
||||
|
||||
@ -9,7 +9,7 @@ The component equipped with reasoning, tool usage, and multi-agent collaboration
|
||||
|
||||
---
|
||||
|
||||
An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.1 onwards, an **Agent** component is able to work independently and with the following capabilities:
|
||||
An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.2 onwards, an **Agent** component is able to work independently and with the following capabilities:
|
||||
|
||||
- Autonomous reasoning with reflection and adjustment based on environmental feedback.
|
||||
- Use of tools or subagents to complete tasks.
|
||||
|
||||
@ -9,7 +9,7 @@ A component that retrieves information from specified datasets.
|
||||
|
||||
## Scenarios
|
||||
|
||||
A **Retrieval** component is essential in most RAG scenarios, where information is extracted from designated knowledge bases before being sent to the LLM for content generation. As of v0.20.1, a **Retrieval** component can operate either as a workflow component or as a tool of an **Agent**, enabling the Agent to control its invocation and search queries.
|
||||
A **Retrieval** component is essential in most RAG scenarios, where information is extracted from designated knowledge bases before being sent to the LLM for content generation. As of v0.20.2, a **Retrieval** component can operate either as a workflow component or as a tool of an **Agent**, enabling the Agent to control its invocation and search queries.
|
||||
|
||||
## Configurations
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ You start an AI conversation by creating an assistant.
|
||||
- If no target language is selected, the system will search only in the language of your query, which may cause relevant information in other languages to be missed.
|
||||
- **Variable** refers to the variables (keys) to be used in the system prompt. `{knowledge}` is a reserved variable. Click **Add** to add more variables for the system prompt.
|
||||
- If you are uncertain about the logic behind **Variable**, leave it *as-is*.
|
||||
- As of v0.20.1, if you add custom variables here, the only way you can pass in their values is to call:
|
||||
- As of v0.20.2, if you add custom variables here, the only way you can pass in their values is to call:
|
||||
- HTTP method [Converse with chat assistant](../../references/http_api_reference.md#converse-with-chat-assistant), or
|
||||
- Python method [Converse with chat assistant](../../references/python_api_reference.md#converse-with-chat-assistant).
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ See [Run retrieval test](./run_retrieval_test.md) for details.
|
||||
|
||||
## Search for knowledge base
|
||||
|
||||
As of RAGFlow v0.20.1, the search feature is still in a rudimentary form, supporting only knowledge base search by name.
|
||||
As of RAGFlow v0.20.2, the search feature is still in a rudimentary form, supporting only knowledge base search by name.
|
||||
|
||||

|
||||
|
||||
|
||||
@ -87,4 +87,4 @@ RAGFlow's file management allows you to download an uploaded file:
|
||||
|
||||

|
||||
|
||||
> As of RAGFlow v0.20.1, bulk download is not supported, nor can you download an entire folder.
|
||||
> As of RAGFlow v0.20.2, bulk download is not supported, nor can you download an entire folder.
|
||||
|
||||
@ -18,7 +18,7 @@ RAGFlow ships with a built-in [Langfuse](https://langfuse.com) integration so th
|
||||
Langfuse stores traces, spans and prompt payloads in a purpose-built observability backend and offers filtering and visualisations on top.
|
||||
|
||||
:::info NOTE
|
||||
• RAGFlow **≥ 0.20.1** (contains the Langfuse connector)
|
||||
• RAGFlow **≥ 0.20.2** (contains the Langfuse connector)
|
||||
• A Langfuse workspace (cloud or self-hosted) with a _Project Public Key_ and _Secret Key_
|
||||
:::
|
||||
|
||||
|
||||
@ -66,10 +66,10 @@ To upgrade RAGFlow, you must upgrade **both** your code **and** your Docker imag
|
||||
git clone https://github.com/infiniflow/ragflow.git
|
||||
```
|
||||
|
||||
2. Switch to the latest, officially published release, e.g., `v0.20.1`:
|
||||
2. Switch to the latest, officially published release, e.g., `v0.20.2`:
|
||||
|
||||
```bash
|
||||
git checkout -f v0.20.1
|
||||
git checkout -f v0.20.2
|
||||
```
|
||||
|
||||
3. Update **ragflow/docker/.env**:
|
||||
@ -83,14 +83,14 @@ To upgrade RAGFlow, you must upgrade **both** your code **and** your Docker imag
|
||||
<TabItem value="slim">
|
||||
|
||||
```bash
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1-slim
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2-slim
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="full">
|
||||
|
||||
```bash
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1
|
||||
RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -114,10 +114,10 @@ No, you do not need to. Upgrading RAGFlow in itself will *not* remove your uploa
|
||||
1. From an environment with Internet access, pull the required Docker image.
|
||||
2. Save the Docker image to a **.tar** file.
|
||||
```bash
|
||||
docker save -o ragflow.v0.20.1.tar infiniflow/ragflow:v0.20.1
|
||||
docker save -o ragflow.v0.20.2.tar infiniflow/ragflow:v0.20.2
|
||||
```
|
||||
3. Copy the **.tar** file to the target server.
|
||||
4. Load the **.tar** file into Docker:
|
||||
```bash
|
||||
docker load -i ragflow.v0.20.1.tar
|
||||
docker load -i ragflow.v0.20.2.tar
|
||||
```
|
||||
|
||||
@ -44,7 +44,7 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
||||
|
||||
`vm.max_map_count`. This value sets the maximum number of memory map areas a process may have. Its default value is 65530. While most applications require fewer than a thousand maps, reducing this value can result in abnormal behaviors, and the system will throw out-of-memory errors when a process reaches the limitation.
|
||||
|
||||
RAGFlow v0.20.1 uses Elasticsearch or [Infinity](https://github.com/infiniflow/infinity) for multiple recall. Setting the value of `vm.max_map_count` correctly is crucial to the proper functioning of the Elasticsearch component.
|
||||
RAGFlow v0.20.2 uses Elasticsearch or [Infinity](https://github.com/infiniflow/infinity) for multiple recall. Setting the value of `vm.max_map_count` correctly is crucial to the proper functioning of the Elasticsearch component.
|
||||
|
||||
<Tabs
|
||||
defaultValue="linux"
|
||||
@ -184,13 +184,13 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
||||
```bash
|
||||
$ git clone https://github.com/infiniflow/ragflow.git
|
||||
$ cd ragflow/docker
|
||||
$ git checkout -f v0.20.1
|
||||
$ git checkout -f v0.20.2
|
||||
```
|
||||
|
||||
3. Use the pre-built Docker images and start up the server:
|
||||
|
||||
:::tip NOTE
|
||||
The command below downloads the `v0.20.1-slim` edition of the RAGFlow Docker image. Refer to the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.1-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.1` for the full edition `v0.20.1`.
|
||||
The command below downloads the `v0.20.2-slim` edition of the RAGFlow Docker image. Refer to the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.2-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.2` for the full edition `v0.20.2`.
|
||||
:::
|
||||
|
||||
```bash
|
||||
@ -207,8 +207,8 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models and Python packages? | Stable? |
|
||||
| ------------------- | --------------- | ----------------------------------------- | ------------------------ |
|
||||
| `v0.20.1` | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| `v0.20.1-slim` | ≈2 | ❌ | Stable release |
|
||||
| `v0.20.2` | ≈9 | :heavy_check_mark: | Stable release |
|
||||
| `v0.20.2-slim` | ≈2 | ❌ | Stable release |
|
||||
| `nightly` | ≈9 | :heavy_check_mark: | *Unstable* nightly build |
|
||||
| `nightly-slim` | ≈2 | ❌ | *Unstable* nightly build |
|
||||
|
||||
@ -217,7 +217,7 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
||||
```
|
||||
|
||||
:::danger IMPORTANT
|
||||
The embedding models included in `v0.20.1` and `nightly` are:
|
||||
The embedding models included in `v0.20.2` and `nightly` are:
|
||||
|
||||
- BAAI/bge-large-zh-v1.5
|
||||
- maidalun1020/bce-embedding-base_v1
|
||||
|
||||
@ -19,7 +19,7 @@ import TOCInline from '@theme/TOCInline';
|
||||
|
||||
### Cross-language search
|
||||
|
||||
Cross-language search (also known as cross-lingual retrieval) is a feature introduced in version 0.20.1. It enables users to submit queries in one language (for example, English) and retrieve relevant documents written in other languages such as Chinese or Spanish. This feature is enabled by the system’s default chat model, which translates queries to ensure accurate matching of semantic meaning across languages.
|
||||
Cross-language search (also known as cross-lingual retrieval) is a feature introduced in version 0.20.2. It enables users to submit queries in one language (for example, English) and retrieve relevant documents written in other languages such as Chinese or Spanish. This feature is enabled by the system’s default chat model, which translates queries to ensure accurate matching of semantic meaning across languages.
|
||||
|
||||
By enabling cross-language search, users can effortlessly access a broader range of information regardless of language barriers, significantly enhancing the system’s usability and inclusiveness.
|
||||
|
||||
|
||||
@ -2656,7 +2656,7 @@ Creates a session with an agent.
|
||||
- Body:
|
||||
- the required parameters:`str`
|
||||
- other parameters:
|
||||
The parameters specified in the **Begin** component.
|
||||
The variables specified in the **Begin** component.
|
||||
|
||||
##### Request example
|
||||
|
||||
@ -3000,13 +3000,19 @@ curl --request POST \
|
||||
- `"session_id"`: (*Body Parameter*)
|
||||
The ID of the session. If it is not provided, a new session will be generated.
|
||||
- `"inputs"`: (*Body Parameter*)
|
||||
Parameters specified in the **Begin** component.
|
||||
Variables specified in the **Begin** component.
|
||||
- `"user_id"`: (*Body parameter*), `string`
|
||||
The optional user-defined ID. Valid *only* when no `session_id` is provided.
|
||||
|
||||
:::tip NOTE
|
||||
For now, this method does *not* support a file type input/variable. As a workaround, use the following to upload a file to an agent:
|
||||
`http://{address}/v1/canvas/upload/{agent_id}`
|
||||
*You will get a corresponding file ID from its response body.*
|
||||
:::
|
||||
|
||||
#### Response
|
||||
|
||||
success without `session_id` provided and with no parameters specified in the **Begin** component:
|
||||
success without `session_id` provided and with no variables specified in the **Begin** component:
|
||||
|
||||
Stream:
|
||||
|
||||
@ -3074,7 +3080,7 @@ Non-stream:
|
||||
}
|
||||
```
|
||||
|
||||
Success without `session_id` provided and with parameters specified in the **Begin** component:
|
||||
Success without `session_id` provided and with variables specified in the **Begin** component:
|
||||
|
||||
Stream:
|
||||
|
||||
@ -3163,7 +3169,7 @@ Non-stream:
|
||||
}
|
||||
```
|
||||
|
||||
Success with parameters specified in the **Begin** component:
|
||||
Success with variables specified in the **Begin** component:
|
||||
|
||||
Stream:
|
||||
|
||||
|
||||
@ -22,13 +22,14 @@ The embedding models included in a full edition are:
|
||||
These two embedding models are optimized specifically for English and Chinese, so performance may be compromised if you use them to embed documents in other languages.
|
||||
:::
|
||||
|
||||
## v0.20.2 (Ongoing🔨)
|
||||
## v0.20.2
|
||||
|
||||
Released on August ??, 2025.
|
||||
Released on August 19, 2025.
|
||||
|
||||
### Improvements
|
||||
|
||||
- Revamps the user interface for the **Datasets**, **Chat**, and **Search** pages.
|
||||
- Search and Chat: Introduces document-level metadata filtering, allowing automatic or manual filtering during chats or searches.
|
||||
- Search: Supports creating search apps tailored to various business scenarios
|
||||
- Chat: Supports comparing answer performance of up to three chat model settings on a single **Chat** page.
|
||||
- Agent:
|
||||
@ -42,6 +43,7 @@ Released on August ??, 2025.
|
||||
|
||||
### Fixed issues
|
||||
|
||||
- The timeout mechanism introduced in v0.20.0 caused tasks like GraphRAG to halt.
|
||||
- Predefined opening greeting in the **Agent** component was missing during conversations.
|
||||
- An automatic line break issue in the prompt editor.
|
||||
- A memory leak issue caused by PyPDF. [#9469](https://github.com/infiniflow/ragflow/pull/9469)
|
||||
|
||||
@ -56,7 +56,7 @@ env:
|
||||
ragflow:
|
||||
image:
|
||||
repository: infiniflow/ragflow
|
||||
tag: v0.20.1-slim
|
||||
tag: v0.20.2-slim
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecrets: []
|
||||
# Optional service configuration overrides
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "ragflow"
|
||||
version = "0.20.1"
|
||||
version = "0.20.2"
|
||||
description = "[RAGFlow](https://ragflow.io/) is an open-source RAG (Retrieval-Augmented Generation) engine based on deep document understanding. It offers a streamlined RAG workflow for businesses of any scale, combining LLM (Large Language Models) to provide truthful question-answering capabilities, backed by well-founded citations from various complex formatted data."
|
||||
authors = [{ name = "Zhichang Yu", email = "yuzhichang@gmail.com" }]
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
@ -14,31 +14,48 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
from api.db import LLMType
|
||||
from rag.nlp import rag_tokenizer
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from rag.nlp import tokenize
|
||||
from rag.nlp import rag_tokenizer, tokenize
|
||||
|
||||
|
||||
def chunk(filename, binary, tenant_id, lang, callback=None, **kwargs):
|
||||
doc = {
|
||||
"docnm_kwd": filename,
|
||||
"title_tks": rag_tokenizer.tokenize(re.sub(r"\.[a-zA-Z]+$", "", filename))
|
||||
}
|
||||
doc = {"docnm_kwd": filename, "title_tks": rag_tokenizer.tokenize(re.sub(r"\.[a-zA-Z]+$", "", filename))}
|
||||
doc["title_sm_tks"] = rag_tokenizer.fine_grained_tokenize(doc["title_tks"])
|
||||
|
||||
# is it English
|
||||
eng = lang.lower() == "english" # is_english(sections)
|
||||
try:
|
||||
_, ext = os.path.splitext(filename)
|
||||
if not ext:
|
||||
raise RuntimeError("No extension detected.")
|
||||
|
||||
if ext not in [".da", ".wave", ".wav", ".mp3", ".wav", ".aac", ".flac", ".ogg", ".aiff", ".au", ".midi", ".wma", ".realaudio", ".vqf", ".oggvorbis", ".aac", ".ape"]:
|
||||
raise RuntimeError(f"Extension {ext} is not supported yet.")
|
||||
|
||||
tmp_path = ""
|
||||
with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmpf:
|
||||
tmpf.write(binary)
|
||||
tmpf.flush()
|
||||
tmp_path = os.path.abspath(tmpf.name)
|
||||
|
||||
callback(0.1, "USE Sequence2Txt LLM to transcription the audio")
|
||||
seq2txt_mdl = LLMBundle(tenant_id, LLMType.SPEECH2TEXT, lang=lang)
|
||||
ans = seq2txt_mdl.transcription(binary)
|
||||
ans = seq2txt_mdl.transcription(tmp_path)
|
||||
callback(0.8, "Sequence2Txt LLM respond: %s ..." % ans[:32])
|
||||
|
||||
tokenize(doc, ans, eng)
|
||||
return [doc]
|
||||
except Exception as e:
|
||||
callback(prog=-1, msg=str(e))
|
||||
|
||||
finally:
|
||||
if tmp_path and os.path.exists(tmp_path):
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
@ -35,8 +35,9 @@ class Base(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
def transcription(self, audio, **kwargs):
|
||||
transcription = self.client.audio.transcriptions.create(model=self.model_name, file=audio, response_format="text")
|
||||
def transcription(self, audio_path, **kwargs):
|
||||
audio_file = open(audio_path, "rb")
|
||||
transcription = self.client.audio.transcriptions.create(model=self.model_name, file=audio_file)
|
||||
return transcription.text.strip(), num_tokens_from_string(transcription.text.strip())
|
||||
|
||||
def audio2base64(self, audio):
|
||||
@ -50,7 +51,7 @@ class Base(ABC):
|
||||
class GPTSeq2txt(Base):
|
||||
_FACTORY_NAME = "OpenAI"
|
||||
|
||||
def __init__(self, key, model_name="whisper-1", base_url="https://api.openai.com/v1"):
|
||||
def __init__(self, key, model_name="whisper-1", base_url="https://api.openai.com/v1", **kwargs):
|
||||
if not base_url:
|
||||
base_url = "https://api.openai.com/v1"
|
||||
self.client = OpenAI(api_key=key, base_url=base_url)
|
||||
@ -60,27 +61,38 @@ class GPTSeq2txt(Base):
|
||||
class QWenSeq2txt(Base):
|
||||
_FACTORY_NAME = "Tongyi-Qianwen"
|
||||
|
||||
def __init__(self, key, model_name="paraformer-realtime-8k-v1", **kwargs):
|
||||
def __init__(self, key, model_name="qwen-audio-asr", **kwargs):
|
||||
import dashscope
|
||||
|
||||
dashscope.api_key = key
|
||||
self.model_name = model_name
|
||||
|
||||
def transcription(self, audio, format):
|
||||
from http import HTTPStatus
|
||||
def transcription(self, audio_path):
|
||||
if "paraformer" in self.model_name or "sensevoice" in self.model_name:
|
||||
return f"**ERROR**: model {self.model_name} is not suppported yet.", 0
|
||||
|
||||
from dashscope.audio.asr import Recognition
|
||||
from dashscope import MultiModalConversation
|
||||
|
||||
recognition = Recognition(model=self.model_name, format=format, sample_rate=16000, callback=None)
|
||||
result = recognition.call(audio)
|
||||
audio_path = f"file://{audio_path}"
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [{"audio": audio_path}],
|
||||
}
|
||||
]
|
||||
|
||||
ans = ""
|
||||
if result.status_code == HTTPStatus.OK:
|
||||
for sentence in result.get_sentence():
|
||||
ans += sentence.text.decode("utf-8") + "\n"
|
||||
return ans, num_tokens_from_string(ans)
|
||||
|
||||
return "**ERROR**: " + result.message, 0
|
||||
response = None
|
||||
full_content = ""
|
||||
try:
|
||||
response = MultiModalConversation.call(model="qwen-audio-asr", messages=messages, result_format="message", stream=True)
|
||||
for response in response:
|
||||
try:
|
||||
full_content += response["output"]["choices"][0]["message"].content[0]["text"]
|
||||
except Exception:
|
||||
pass
|
||||
return full_content, num_tokens_from_string(full_content)
|
||||
except Exception as e:
|
||||
return "**ERROR**: " + str(e), 0
|
||||
|
||||
|
||||
class AzureSeq2txt(Base):
|
||||
@ -212,6 +224,7 @@ class GiteeSeq2txt(Base):
|
||||
self.client = OpenAI(api_key=key, base_url=base_url)
|
||||
self.model_name = model_name
|
||||
|
||||
|
||||
class DeepInfraSeq2txt(Base):
|
||||
_FACTORY_NAME = "DeepInfra"
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "ragflow-sdk"
|
||||
version = "0.20.1"
|
||||
version = "0.20.2"
|
||||
description = "Python client sdk of [RAGFlow](https://github.com/infiniflow/ragflow). RAGFlow is an open-source RAG (Retrieval-Augmented Generation) engine based on deep document understanding."
|
||||
authors = [{ name = "Zhichang Yu", email = "yuzhichang@gmail.com" }]
|
||||
license = { text = "Apache License, Version 2.0" }
|
||||
|
||||
2
sdk/python/uv.lock
generated
2
sdk/python/uv.lock
generated
@ -342,7 +342,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ragflow-sdk"
|
||||
version = "0.20.1"
|
||||
version = "0.20.2"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "beartype" },
|
||||
|
||||
2
uv.lock
generated
2
uv.lock
generated
@ -5268,7 +5268,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ragflow"
|
||||
version = "0.20.1"
|
||||
version = "0.20.2"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "akshare" },
|
||||
|
||||
@ -317,7 +317,11 @@ export function ChunkMethodDialog({
|
||||
</FormContainer>
|
||||
)}
|
||||
{showGraphRagItems(selectedTag as DocumentParserType) &&
|
||||
useGraphRag && <UseGraphRagFormField></UseGraphRagFormField>}
|
||||
useGraphRag && (
|
||||
<FormContainer>
|
||||
<UseGraphRagFormField></UseGraphRagFormField>
|
||||
</FormContainer>
|
||||
)}
|
||||
{showEntityTypes && <EntityTypesFormField></EntityTypesFormField>}
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@ -50,10 +50,10 @@ export function DelimiterFormField() {
|
||||
}
|
||||
return (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<FormLabel
|
||||
tooltip={t('knowledgeDetails.delimiterTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4"
|
||||
>
|
||||
{t('knowledgeDetails.delimiter')}
|
||||
</FormLabel>
|
||||
|
||||
@ -25,10 +25,10 @@ export function ExcelToHtmlFormField() {
|
||||
|
||||
return (
|
||||
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<FormLabel
|
||||
tooltip={t('html4excelTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4"
|
||||
>
|
||||
{t('html4excel')}
|
||||
</FormLabel>
|
||||
|
||||
54
web/src/components/home-card.tsx
Normal file
54
web/src/components/home-card.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { formatDate } from '@/utils/date';
|
||||
|
||||
interface IProps {
|
||||
data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
avatar?: string;
|
||||
update_time?: string | number;
|
||||
};
|
||||
onClick?: () => void;
|
||||
moreDropdown: React.ReactNode;
|
||||
}
|
||||
export function HomeCard({ data, onClick, moreDropdown }: IProps) {
|
||||
return (
|
||||
<Card
|
||||
className="bg-bg-card border-colors-outline-neutral-standard"
|
||||
onClick={() => {
|
||||
// navigateToSearch(data?.id);
|
||||
onClick?.();
|
||||
}}
|
||||
>
|
||||
<CardContent className="p-4 flex gap-2 items-start group h-full">
|
||||
<div className="flex justify-between mb-4">
|
||||
<RAGFlowAvatar
|
||||
className="w-[32px] h-[32px]"
|
||||
avatar={data.avatar}
|
||||
name={data.name}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between gap-1 flex-1 h-full w-[calc(100%-50px)]">
|
||||
<section className="flex justify-between">
|
||||
<div className="text-[20px] font-bold w-80% leading-5">
|
||||
{data.name}
|
||||
</div>
|
||||
{moreDropdown}
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-1 mt-1">
|
||||
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{data.description}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm opacity-80">
|
||||
{formatDate(data.update_time)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
72
web/src/components/metadata-filter/index.tsx
Normal file
72
web/src/components/metadata-filter/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { DatasetMetadata } from '@/constants/chat';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { SelectWithSearch } from '../originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '../ragflow-form';
|
||||
import { MetadataFilterConditions } from './metadata-filter-conditions';
|
||||
|
||||
type MetadataFilterProps = {
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
export const MetadataFilterSchema = {
|
||||
meta_data_filter: z
|
||||
.object({
|
||||
method: z.string().optional(),
|
||||
manual: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
op: z.string(),
|
||||
value: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
};
|
||||
|
||||
export function MetadataFilter({ prefix = '' }: MetadataFilterProps) {
|
||||
const { t } = useTranslate('chat');
|
||||
const form = useFormContext();
|
||||
|
||||
const methodName = prefix + 'meta_data_filter.method';
|
||||
|
||||
const kbIds: string[] = useWatch({
|
||||
control: form.control,
|
||||
name: prefix + 'kb_ids',
|
||||
});
|
||||
const metadata = useWatch({
|
||||
control: form.control,
|
||||
name: methodName,
|
||||
});
|
||||
const hasKnowledge = Array.isArray(kbIds) && kbIds.length > 0;
|
||||
|
||||
const MetadataOptions = Object.values(DatasetMetadata).map((x) => {
|
||||
return {
|
||||
value: x,
|
||||
label: t(`meta.${x}`),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasKnowledge && (
|
||||
<RAGFlowFormItem
|
||||
label={t('metadata')}
|
||||
name={methodName}
|
||||
tooltip={t('metadataTip')}
|
||||
>
|
||||
<SelectWithSearch options={MetadataOptions} />
|
||||
</RAGFlowFormItem>
|
||||
)}
|
||||
{hasKnowledge && metadata === DatasetMetadata.Manual && (
|
||||
<MetadataFilterConditions
|
||||
kbIds={kbIds}
|
||||
prefix={prefix}
|
||||
></MetadataFilterConditions>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request';
|
||||
import { SwitchOperatorOptions } from '@/pages/agent/constant';
|
||||
import { useBuildSwitchOperatorOptions } from '@/pages/agent/form/switch-form';
|
||||
import { Plus, X } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function MetadataFilterConditions({
|
||||
kbIds,
|
||||
prefix = '',
|
||||
}: {
|
||||
kbIds: string[];
|
||||
prefix?: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
const name = prefix + 'meta_data_filter.manual';
|
||||
const metadata = useFetchKnowledgeMetadata(kbIds);
|
||||
|
||||
const switchOperatorOptions = useBuildSwitchOperatorOptions();
|
||||
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
const add = useCallback(
|
||||
(key: string) => () => {
|
||||
append({
|
||||
key,
|
||||
value: '',
|
||||
op: SwitchOperatorOptions[0].value,
|
||||
});
|
||||
},
|
||||
[append],
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<FormLabel>{t('chat.conditions')}</FormLabel>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant={'ghost'} type="button">
|
||||
<Plus />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{Object.keys(metadata.data).map((key, idx) => {
|
||||
return (
|
||||
<DropdownMenuItem key={idx} onClick={add(key)}>
|
||||
{key}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="space-y-5">
|
||||
{fields.map((field, index) => {
|
||||
const typeField = `${name}.${index}.key`;
|
||||
return (
|
||||
<div key={field.id} className="flex w-full items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={typeField}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t('common.pleaseInput')}
|
||||
></Input>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Separator className="w-3 text-text-secondary" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.op`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<SelectWithSearch
|
||||
{...field}
|
||||
options={switchOperatorOptions}
|
||||
></SelectWithSearch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Separator className="w-3 text-text-secondary" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<Input placeholder={t('common.pleaseInput')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button variant={'ghost'} onClick={() => remove(index)}>
|
||||
<X className="text-text-sub-title-invert " />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -59,10 +59,10 @@ export function UseGraphRagFormField() {
|
||||
name="parser_config.graphrag.use_graphrag"
|
||||
render={({ field }) => (
|
||||
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<FormLabel
|
||||
tooltip={t('useGraphRagTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4"
|
||||
>
|
||||
{t('useGraphRag')}
|
||||
</FormLabel>
|
||||
|
||||
@ -86,10 +86,10 @@ const RaptorFormFields = () => {
|
||||
defaultChecked={false}
|
||||
className="items-center space-y-0 "
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<FormLabel
|
||||
tooltip={t('useRaptorTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
className="text-sm text-muted-foreground w-1/4 whitespace-break-spaces"
|
||||
>
|
||||
{t('useRaptor')}
|
||||
</FormLabel>
|
||||
|
||||
@ -3,6 +3,13 @@ import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
import { forwardRef, memo, useEffect, useRef, useState } from 'react';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||
|
||||
const PREDEFINED_COLORS = [
|
||||
{ from: '#4F6DEE', to: '#67BDF9' },
|
||||
{ from: '#38A04D', to: '#93DCA2' },
|
||||
{ from: '#C35F2B', to: '#EDB395' },
|
||||
{ from: '#633897', to: '#CBA1FF' },
|
||||
];
|
||||
|
||||
const getStringHash = (str: string): number => {
|
||||
const normalized = str.trim().toLowerCase();
|
||||
let hash = 104729;
|
||||
@ -17,16 +24,12 @@ const getStringHash = (str: string): number => {
|
||||
return Math.abs(hash);
|
||||
};
|
||||
|
||||
// Generate a hash function with a fixed color
|
||||
const getColorForName = (name: string): { from: string; to: string } => {
|
||||
const hash = getStringHash(name);
|
||||
const hue = hash % 360;
|
||||
|
||||
return {
|
||||
to: `hsl(${hue}, 70%, 80%)`,
|
||||
from: `hsl(${hue}, 60%, 30%)`,
|
||||
};
|
||||
const index = hash % PREDEFINED_COLORS.length;
|
||||
return PREDEFINED_COLORS[index];
|
||||
};
|
||||
|
||||
export const RAGFlowAvatar = memo(
|
||||
forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
@ -43,7 +46,7 @@ export const RAGFlowAvatar = memo(
|
||||
if (parts.length === 1) {
|
||||
return parts[0][0].toUpperCase();
|
||||
}
|
||||
return parts[0][0].toUpperCase() + parts[1][0].toUpperCase();
|
||||
return parts[0][0].toUpperCase();
|
||||
};
|
||||
|
||||
const initials = getInitials(name);
|
||||
@ -98,7 +101,7 @@ export const RAGFlowAvatar = memo(
|
||||
'bg-gradient-to-b',
|
||||
`from-[${from}] to-[${to}]`,
|
||||
'flex items-center justify-center',
|
||||
'text-white font-bold',
|
||||
'text-white ',
|
||||
{ 'rounded-md': !isPerson },
|
||||
)}
|
||||
style={{
|
||||
|
||||
@ -49,12 +49,12 @@ export function SliderInputFormField({
|
||||
defaultValue={defaultValue || 0}
|
||||
render={({ field }) => (
|
||||
<FormItem
|
||||
className={cn({ 'flex items-center space-y-0': isHorizontal })}
|
||||
className={cn({ 'flex items-center gap-1 space-y-0': isHorizontal })}
|
||||
>
|
||||
<FormLabel
|
||||
tooltip={tooltip}
|
||||
className={cn({
|
||||
'text-sm text-muted-foreground whitespace-nowrap w-1/4':
|
||||
'text-sm text-muted-foreground whitespace-break-spaces w-1/4':
|
||||
isHorizontal,
|
||||
})}
|
||||
>
|
||||
|
||||
@ -32,3 +32,9 @@ export enum ChatSearchParams {
|
||||
}
|
||||
|
||||
export const EmptyConversationId = 'empty';
|
||||
|
||||
export enum DatasetMetadata {
|
||||
Disabled = 'disabled',
|
||||
Automatic = 'automatic',
|
||||
Manual = 'manual',
|
||||
}
|
||||
|
||||
@ -557,6 +557,15 @@ export const useSelectDerivedMessages = () => {
|
||||
setDerivedMessages([]);
|
||||
}, [setDerivedMessages]);
|
||||
|
||||
const removeAllMessagesExceptFirst = useCallback(() => {
|
||||
setDerivedMessages((list) => {
|
||||
if (list.length <= 1) {
|
||||
return list;
|
||||
}
|
||||
return list.slice(0, 1);
|
||||
});
|
||||
}, [setDerivedMessages]);
|
||||
|
||||
return {
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
@ -571,6 +580,7 @@ export const useSelectDerivedMessages = () => {
|
||||
removeMessagesAfterCurrentMessage,
|
||||
removeAllMessages,
|
||||
scrollToBottom,
|
||||
removeAllMessagesExceptFirst,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -24,13 +24,17 @@ export const useNavigatePage = () => {
|
||||
);
|
||||
|
||||
const navigateToHome = useCallback(() => {
|
||||
navigate(Routes.Home);
|
||||
navigate(Routes.Root);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToProfile = useCallback(() => {
|
||||
navigate(Routes.ProfileSetting);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToOldProfile = useCallback(() => {
|
||||
navigate(Routes.UserSetting);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToChatList = useCallback(() => {
|
||||
navigate(Routes.Chats);
|
||||
}, [navigate]);
|
||||
@ -139,5 +143,6 @@ export const useNavigatePage = () => {
|
||||
navigateToSearch,
|
||||
navigateToFiles,
|
||||
navigateToAgentList,
|
||||
navigateToOldProfile,
|
||||
};
|
||||
};
|
||||
|
||||
@ -40,7 +40,7 @@ export function Header() {
|
||||
const { t } = useTranslation();
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigateWithFromState();
|
||||
const { navigateToProfile } = useNavigatePage();
|
||||
const { navigateToOldProfile } = useNavigatePage();
|
||||
|
||||
const changeLanguage = useChangeLanguage();
|
||||
const { setTheme, theme } = useTheme();
|
||||
@ -74,8 +74,8 @@ export function Header() {
|
||||
|
||||
const tagsData = useMemo(
|
||||
() => [
|
||||
{ path: Routes.Home, name: t('header.home'), icon: House },
|
||||
{ path: Routes.Datasets, name: t('header.knowledgeBase'), icon: Library },
|
||||
{ path: Routes.Root, name: t('header.Root'), icon: House },
|
||||
{ path: Routes.Datasets, name: t('header.dataset'), icon: Library },
|
||||
{ path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText },
|
||||
{ path: Routes.Searches, name: t('header.search'), icon: Search },
|
||||
{ path: Routes.Agents, name: t('header.flow'), icon: Cpu },
|
||||
@ -90,7 +90,7 @@ export function Header() {
|
||||
|
||||
return {
|
||||
label:
|
||||
tag.path === Routes.Home ? (
|
||||
tag.path === Routes.Root ? (
|
||||
<HeaderIcon className="size-6"></HeaderIcon>
|
||||
) : (
|
||||
<span>{tag.name}</span>
|
||||
@ -100,18 +100,18 @@ export function Header() {
|
||||
});
|
||||
}, [tagsData]);
|
||||
|
||||
const currentPath = useMemo(() => {
|
||||
return (
|
||||
tagsData.find((x) => pathname.startsWith(x.path))?.path || Routes.Home
|
||||
);
|
||||
}, [pathname, tagsData]);
|
||||
// const currentPath = useMemo(() => {
|
||||
// return (
|
||||
// tagsData.find((x) => pathname.startsWith(x.path))?.path || Routes.Root
|
||||
// );
|
||||
// }, [pathname, tagsData]);
|
||||
|
||||
const handleChange = (path: SegmentedValue) => {
|
||||
navigate(path as Routes);
|
||||
};
|
||||
|
||||
const handleLogoClick = useCallback(() => {
|
||||
navigate(Routes.Home);
|
||||
navigate(Routes.Root);
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
@ -123,14 +123,19 @@ export function Header() {
|
||||
className="size-10 mr-[12]"
|
||||
onClick={handleLogoClick}
|
||||
/>
|
||||
<div className="flex items-center gap-1.5 text-text-secondary">
|
||||
<Github className="size-3.5" />
|
||||
<span className=" text-base">21.5k stars</span>
|
||||
</div>
|
||||
<a
|
||||
className="flex items-center gap-1.5 text-text-secondary"
|
||||
target="_blank"
|
||||
href="https://github.com/infiniflow/ragflow"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Github className="size-4" />
|
||||
{/* <span className=" text-base">21.5k stars</span> */}
|
||||
</a>
|
||||
</div>
|
||||
<Segmented
|
||||
options={options}
|
||||
value={currentPath}
|
||||
value={pathname}
|
||||
onChange={handleChange}
|
||||
></Segmented>
|
||||
<div className="flex items-center gap-5 text-text-badge">
|
||||
@ -160,7 +165,7 @@ export function Header() {
|
||||
name={nickname}
|
||||
avatar={avatar}
|
||||
className="size-8 cursor-pointer"
|
||||
onClick={navigateToProfile}
|
||||
onClick={navigateToOldProfile}
|
||||
></RAGFlowAvatar>
|
||||
{/* Temporarily hidden */}
|
||||
{/* <Badge className="h-5 w-8 absolute font-normal p-0 justify-center -right-8 -top-2 text-bg-base bg-gradient-to-l from-[#42D7E7] to-[#478AF5]">
|
||||
|
||||
@ -81,6 +81,7 @@ export default {
|
||||
flow: 'Agent',
|
||||
search: 'Search',
|
||||
welcome: 'Welcome to',
|
||||
dataset: 'Dataset',
|
||||
},
|
||||
knowledgeList: {
|
||||
welcome: 'Welcome back',
|
||||
|
||||
@ -73,6 +73,7 @@ export default {
|
||||
flow: 'Agent',
|
||||
search: '搜索',
|
||||
welcome: '欢迎来到',
|
||||
dataset: '数据集',
|
||||
},
|
||||
knowledgeList: {
|
||||
welcome: '欢迎回来',
|
||||
|
||||
@ -3,7 +3,6 @@ import { useCallback } from 'react';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ExeSQLFormSchema = {
|
||||
sql: z.string(),
|
||||
db_type: z.string().min(1),
|
||||
database: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
@ -14,7 +13,7 @@ export const ExeSQLFormSchema = {
|
||||
};
|
||||
|
||||
export const FormSchema = z.object({
|
||||
query: z.string().optional(),
|
||||
sql: z.string().optional(),
|
||||
...ExeSQLFormSchema,
|
||||
});
|
||||
|
||||
|
||||
@ -25,11 +25,13 @@ const ExeSQLForm = () => {
|
||||
defaultValues: defaultValues as FormType,
|
||||
});
|
||||
|
||||
const onError = (error: any) => console.log(error);
|
||||
|
||||
useWatchFormChange(form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<FormWrapper onSubmit={form.handleSubmit(onSubmit, onError)}>
|
||||
<ExeSQLFormWidgets loading={loading}></ExeSQLFormWidgets>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
|
||||
@ -23,8 +23,13 @@ export const useShowFormDrawer = () => {
|
||||
|
||||
const handleShow = useCallback(
|
||||
(e: React.MouseEvent<Element>, nodeId: string) => {
|
||||
const tool = get(e.target, 'dataset.tool');
|
||||
// TODO: Operator type judgment should be used
|
||||
if (nodeId.startsWith(Operator.Tool) && !tool) {
|
||||
return;
|
||||
}
|
||||
setClickedNodeId(nodeId);
|
||||
setClickedToolId(get(e.target, 'dataset.tool'));
|
||||
setClickedToolId(tool);
|
||||
showFormDrawer();
|
||||
},
|
||||
[setClickedNodeId, setClickedToolId, showFormDrawer],
|
||||
|
||||
@ -63,7 +63,7 @@ function AgentDropdownMenuItem({
|
||||
|
||||
export default function Agent() {
|
||||
const { id } = useParams();
|
||||
const { navigateToAgentList } = useNavigatePage();
|
||||
const { navigateToAgents } = useNavigatePage();
|
||||
const {
|
||||
visible: chatDrawerVisible,
|
||||
hideModal: hideChatDrawer,
|
||||
@ -113,7 +113,7 @@ export default function Agent() {
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToAgentList}>
|
||||
<BreadcrumbLink onClick={navigateToAgents}>
|
||||
Agent
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { HomeCard } from '@/components/home-card';
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { SharedBadge } from '@/components/shared-badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { IFlow } from '@/interfaces/database/agent';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { AgentDropdown } from './agent-dropdown';
|
||||
import { useRenameAgent } from './use-rename-agent';
|
||||
|
||||
@ -16,36 +13,16 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) {
|
||||
const { navigateToAgent } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<Card key={data.id} className="w-40" onClick={navigateToAgent(data.id)}>
|
||||
<CardContent className="p-2.5 pt-2 group">
|
||||
<section className="flex justify-between mb-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<RAGFlowAvatar
|
||||
className="size-6 rounded-lg"
|
||||
avatar={data.avatar}
|
||||
name={data.title || 'CN'}
|
||||
></RAGFlowAvatar>
|
||||
<SharedBadge>{data.nickname}</SharedBadge>
|
||||
</div>
|
||||
<AgentDropdown
|
||||
showAgentRenameModal={showAgentRenameModal}
|
||||
agent={data}
|
||||
>
|
||||
<MoreButton></MoreButton>
|
||||
</AgentDropdown>
|
||||
</section>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="w-full">
|
||||
<h3 className="text-lg font-semibold mb-2 line-clamp-1">
|
||||
{data.title}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary">{data.description}</p>
|
||||
<p className="text-xs text-text-secondary">
|
||||
{formatDate(data.update_time)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<HomeCard
|
||||
data={{ ...data, name: data.title, description: data.description || '' }}
|
||||
moreDropdown={
|
||||
<AgentDropdown showAgentRenameModal={showAgentRenameModal} agent={data}>
|
||||
<MoreButton></MoreButton>
|
||||
</AgentDropdown>
|
||||
}
|
||||
onClick={() => {
|
||||
navigateToAgent(data?.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ const getEndOfToday = (): Date => {
|
||||
return today;
|
||||
};
|
||||
const AgentLogPage: React.FC = () => {
|
||||
const { navigateToAgentList, navigateToAgent } = useNavigatePage();
|
||||
const { navigateToAgents, navigateToAgent } = useNavigatePage();
|
||||
const { flowDetail: agentDetail } = useFetchDataOnMount();
|
||||
const { id: canvasId } = useParams();
|
||||
const queryClient = useQueryClient();
|
||||
@ -210,9 +210,7 @@ const AgentLogPage: React.FC = () => {
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToAgentList}>
|
||||
Agent
|
||||
</BreadcrumbLink>
|
||||
<BreadcrumbLink onClick={navigateToAgents}>Agent</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
|
||||
@ -18,7 +18,7 @@ import { TemplateCard } from './template-card';
|
||||
import { MenuItemKey, SideBar } from './template-sidebar';
|
||||
|
||||
export default function AgentTemplates() {
|
||||
const { navigateToAgentList } = useNavigatePage();
|
||||
const { navigateToAgents } = useNavigatePage();
|
||||
const { t } = useTranslation();
|
||||
const list = useFetchAgentTemplates();
|
||||
const { loading, setAgent } = useSetAgent();
|
||||
@ -89,9 +89,7 @@ export default function AgentTemplates() {
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToAgentList}>
|
||||
Agent
|
||||
</BreadcrumbLink>
|
||||
<BreadcrumbLink onClick={navigateToAgents}>Agent</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
|
||||
@ -46,7 +46,7 @@ export default function Agents() {
|
||||
</ListFilterBar>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="flex flex-wrap gap-4 px-8">
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[78vh] overflow-auto px-8">
|
||||
{data.map((x) => {
|
||||
return (
|
||||
<AgentCard
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { HomeCard } from '@/components/home-card';
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { DatasetDropdown } from './dataset-dropdown';
|
||||
import { useDisplayOwnerName } from './use-display-owner';
|
||||
@ -24,47 +22,20 @@ export function DatasetCard({
|
||||
const owner = displayOwnerName(dataset.tenant_id, dataset.nickname);
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={dataset.id}
|
||||
className="w-40"
|
||||
onClick={navigateToDataset(dataset.id)}
|
||||
>
|
||||
<CardContent className="p-2.5 pt-2 group">
|
||||
<section className="flex justify-between mb-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<RAGFlowAvatar
|
||||
className="size-6 rounded-lg"
|
||||
avatar={dataset.avatar}
|
||||
name={dataset.name || 'CN'}
|
||||
></RAGFlowAvatar>
|
||||
{owner && (
|
||||
<Badge className="h-5 rounded-sm px-1 bg-background-badge text-text-badge">
|
||||
{owner}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<DatasetDropdown
|
||||
showDatasetRenameModal={showDatasetRenameModal}
|
||||
dataset={dataset}
|
||||
>
|
||||
<MoreButton></MoreButton>
|
||||
</DatasetDropdown>
|
||||
</section>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="w-full">
|
||||
<h3 className="text-lg font-semibold mb-2 line-clamp-1">
|
||||
{dataset.name}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary">
|
||||
{dataset.doc_num} files
|
||||
</p>
|
||||
<p className="text-xs text-text-secondary">
|
||||
{formatDate(dataset.update_time)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<HomeCard
|
||||
data={{ ...dataset, description: `${dataset.doc_num} files` }}
|
||||
moreDropdown={
|
||||
<DatasetDropdown
|
||||
showDatasetRenameModal={showDatasetRenameModal}
|
||||
dataset={dataset}
|
||||
>
|
||||
<MoreButton></MoreButton>
|
||||
</DatasetDropdown>
|
||||
}
|
||||
onClick={() => {
|
||||
navigateToDataset(dataset.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ export default function Datasets() {
|
||||
</Button>
|
||||
</ListFilterBar>
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-wrap gap-4 max-h-[78vh] overflow-auto px-8">
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[78vh] overflow-auto px-8">
|
||||
{kbs.map((dataset) => {
|
||||
return (
|
||||
<DatasetCard
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchAgentListByPage } from '@/hooks/use-agent-request';
|
||||
import { ApplicationCard } from './application-card';
|
||||
|
||||
export function Agents() {
|
||||
const { data } = useFetchAgentListByPage();
|
||||
const { navigateToAgent } = useNavigatePage();
|
||||
|
||||
return data
|
||||
.slice(0, 10)
|
||||
.map((x) => <ApplicationCard key={x.id} app={x}></ApplicationCard>);
|
||||
.map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={x}
|
||||
onClick={navigateToAgent(x.id)}
|
||||
></ApplicationCard>
|
||||
));
|
||||
}
|
||||
|
||||
@ -10,11 +10,12 @@ type ApplicationCardProps = {
|
||||
title: string;
|
||||
update_time: number;
|
||||
};
|
||||
onClick?(): void;
|
||||
};
|
||||
|
||||
export function ApplicationCard({ app }: ApplicationCardProps) {
|
||||
export function ApplicationCard({ app, onClick }: ApplicationCardProps) {
|
||||
return (
|
||||
<Card className="w-[264px]">
|
||||
<Card className="w-[264px]" onClick={onClick}>
|
||||
<CardContent className="p-2.5 group flex justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<RAGFlowAvatar
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchDialogList } from '@/hooks/use-chat-request';
|
||||
import { ApplicationCard } from './application-card';
|
||||
|
||||
export function ChatList() {
|
||||
const { data } = useFetchDialogList();
|
||||
const { navigateToChat } = useNavigatePage();
|
||||
|
||||
return data.dialogs
|
||||
.slice(0, 10)
|
||||
.map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={{ avatar: x.icon, title: x.name, update_time: x.update_time }}
|
||||
></ApplicationCard>
|
||||
));
|
||||
return data.dialogs.slice(0, 10).map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={{
|
||||
avatar: x.icon,
|
||||
title: x.name,
|
||||
update_time: x.update_time,
|
||||
}}
|
||||
onClick={navigateToChat(x.id)}
|
||||
></ApplicationCard>
|
||||
));
|
||||
}
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchSearchList } from '../next-searches/hooks';
|
||||
import { ApplicationCard } from './application-card';
|
||||
|
||||
export function SearchList() {
|
||||
const { data } = useFetchSearchList();
|
||||
const { navigateToSearch } = useNavigatePage();
|
||||
|
||||
return data?.data.search_apps
|
||||
.slice(0, 10)
|
||||
.map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={{ avatar: x.avatar, title: x.name, update_time: x.update_time }}
|
||||
></ApplicationCard>
|
||||
));
|
||||
return data?.data.search_apps.slice(0, 10).map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={{
|
||||
avatar: x.avatar,
|
||||
title: x.name,
|
||||
update_time: x.update_time,
|
||||
}}
|
||||
onClick={() => navigateToSearch(x.id)}
|
||||
></ApplicationCard>
|
||||
));
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ const Login = () => {
|
||||
const { isLogin } = useAuth();
|
||||
useEffect(() => {
|
||||
if (isLogin) {
|
||||
navigate('/knowledge');
|
||||
navigate('/');
|
||||
}
|
||||
}, [isLogin, navigate]);
|
||||
|
||||
@ -68,7 +68,7 @@ const Login = () => {
|
||||
password: rsaPassWord,
|
||||
});
|
||||
if (code === 0) {
|
||||
navigate('/knowledge');
|
||||
navigate('/');
|
||||
}
|
||||
} else {
|
||||
const code = await register({
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { HomeCard } from '@/components/home-card';
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { IDialog } from '@/interfaces/database/chat';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { ChatDropdown } from './chat-dropdown';
|
||||
import { useRenameChat } from './hooks/use-rename-chat';
|
||||
|
||||
@ -15,32 +13,21 @@ export function ChatCard({ data, showChatRenameModal }: IProps) {
|
||||
const { navigateToChat } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<Card key={data.id} className="w-40" onClick={navigateToChat(data.id)}>
|
||||
<CardContent className="p-2.5 pt-2 group">
|
||||
<section className="flex justify-between mb-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<RAGFlowAvatar
|
||||
className="size-6 rounded-lg"
|
||||
avatar={data.icon}
|
||||
name={data.name || 'CN'}
|
||||
></RAGFlowAvatar>
|
||||
</div>
|
||||
<ChatDropdown chat={data} showChatRenameModal={showChatRenameModal}>
|
||||
<MoreButton></MoreButton>
|
||||
</ChatDropdown>
|
||||
</section>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="w-full">
|
||||
<h3 className="text-lg font-semibold mb-2 line-clamp-1 truncate">
|
||||
{data.name}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary">{data.description}</p>
|
||||
<p className="text-xs text-text-secondary">
|
||||
{formatDate(data.update_time)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<HomeCard
|
||||
data={{
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
avatar: data.icon,
|
||||
update_time: data.update_time,
|
||||
}}
|
||||
moreDropdown={
|
||||
<ChatDropdown chat={data} showChatRenameModal={showChatRenameModal}>
|
||||
<MoreButton></MoreButton>
|
||||
</ChatDropdown>
|
||||
}
|
||||
onClick={() => {
|
||||
navigateToChat(data?.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
|
||||
import { FileUploader } from '@/components/file-uploader';
|
||||
import { KnowledgeBaseFormField } from '@/components/knowledge-base-item';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { MetadataFilter } from '@/components/metadata-filter';
|
||||
import { SwitchFormField } from '@/components/switch-fom-field';
|
||||
import { TavilyFormField } from '@/components/tavily-form-field';
|
||||
import {
|
||||
@ -16,26 +15,11 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { DatasetMetadata } from '../../constants';
|
||||
import { MetadataFilterConditions } from './metadata-filter-conditions';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
export default function ChatBasicSetting() {
|
||||
const { t } = useTranslate('chat');
|
||||
const form = useFormContext();
|
||||
const kbIds: string[] = useWatch({ control: form.control, name: 'kb_ids' });
|
||||
const metadata = useWatch({
|
||||
control: form.control,
|
||||
name: 'meta_data_filter.method',
|
||||
});
|
||||
const hasKnowledge = Array.isArray(kbIds) && kbIds.length > 0;
|
||||
|
||||
const MetadataOptions = Object.values(DatasetMetadata).map((x) => {
|
||||
return {
|
||||
value: x,
|
||||
label: t(`meta.${x}`),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
@ -125,18 +109,7 @@ export default function ChatBasicSetting() {
|
||||
></SwitchFormField>
|
||||
<TavilyFormField></TavilyFormField>
|
||||
<KnowledgeBaseFormField></KnowledgeBaseFormField>
|
||||
{hasKnowledge && (
|
||||
<RAGFlowFormItem
|
||||
label={t('metadata')}
|
||||
name={'meta_data_filter.method'}
|
||||
tooltip={t('metadataTip')}
|
||||
>
|
||||
<SelectWithSearch options={MetadataOptions} />
|
||||
</RAGFlowFormItem>
|
||||
)}
|
||||
{hasKnowledge && metadata === DatasetMetadata.Manual && (
|
||||
<MetadataFilterConditions kbIds={kbIds}></MetadataFilterConditions>
|
||||
)}
|
||||
<MetadataFilter></MetadataFilter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
LlmSettingEnabledSchema,
|
||||
LlmSettingFieldSchema,
|
||||
} from '@/components/llm-setting-items/next';
|
||||
import { MetadataFilterSchema } from '@/components/metadata-filter';
|
||||
import { rerankFormSchema } from '@/components/rerank';
|
||||
import { vectorSimilarityWeightSchema } from '@/components/similarity-slider';
|
||||
import { topnSchema } from '@/components/top-n-item';
|
||||
@ -46,20 +47,7 @@ export function useChatSettingSchema() {
|
||||
llm_id: z.string().optional(),
|
||||
...vectorSimilarityWeightSchema,
|
||||
...topnSchema,
|
||||
meta_data_filter: z
|
||||
.object({
|
||||
method: z.string().optional(),
|
||||
manual: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
op: z.string(),
|
||||
value: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
...MetadataFilterSchema,
|
||||
});
|
||||
|
||||
return formSchema;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { removeUselessFieldsFromValues } from '@/utils/form';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
@ -23,7 +24,7 @@ export function useBuildFormRefs(chatBoxIds: string[]) {
|
||||
? formRefs.current[chatBoxId].getFormData()
|
||||
: {};
|
||||
|
||||
return llmConfig;
|
||||
return removeUselessFieldsFromValues(llmConfig, '');
|
||||
},
|
||||
[formRefs],
|
||||
);
|
||||
|
||||
@ -1,32 +1,9 @@
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useSetDialog } from '@/hooks/use-chat-request';
|
||||
import { IDialog } from '@/interfaces/database/chat';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
const InitialData = {
|
||||
name: '',
|
||||
icon: '',
|
||||
language: 'English',
|
||||
prompt_config: {
|
||||
empty_response: '',
|
||||
prologue: '你好! 我是你的助理,有什么可以帮到你的吗?',
|
||||
quote: true,
|
||||
keyword: false,
|
||||
tts: false,
|
||||
system:
|
||||
'你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。\n 以下是知识库:\n {knowledge}\n 以上是知识库。',
|
||||
refine_multiturn: false,
|
||||
use_kg: false,
|
||||
reasoning: false,
|
||||
parameters: [{ key: 'knowledge', optional: false }],
|
||||
},
|
||||
llm_id: '',
|
||||
llm_setting: {},
|
||||
similarity_threshold: 0.2,
|
||||
vector_similarity_weight: 0.30000000000000004,
|
||||
top_n: 8,
|
||||
};
|
||||
import { isEmpty, omit } from 'lodash';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const useRenameChat = () => {
|
||||
const [chat, setChat] = useState<IDialog>({} as IDialog);
|
||||
@ -36,11 +13,40 @@ export const useRenameChat = () => {
|
||||
showModal: showChatRenameModal,
|
||||
} = useSetModalState();
|
||||
const { setDialog, loading } = useSetDialog();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const InitialData = useMemo(
|
||||
() => ({
|
||||
name: '',
|
||||
icon: '',
|
||||
language: 'English',
|
||||
prompt_config: {
|
||||
empty_response: '',
|
||||
prologue: t('chat.setAnOpenerInitial'),
|
||||
quote: true,
|
||||
keyword: false,
|
||||
tts: false,
|
||||
system: t('chat.systemInitialValue'),
|
||||
refine_multiturn: false,
|
||||
use_kg: false,
|
||||
reasoning: false,
|
||||
parameters: [{ key: 'knowledge', optional: false }],
|
||||
},
|
||||
llm_id: '',
|
||||
llm_setting: {},
|
||||
similarity_threshold: 0.2,
|
||||
vector_similarity_weight: 0.30000000000000004,
|
||||
top_n: 8,
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
|
||||
const onChatRenameOk = useCallback(
|
||||
async (name: string) => {
|
||||
const nextChat = {
|
||||
...(isEmpty(chat) ? InitialData : chat),
|
||||
...(isEmpty(chat)
|
||||
? InitialData
|
||||
: { ...omit(chat, 'nickname', 'tenant_avatar'), dialog_id: chat.id }),
|
||||
name,
|
||||
};
|
||||
const ret = await setDialog(nextChat);
|
||||
@ -49,7 +55,7 @@ export const useRenameChat = () => {
|
||||
hideChatRenameModal();
|
||||
}
|
||||
},
|
||||
[setDialog, chat, hideChatRenameModal],
|
||||
[chat, InitialData, setDialog, hideChatRenameModal],
|
||||
);
|
||||
|
||||
const handleShowChatRenameModal = useCallback(
|
||||
|
||||
@ -60,6 +60,7 @@ export const useSendSharedMessage = () => {
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
removeAllMessagesExceptFirst,
|
||||
} = useSelectDerivedMessages();
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
@ -149,5 +150,6 @@ export const useSendSharedMessage = () => {
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
removeAllMessagesExceptFirst,
|
||||
};
|
||||
};
|
||||
|
||||
@ -49,7 +49,7 @@ export default function ChatList() {
|
||||
</ListFilterBar>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="flex flex-wrap gap-4 px-8">
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[84vh] overflow-auto px-8">
|
||||
{data.dialogs.map((x) => {
|
||||
return (
|
||||
<ChatCard
|
||||
|
||||
@ -37,7 +37,7 @@ const ChatContainer = () => {
|
||||
stopOutputMessage,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
removeAllMessagesExceptFirst,
|
||||
} = useSendSharedMessage();
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const { data: chatInfo } = useFetchExternalChatInfo();
|
||||
@ -63,7 +63,7 @@ const ChatContainer = () => {
|
||||
<EmbedContainer
|
||||
title={chatInfo.title}
|
||||
avatar={chatInfo.avatar}
|
||||
handleReset={removeAllMessages}
|
||||
handleReset={removeAllMessagesExceptFirst}
|
||||
>
|
||||
<div className="flex flex-1 flex-col p-2.5 h-[90vh] m-3">
|
||||
<div
|
||||
|
||||
@ -288,20 +288,23 @@ export const useSendQuestion = (
|
||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||
|
||||
const sendQuestion = useCallback(
|
||||
(question: string) => {
|
||||
(question: string, enableAI: boolean = true) => {
|
||||
const q = trim(question);
|
||||
if (isEmpty(q)) return;
|
||||
setPagination({ page: 1 });
|
||||
setIsFirstRender(false);
|
||||
setCurrentAnswer({} as IAnswer);
|
||||
setSendingLoading(true);
|
||||
send({ kb_ids: kbIds, question: q, tenantId, search_id: searchId });
|
||||
if (enableAI) {
|
||||
setSendingLoading(true);
|
||||
send({ kb_ids: kbIds, question: q, tenantId, search_id: searchId });
|
||||
}
|
||||
testChunk({
|
||||
kb_id: kbIds,
|
||||
highlight: true,
|
||||
question: q,
|
||||
page: 1,
|
||||
size: pagination.pageSize,
|
||||
search_id: searchId,
|
||||
});
|
||||
|
||||
if (related_search) {
|
||||
@ -327,12 +330,13 @@ export const useSendQuestion = (
|
||||
}, []);
|
||||
|
||||
const handleClickRelatedQuestion = useCallback(
|
||||
(question: string) => () => {
|
||||
if (sendingLoading) return;
|
||||
(question: string, enableAI: boolean = true) =>
|
||||
() => {
|
||||
if (sendingLoading) return;
|
||||
|
||||
setSearchStr(question);
|
||||
sendQuestion(question);
|
||||
},
|
||||
setSearchStr(question);
|
||||
sendQuestion(question, enableAI);
|
||||
},
|
||||
[sendQuestion, sendingLoading],
|
||||
);
|
||||
|
||||
@ -348,6 +352,7 @@ export const useSendQuestion = (
|
||||
doc_ids: documentIds ?? selectedDocumentIds,
|
||||
page,
|
||||
size,
|
||||
search_id: searchId,
|
||||
});
|
||||
|
||||
testChunkAll({
|
||||
@ -357,6 +362,7 @@ export const useSendQuestion = (
|
||||
doc_ids: [],
|
||||
page,
|
||||
size,
|
||||
search_id: searchId,
|
||||
});
|
||||
},
|
||||
[
|
||||
@ -366,6 +372,7 @@ export const useSendQuestion = (
|
||||
kbIds,
|
||||
selectedDocumentIds,
|
||||
testChunkAll,
|
||||
searchId,
|
||||
],
|
||||
);
|
||||
|
||||
@ -430,7 +437,6 @@ export const useSearching = ({
|
||||
|
||||
const handleSearchStrChange = useCallback(
|
||||
(value: string) => {
|
||||
console.log('handleSearchStrChange', value);
|
||||
setSearchStr(value);
|
||||
},
|
||||
[setSearchStr],
|
||||
@ -442,10 +448,16 @@ export const useSearching = ({
|
||||
useEffect(() => {
|
||||
if (searchText) {
|
||||
setSearchStr(searchText);
|
||||
sendQuestion(searchText);
|
||||
sendQuestion(searchText, searchData.search_config.summary);
|
||||
setSearchText?.('');
|
||||
}
|
||||
}, [searchText, sendQuestion, setSearchStr, setSearchText]);
|
||||
}, [
|
||||
searchText,
|
||||
sendQuestion,
|
||||
setSearchStr,
|
||||
setSearchText,
|
||||
searchData.search_config.summary,
|
||||
]);
|
||||
|
||||
const {
|
||||
mindMapVisible,
|
||||
@ -462,11 +474,16 @@ export const useSearching = ({
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(value: string) => {
|
||||
sendQuestion(value);
|
||||
sendQuestion(value, searchData.search_config.summary);
|
||||
setSearchStr?.(value);
|
||||
hideMindMapModal();
|
||||
},
|
||||
[setSearchStr, sendQuestion, hideMindMapModal],
|
||||
[
|
||||
setSearchStr,
|
||||
sendQuestion,
|
||||
hideMindMapModal,
|
||||
searchData.search_config.summary,
|
||||
],
|
||||
);
|
||||
|
||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||
|
||||
@ -126,7 +126,7 @@ export default function SearchPage() {
|
||||
// ></EmbedDialog>
|
||||
}
|
||||
</div>
|
||||
<div className="absolute right-5 top-12 ">
|
||||
<div className="absolute right-5 top-4 ">
|
||||
<Button
|
||||
className="bg-text-primary text-bg-base border-b-[#00BEB4] border-b-2"
|
||||
onClick={() => {
|
||||
|
||||
@ -30,18 +30,20 @@ interface LlmSettingFieldItemsProps {
|
||||
prefix?: string;
|
||||
options?: any[];
|
||||
}
|
||||
|
||||
export const LlmSettingSchema = {
|
||||
llm_id: z.string(),
|
||||
parameter: z.string(),
|
||||
temperature: z.coerce.number(),
|
||||
top_p: z.coerce.number(),
|
||||
presence_penalty: z.coerce.number(),
|
||||
frequency_penalty: z.coerce.number(),
|
||||
const LlmSettingEnableSchema = {
|
||||
temperatureEnabled: z.boolean(),
|
||||
topPEnabled: z.boolean(),
|
||||
presencePenaltyEnabled: z.boolean(),
|
||||
frequencyPenaltyEnabled: z.boolean(),
|
||||
};
|
||||
export const LlmSettingSchema = {
|
||||
llm_id: z.string(),
|
||||
parameter: z.string().optional(),
|
||||
temperature: z.coerce.number().optional(),
|
||||
top_p: z.coerce.number().optional(),
|
||||
presence_penalty: z.coerce.number().optional(),
|
||||
frequency_penalty: z.coerce.number().optional(),
|
||||
...LlmSettingEnableSchema,
|
||||
// maxTokensEnabled: z.boolean(),
|
||||
};
|
||||
|
||||
@ -65,6 +67,7 @@ export function LlmSettingFieldItems({
|
||||
settledModelVariableMap[
|
||||
parameter as keyof typeof settledModelVariableMap
|
||||
];
|
||||
const enabledKeys = Object.keys(LlmSettingEnableSchema);
|
||||
|
||||
// const nextValues = { ...currentValues, ...values };
|
||||
|
||||
@ -75,6 +78,11 @@ export function LlmSettingFieldItems({
|
||||
form.setValue(`${prefix}.${key}`, element);
|
||||
}
|
||||
}
|
||||
if (enabledKeys && enabledKeys.length) {
|
||||
for (const key of enabledKeys) {
|
||||
form.setValue(`${prefix}.${key}`, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
[form, prefix],
|
||||
);
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
// src/pages/next-search/search-setting.tsx
|
||||
|
||||
import {
|
||||
MetadataFilter,
|
||||
MetadataFilterSchema,
|
||||
} from '@/components/metadata-filter';
|
||||
import { Input } from '@/components/originui/input';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -34,11 +38,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
LlmModelType,
|
||||
ModelVariableType,
|
||||
settledModelVariableMap,
|
||||
} from '../dataset/dataset/constant';
|
||||
import { LlmModelType } from '../dataset/dataset/constant';
|
||||
import {
|
||||
ISearchAppDetailProps,
|
||||
IUpdateSearchProps,
|
||||
@ -76,6 +76,7 @@ const SearchSettingFormSchema = z
|
||||
llm_setting: z.object(LlmSettingSchema),
|
||||
related_search: z.boolean(),
|
||||
query_mindmap: z.boolean(),
|
||||
...MetadataFilterSchema,
|
||||
}),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
@ -138,21 +139,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
chat_id: '',
|
||||
llm_setting: {
|
||||
llm_id: llm_setting?.llm_id || '',
|
||||
parameter: llm_setting?.parameter || ModelVariableType.Improvise,
|
||||
temperature:
|
||||
llm_setting?.temperature ||
|
||||
settledModelVariableMap[ModelVariableType.Improvise].temperature,
|
||||
top_p:
|
||||
llm_setting?.top_p ||
|
||||
settledModelVariableMap[ModelVariableType.Improvise].top_p,
|
||||
frequency_penalty:
|
||||
llm_setting?.frequency_penalty ||
|
||||
settledModelVariableMap[ModelVariableType.Improvise]
|
||||
.frequency_penalty,
|
||||
presence_penalty:
|
||||
llm_setting?.presence_penalty ||
|
||||
settledModelVariableMap[ModelVariableType.Improvise]
|
||||
.presence_penalty,
|
||||
parameter: llm_setting?.parameter,
|
||||
temperature: llm_setting?.temperature,
|
||||
top_p: llm_setting?.top_p,
|
||||
frequency_penalty: llm_setting?.frequency_penalty,
|
||||
presence_penalty: llm_setting?.presence_penalty,
|
||||
temperatureEnabled: llm_setting?.temperature ? true : false,
|
||||
topPEnabled: llm_setting?.top_p ? true : false,
|
||||
presencePenaltyEnabled: llm_setting?.presence_penalty ? true : false,
|
||||
@ -165,6 +156,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
keyword: false,
|
||||
related_search: search_config?.related_search || false,
|
||||
query_mindmap: search_config?.query_mindmap || false,
|
||||
meta_data_filter: search_config?.meta_data_filter,
|
||||
},
|
||||
});
|
||||
}, [data, search_config, llm_setting, formMethods]);
|
||||
@ -255,8 +247,13 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
) => {
|
||||
try {
|
||||
const { search_config, ...other_formdata } = formData;
|
||||
const { llm_setting, vector_similarity_weight, ...other_config } =
|
||||
search_config;
|
||||
const {
|
||||
llm_setting,
|
||||
vector_similarity_weight,
|
||||
use_rerank,
|
||||
rerank_id,
|
||||
...other_config
|
||||
} = search_config;
|
||||
const llmSetting = {
|
||||
llm_id: llm_setting.llm_id,
|
||||
parameter: llm_setting.parameter,
|
||||
@ -283,6 +280,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
search_config: {
|
||||
...other_config,
|
||||
vector_similarity_weight: 1 - vector_similarity_weight,
|
||||
rerank_id: use_rerank ? rerank_id : '',
|
||||
llm_setting: { ...llmSetting },
|
||||
},
|
||||
tenant_id: systemSetting.tenant_id,
|
||||
@ -296,7 +294,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-primary border p-4 rounded-lg',
|
||||
'text-text-primary border p-4 pb-12 rounded-lg',
|
||||
{
|
||||
'animate-fade-in-right': open,
|
||||
'animate-fade-out-right': !open,
|
||||
@ -346,7 +344,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Avatar */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -409,7 +406,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Description */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -437,7 +433,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Datasets */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -467,6 +462,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<MetadataFilter prefix="search_config."></MetadataFilter>
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
name="search_config.similarity_threshold"
|
||||
@ -541,7 +537,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Rerank Model */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -617,7 +612,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* AI Summary */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -634,14 +628,12 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{aiSummaryDisabled && (
|
||||
<LlmSettingFieldItems
|
||||
prefix="search_config.llm_setting"
|
||||
options={aiSummeryModelOptions}
|
||||
></LlmSettingFieldItems>
|
||||
)}
|
||||
|
||||
{/* Feature Controls */}
|
||||
{/* <FormField
|
||||
control={formMethods.control}
|
||||
@ -674,7 +666,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
name="search_config.query_mindmap"
|
||||
@ -692,7 +683,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
/>
|
||||
{/* Submit Button */}
|
||||
<div className="flex justify-end"></div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<div className="flex justify-end gap-2 absolute bottom-1 right-3 bg-bg-base w-[calc(100%-1em)] py-2">
|
||||
<Button
|
||||
type="reset"
|
||||
variant={'transparent'}
|
||||
|
||||
@ -252,7 +252,10 @@ export default function SearchingView({
|
||||
key={idx}
|
||||
variant="transparent"
|
||||
className="bg-bg-card text-text-secondary"
|
||||
onClick={handleClickRelatedQuestion(x)}
|
||||
onClick={handleClickRelatedQuestion(
|
||||
x,
|
||||
searchData.search_config.summary,
|
||||
)}
|
||||
>
|
||||
{x}
|
||||
</Button>
|
||||
|
||||
@ -89,7 +89,10 @@ export const useFetchSearchList = (params?: SearchListParams) => {
|
||||
...params,
|
||||
});
|
||||
|
||||
const { data, isLoading, isError } = useQuery<SearchListResponse, Error>({
|
||||
const { data, isLoading, isError, refetch } = useQuery<
|
||||
SearchListResponse,
|
||||
Error
|
||||
>({
|
||||
queryKey: ['searchList', searchParams],
|
||||
queryFn: async () => {
|
||||
const { data: response } =
|
||||
@ -108,7 +111,14 @@ export const useFetchSearchList = (params?: SearchListParams) => {
|
||||
}));
|
||||
};
|
||||
|
||||
return { data, isLoading, isError, searchParams, setSearchListParams };
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
searchParams,
|
||||
setSearchListParams,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
interface DeleteSearchProps {
|
||||
@ -150,6 +160,7 @@ export interface ISearchAppDetailProps {
|
||||
query_mindmap: boolean;
|
||||
related_search: boolean;
|
||||
rerank_id: string;
|
||||
use_rerank?: boolean;
|
||||
similarity_threshold: number;
|
||||
summary: boolean;
|
||||
llm_setting: IllmSettingProps & IllmSettingEnableProps;
|
||||
@ -158,6 +169,10 @@ export interface ISearchAppDetailProps {
|
||||
vector_similarity_weight: number;
|
||||
web_search: boolean;
|
||||
chat_settingcross_languages: string[];
|
||||
meta_data_filter?: {
|
||||
method: string;
|
||||
manual: { key: string; op: string; value: string }[];
|
||||
};
|
||||
};
|
||||
tenant_id: string;
|
||||
update_time: number;
|
||||
@ -187,6 +202,7 @@ export const useFetchSearchDetail = (tenantId?: string) => {
|
||||
const fetchSearchDetailFunc = shared_id
|
||||
? searchService.getSearchDetailShare
|
||||
: searchService.getSearchDetail;
|
||||
|
||||
const { data, isLoading, isError } = useQuery<SearchDetailResponse, Error>({
|
||||
queryKey: ['searchDetail', searchId],
|
||||
enabled: !shared_id || !!tenantId,
|
||||
|
||||
@ -13,13 +13,20 @@ import { Modal } from '@/components/ui/modal/modal';
|
||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import searchService from '@/services/search-service';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { pick } from 'lodash';
|
||||
import { Plus, Search } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { useCreateSearch, useFetchSearchList } from './hooks';
|
||||
import {
|
||||
ISearchAppProps,
|
||||
IUpdateSearchProps,
|
||||
useCreateSearch,
|
||||
useFetchSearchList,
|
||||
useUpdateSearch,
|
||||
} from './hooks';
|
||||
import { SearchCard } from './search-card';
|
||||
const searchFormSchema = z.object({
|
||||
name: z.string().min(1, {
|
||||
@ -27,16 +34,22 @@ const searchFormSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
type SearchFormValues = z.infer<typeof searchFormSchema>;
|
||||
type SearchFormValues = z.infer<typeof searchFormSchema> & {
|
||||
search_id?: string;
|
||||
};
|
||||
|
||||
export default function SearchList() {
|
||||
// const { data } = useFetchFlowList();
|
||||
const { t } = useTranslate('search');
|
||||
const { navigateToSearch } = useNavigatePage();
|
||||
const { isLoading, createSearch } = useCreateSearch();
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
const [searchData, setSearchData] = useState<ISearchAppProps | null>(null);
|
||||
const {
|
||||
data: list,
|
||||
searchParams,
|
||||
setSearchListParams,
|
||||
refetch: refetchList,
|
||||
} = useFetchSearchList();
|
||||
const [openCreateModal, setOpenCreateModal] = useState(false);
|
||||
const form = useForm<SearchFormValues>({
|
||||
@ -48,10 +61,30 @@ export default function SearchList() {
|
||||
const handleSearchChange = (value: string) => {
|
||||
console.log(value);
|
||||
};
|
||||
|
||||
const { updateSearch } = useUpdateSearch();
|
||||
const onSubmit = async (values: SearchFormValues) => {
|
||||
const res = await createSearch({ name: values.name });
|
||||
if (res) {
|
||||
let res;
|
||||
if (isEdit) {
|
||||
try {
|
||||
const reponse = await searchService.getSearchDetail({
|
||||
search_id: searchData?.id,
|
||||
});
|
||||
const detail = reponse.data?.data;
|
||||
console.log('detail-->', detail);
|
||||
const { id, created_by, update_time, ...searchDataTemp } = detail;
|
||||
res = await updateSearch({
|
||||
...searchDataTemp,
|
||||
name: values.name,
|
||||
search_id: searchData?.id,
|
||||
} as unknown as IUpdateSearchProps);
|
||||
refetchList();
|
||||
} catch (e) {
|
||||
console.error('error', e);
|
||||
}
|
||||
} else {
|
||||
res = await createSearch({ name: values.name });
|
||||
}
|
||||
if (res && !searchData?.id) {
|
||||
navigateToSearch(res?.search_id);
|
||||
}
|
||||
if (!isLoading) {
|
||||
@ -60,11 +93,23 @@ export default function SearchList() {
|
||||
form.reset({ name: '' });
|
||||
};
|
||||
const openCreateModalFun = () => {
|
||||
setIsEdit(false);
|
||||
setOpenCreateModal(true);
|
||||
};
|
||||
const handlePageChange = (page: number, pageSize: number) => {
|
||||
setIsEdit(false);
|
||||
setSearchListParams({ ...searchParams, page, page_size: pageSize });
|
||||
};
|
||||
|
||||
const showSearchRenameModal = (data: ISearchAppProps) => {
|
||||
form.setValue('name', data.name);
|
||||
if (data.id) {
|
||||
setIsEdit(true);
|
||||
}
|
||||
|
||||
setSearchData(data);
|
||||
setOpenCreateModal(true);
|
||||
};
|
||||
return (
|
||||
<section className="w-full h-full flex flex-col">
|
||||
<div className="px-8 pt-8">
|
||||
@ -92,7 +137,13 @@ export default function SearchList() {
|
||||
<div className="flex-1">
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[84vh] overflow-auto px-8">
|
||||
{list?.data.search_apps.map((x) => {
|
||||
return <SearchCard key={x.id} data={x}></SearchCard>;
|
||||
return (
|
||||
<SearchCard
|
||||
key={x.id}
|
||||
data={x}
|
||||
showSearchRenameModal={showSearchRenameModal}
|
||||
></SearchCard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,52 +1,30 @@
|
||||
import { HomeCard } from '@/components/home-card';
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { ISearchAppProps } from './hooks';
|
||||
import { SearchDropdown } from './search-dropdown';
|
||||
|
||||
interface IProps {
|
||||
data: ISearchAppProps;
|
||||
showSearchRenameModal: (data: ISearchAppProps) => void;
|
||||
}
|
||||
export function SearchCard({ data }: IProps) {
|
||||
export function SearchCard({ data, showSearchRenameModal }: IProps) {
|
||||
const { navigateToSearch } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="bg-bg-card border-colors-outline-neutral-standard"
|
||||
<HomeCard
|
||||
data={data}
|
||||
moreDropdown={
|
||||
<SearchDropdown
|
||||
dataset={data}
|
||||
showSearchRenameModal={showSearchRenameModal}
|
||||
>
|
||||
<MoreButton></MoreButton>
|
||||
</SearchDropdown>
|
||||
}
|
||||
onClick={() => {
|
||||
navigateToSearch(data?.id);
|
||||
}}
|
||||
>
|
||||
<CardContent className="p-4 flex gap-2 items-start group h-full">
|
||||
<div className="flex justify-between mb-4">
|
||||
<RAGFlowAvatar
|
||||
className="w-[32px] h-[32px]"
|
||||
avatar={data.avatar}
|
||||
name={data.name}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between gap-1 flex-1 h-full">
|
||||
<section className="flex justify-between">
|
||||
<div className="text-[20px] font-bold w-80% leading-5">
|
||||
{data.name}
|
||||
</div>
|
||||
<SearchDropdown dataset={data}>
|
||||
<MoreButton></MoreButton>
|
||||
</SearchDropdown>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-1 mt-1">
|
||||
<div>{data.description}</div>
|
||||
<div>
|
||||
<p className="text-sm opacity-80">
|
||||
{formatDate(data.update_time)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,9 +3,10 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { PenLine, Trash2 } from 'lucide-react';
|
||||
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ISearchAppProps, useDeleteSearch } from './hooks';
|
||||
@ -13,12 +14,21 @@ import { ISearchAppProps, useDeleteSearch } from './hooks';
|
||||
export function SearchDropdown({
|
||||
children,
|
||||
dataset,
|
||||
showSearchRenameModal,
|
||||
}: PropsWithChildren & {
|
||||
dataset: ISearchAppProps;
|
||||
showSearchRenameModal: (dataset: ISearchAppProps) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { deleteSearch } = useDeleteSearch();
|
||||
|
||||
const handleShowChatRenameModal: MouseEventHandler<HTMLDivElement> =
|
||||
useCallback(
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
showSearchRenameModal(dataset);
|
||||
},
|
||||
[dataset, showSearchRenameModal],
|
||||
);
|
||||
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
|
||||
deleteSearch({ search_id: dataset.id });
|
||||
}, [dataset.id, deleteSearch]);
|
||||
@ -27,10 +37,10 @@ export function SearchDropdown({
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{/* <DropdownMenuItem onClick={handleShowDatasetRenameModal}>
|
||||
<DropdownMenuItem onClick={handleShowChatRenameModal}>
|
||||
{t('common.rename')} <PenLine />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator /> */}
|
||||
<DropdownMenuSeparator />
|
||||
<ConfirmDeleteDialog onOk={handleDelete}>
|
||||
<DropdownMenuItem
|
||||
className="text-text-delete-red"
|
||||
|
||||
@ -2,16 +2,49 @@ import { Flex } from 'antd';
|
||||
import { Outlet } from 'umi';
|
||||
import SideBar from './sidebar';
|
||||
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { House } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './index.less';
|
||||
|
||||
const UserSetting = () => {
|
||||
const { t } = useTranslation();
|
||||
const { navigateToHome } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<Flex className={styles.settingWrapper}>
|
||||
<SideBar></SideBar>
|
||||
<Flex flex={1} className={styles.outletWrapper}>
|
||||
<Outlet></Outlet>
|
||||
<section>
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToHome}>
|
||||
<House className="size-4" />
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{t('setting.profile')}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<Flex className={cn(styles.settingWrapper, '-translate-y-6')}>
|
||||
<SideBar></SideBar>
|
||||
<Flex flex={1} className={styles.outletWrapper}>
|
||||
<Outlet></Outlet>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export enum Routes {
|
||||
Root = '/',
|
||||
Login = '/login',
|
||||
Logout = '/logout',
|
||||
Home = '/home',
|
||||
@ -40,6 +41,7 @@ export enum Routes {
|
||||
AgentLogPage = '/agent-log-page',
|
||||
AgentShare = '/agent/share',
|
||||
ChatShare = `${Chats}/share`,
|
||||
UserSetting = '/user-setting',
|
||||
}
|
||||
|
||||
const routes = [
|
||||
@ -68,116 +70,116 @@ const routes = [
|
||||
component: `@/pages${Routes.AgentShare}`,
|
||||
layout: false,
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: '@/layouts',
|
||||
layout: false,
|
||||
wrappers: ['@/wrappers/auth'],
|
||||
routes: [
|
||||
{ path: '/', redirect: '/knowledge' },
|
||||
{
|
||||
path: '/knowledge',
|
||||
component: '@/pages/knowledge',
|
||||
// component: '@/pages/knowledge/datasets',
|
||||
},
|
||||
{
|
||||
path: '/knowledge',
|
||||
component: '@/pages/add-knowledge',
|
||||
routes: [
|
||||
{
|
||||
path: '/knowledge/dataset',
|
||||
component: '@/pages/add-knowledge/components/knowledge-dataset',
|
||||
routes: [
|
||||
{
|
||||
path: '/knowledge/dataset',
|
||||
component: '@/pages/add-knowledge/components/knowledge-file',
|
||||
},
|
||||
{
|
||||
path: '/knowledge/dataset/chunk',
|
||||
component: '@/pages/add-knowledge/components/knowledge-chunk',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/knowledge/configuration',
|
||||
component: '@/pages/add-knowledge/components/knowledge-setting',
|
||||
},
|
||||
{
|
||||
path: '/knowledge/testing',
|
||||
component: '@/pages/add-knowledge/components/knowledge-testing',
|
||||
},
|
||||
{
|
||||
path: '/knowledge/knowledgeGraph',
|
||||
component: '@/pages/add-knowledge/components/knowledge-graph',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/chat',
|
||||
component: '@/pages/chat',
|
||||
},
|
||||
{
|
||||
path: '/user-setting',
|
||||
component: '@/pages/user-setting',
|
||||
routes: [
|
||||
{ path: '/user-setting', redirect: '/user-setting/profile' },
|
||||
{
|
||||
path: '/user-setting/profile',
|
||||
// component: '@/pages/user-setting/setting-profile',
|
||||
component: '@/pages/user-setting/setting-profile',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/locale',
|
||||
component: '@/pages/user-setting/setting-locale',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/password',
|
||||
component: '@/pages/user-setting/setting-password',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/model',
|
||||
component: '@/pages/user-setting/setting-model',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/team',
|
||||
component: '@/pages/user-setting/setting-team',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/system',
|
||||
component: '@/pages/user-setting/setting-system',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/api',
|
||||
component: '@/pages/user-setting/setting-api',
|
||||
},
|
||||
{
|
||||
path: `/user-setting${Routes.Mcp}`,
|
||||
component: `@/pages${Routes.ProfileMcp}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/file',
|
||||
component: '@/pages/file-manager',
|
||||
},
|
||||
{
|
||||
path: '/flow',
|
||||
component: '@/pages/flow/list',
|
||||
},
|
||||
{
|
||||
path: Routes.AgentList,
|
||||
component: `@/pages/${Routes.Agents}`,
|
||||
},
|
||||
{
|
||||
path: '/flow/:id',
|
||||
component: '@/pages/flow',
|
||||
},
|
||||
{
|
||||
path: '/search',
|
||||
component: '@/pages/search',
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/',
|
||||
// component: '@/layouts',
|
||||
// layout: false,
|
||||
// wrappers: ['@/wrappers/auth'],
|
||||
// routes: [
|
||||
// { path: '/', redirect: '/knowledge' },
|
||||
// {
|
||||
// path: '/knowledge',
|
||||
// component: '@/pages/knowledge',
|
||||
// // component: '@/pages/knowledge/datasets',
|
||||
// },
|
||||
// {
|
||||
// path: '/knowledge',
|
||||
// component: '@/pages/add-knowledge',
|
||||
// routes: [
|
||||
// {
|
||||
// path: '/knowledge/dataset',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-dataset',
|
||||
// routes: [
|
||||
// {
|
||||
// path: '/knowledge/dataset',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-file',
|
||||
// },
|
||||
// {
|
||||
// path: '/knowledge/dataset/chunk',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-chunk',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/knowledge/configuration',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-setting',
|
||||
// },
|
||||
// {
|
||||
// path: '/knowledge/testing',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-testing',
|
||||
// },
|
||||
// {
|
||||
// path: '/knowledge/knowledgeGraph',
|
||||
// component: '@/pages/add-knowledge/components/knowledge-graph',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/chat',
|
||||
// component: '@/pages/chat',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting',
|
||||
// component: '@/pages/user-setting',
|
||||
// routes: [
|
||||
// { path: '/user-setting', redirect: '/user-setting/profile' },
|
||||
// {
|
||||
// path: '/user-setting/profile',
|
||||
// // component: '@/pages/user-setting/setting-profile',
|
||||
// component: '@/pages/user-setting/setting-profile',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/locale',
|
||||
// component: '@/pages/user-setting/setting-locale',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/password',
|
||||
// component: '@/pages/user-setting/setting-password',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/model',
|
||||
// component: '@/pages/user-setting/setting-model',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/team',
|
||||
// component: '@/pages/user-setting/setting-team',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/system',
|
||||
// component: '@/pages/user-setting/setting-system',
|
||||
// },
|
||||
// {
|
||||
// path: '/user-setting/api',
|
||||
// component: '@/pages/user-setting/setting-api',
|
||||
// },
|
||||
// {
|
||||
// path: `/user-setting${Routes.Mcp}`,
|
||||
// component: `@/pages${Routes.ProfileMcp}`,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/file',
|
||||
// component: '@/pages/file-manager',
|
||||
// },
|
||||
// {
|
||||
// path: '/flow',
|
||||
// component: '@/pages/flow/list',
|
||||
// },
|
||||
// {
|
||||
// path: Routes.AgentList,
|
||||
// component: `@/pages/${Routes.Agents}`,
|
||||
// },
|
||||
// {
|
||||
// path: '/flow/:id',
|
||||
// component: '@/pages/flow',
|
||||
// },
|
||||
// {
|
||||
// path: '/search',
|
||||
// component: '@/pages/search',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: '/document/:id',
|
||||
component: '@/pages/document-viewer',
|
||||
@ -189,12 +191,12 @@ const routes = [
|
||||
layout: false,
|
||||
},
|
||||
{
|
||||
path: Routes.Home,
|
||||
path: Routes.Root,
|
||||
layout: false,
|
||||
component: '@/layouts/next',
|
||||
routes: [
|
||||
{
|
||||
path: Routes.Home,
|
||||
path: Routes.Root,
|
||||
component: `@/pages${Routes.Home}`,
|
||||
},
|
||||
],
|
||||
@ -382,6 +384,47 @@ const routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/user-setting',
|
||||
component: '@/pages/user-setting',
|
||||
layout: false,
|
||||
routes: [
|
||||
{ path: '/user-setting', redirect: '/user-setting/profile' },
|
||||
{
|
||||
path: '/user-setting/profile',
|
||||
// component: '@/pages/user-setting/setting-profile',
|
||||
component: '@/pages/user-setting/setting-profile',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/locale',
|
||||
component: '@/pages/user-setting/setting-locale',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/password',
|
||||
component: '@/pages/user-setting/setting-password',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/model',
|
||||
component: '@/pages/user-setting/setting-model',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/team',
|
||||
component: '@/pages/user-setting/setting-team',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/system',
|
||||
component: '@/pages/user-setting/setting-system',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/api',
|
||||
component: '@/pages/user-setting/setting-api',
|
||||
},
|
||||
{
|
||||
path: `/user-setting${Routes.Mcp}`,
|
||||
component: `@/pages${Routes.ProfileMcp}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
||||
Reference in New Issue
Block a user