mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 044daff668 | |||
| 03f8b01b3b | |||
| ad6f0a1ce5 | |||
| b3843138f4 | |||
| e0bdcbbeba | |||
| 582340a184 | |||
| 890561703b | |||
| a7be5d4e8b | |||
| c344486aa0 | |||
| 111501af5e | |||
| 9e75bd4d88 |
40
Dockerfile
40
Dockerfile
@ -1,20 +1,20 @@
|
|||||||
FROM swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow-base:v1.0
|
FROM swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow-base:v1.0
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
WORKDIR /ragflow
|
WORKDIR /ragflow
|
||||||
|
|
||||||
ADD ./web ./web
|
ADD ./web ./web
|
||||||
RUN cd ./web && npm i && npm run build
|
RUN cd ./web && npm i && npm run build
|
||||||
|
|
||||||
ADD ./api ./api
|
ADD ./api ./api
|
||||||
ADD ./conf ./conf
|
ADD ./conf ./conf
|
||||||
ADD ./deepdoc ./deepdoc
|
ADD ./deepdoc ./deepdoc
|
||||||
ADD ./rag ./rag
|
ADD ./rag ./rag
|
||||||
|
|
||||||
ENV PYTHONPATH=/ragflow/
|
ENV PYTHONPATH=/ragflow/
|
||||||
ENV HF_ENDPOINT=https://hf-mirror.com
|
ENV HF_ENDPOINT=https://hf-mirror.com
|
||||||
|
|
||||||
ADD docker/entrypoint.sh ./entrypoint.sh
|
ADD docker/entrypoint.sh ./entrypoint.sh
|
||||||
RUN chmod +x ./entrypoint.sh
|
RUN chmod +x ./entrypoint.sh
|
||||||
|
|
||||||
ENTRYPOINT ["./entrypoint.sh"]
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
54
Dockerfile.scratch
Normal file
54
Dockerfile.scratch
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
FROM ubuntu:22.04
|
||||||
|
USER root
|
||||||
|
|
||||||
|
WORKDIR /ragflow
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y wget curl build-essential libopenmpi-dev
|
||||||
|
|
||||||
|
RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh && \
|
||||||
|
bash ~/miniconda.sh -b -p /root/miniconda3 && \
|
||||||
|
rm ~/miniconda.sh && ln -s /root/miniconda3/etc/profile.d/conda.sh /etc/profile.d/conda.sh && \
|
||||||
|
echo ". /root/miniconda3/etc/profile.d/conda.sh" >> ~/.bashrc && \
|
||||||
|
echo "conda activate base" >> ~/.bashrc
|
||||||
|
|
||||||
|
ENV PATH /root/miniconda3/bin:$PATH
|
||||||
|
|
||||||
|
RUN conda create -y --name py11 python=3.11
|
||||||
|
|
||||||
|
ENV CONDA_DEFAULT_ENV py11
|
||||||
|
ENV CONDA_PREFIX /root/miniconda3/envs/py11
|
||||||
|
ENV PATH $CONDA_PREFIX/bin:$PATH
|
||||||
|
|
||||||
|
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||||
|
RUN apt-get install -y nodejs
|
||||||
|
|
||||||
|
RUN apt-get install -y nginx
|
||||||
|
|
||||||
|
ADD ./web ./web
|
||||||
|
ADD ./api ./api
|
||||||
|
ADD ./conf ./conf
|
||||||
|
ADD ./deepdoc ./deepdoc
|
||||||
|
ADD ./rag ./rag
|
||||||
|
ADD ./requirements.txt ./requirements.txt
|
||||||
|
|
||||||
|
RUN apt install openmpi-bin openmpi-common libopenmpi-dev
|
||||||
|
ENV LD_LIBRARY_PATH /usr/lib/x86_64-linux-gnu/openmpi/lib:$LD_LIBRARY_PATH
|
||||||
|
RUN rm /root/miniconda3/envs/py11/compiler_compat/ld
|
||||||
|
RUN cd ./web && npm i && npm run build
|
||||||
|
RUN conda run -n py11 pip install -i https://mirrors.aliyun.com/pypi/simple/ -r ./requirements.txt
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y libglib2.0-0 libgl1-mesa-glx && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN conda run -n py11 pip install -i https://mirrors.aliyun.com/pypi/simple/ ollama
|
||||||
|
RUN conda run -n py11 python -m nltk.downloader punkt
|
||||||
|
RUN conda run -n py11 python -m nltk.downloader wordnet
|
||||||
|
|
||||||
|
ENV PYTHONPATH=/ragflow/
|
||||||
|
ENV HF_ENDPOINT=https://hf-mirror.com
|
||||||
|
|
||||||
|
ADD docker/entrypoint.sh ./entrypoint.sh
|
||||||
|
RUN chmod +x ./entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
||||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||||
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
||||||
alt="docker pull ragflow:v1.0"></a>
|
alt="docker pull infiniflow/ragflow:v0.2.0"></a>
|
||||||
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
||||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||||
</a>
|
</a>
|
||||||
@ -55,6 +55,8 @@
|
|||||||
|
|
||||||
## 📌 Latest Features
|
## 📌 Latest Features
|
||||||
|
|
||||||
|
- 2024-04-16 Add an embedding model 'bce-embedding-base_v1' from [BCEmbedding](https://github.com/netease-youdao/BCEmbedding).
|
||||||
|
- 2024-04-16 Add [FastEmbed](https://github.com/qdrant/fastembed) is designed for light and speeding embedding.
|
||||||
- 2024-04-11 Support [Xinference](./docs/xinference.md) for local LLM deployment.
|
- 2024-04-11 Support [Xinference](./docs/xinference.md) for local LLM deployment.
|
||||||
- 2024-04-10 Add a new layout recognization model for analyzing Laws documentation.
|
- 2024-04-10 Add a new layout recognization model for analyzing Laws documentation.
|
||||||
- 2024-04-08 Support [Ollama](./docs/ollama.md) for local LLM deployment.
|
- 2024-04-08 Support [Ollama](./docs/ollama.md) for local LLM deployment.
|
||||||
@ -171,7 +173,7 @@ To build the Docker images from source:
|
|||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/infiniflow/ragflow.git
|
$ git clone https://github.com/infiniflow/ragflow.git
|
||||||
$ cd ragflow/
|
$ cd ragflow/
|
||||||
$ docker build -t infiniflow/ragflow:v1.0 .
|
$ docker build -t infiniflow/ragflow:v0.2.0 .
|
||||||
$ cd ragflow/docker
|
$ cd ragflow/docker
|
||||||
$ chmod +x ./entrypoint.sh
|
$ chmod +x ./entrypoint.sh
|
||||||
$ docker compose up -d
|
$ docker compose up -d
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
||||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||||
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
||||||
alt="docker pull ragflow:v1.0"></a>
|
alt="docker pull infiniflow/ragflow:v0.2.0"></a>
|
||||||
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
||||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||||
</a>
|
</a>
|
||||||
@ -55,6 +55,8 @@
|
|||||||
|
|
||||||
## 📌 最新の機能
|
## 📌 最新の機能
|
||||||
|
|
||||||
|
- 2024-04-16 [BCEmbedding](https://github.com/netease-youdao/BCEmbedding) から埋め込みモデル「bce-embedding-base_v1」を追加します。
|
||||||
|
- 2024-04-16 [FastEmbed](https://github.com/qdrant/fastembed) は、軽量かつ高速な埋め込み用に設計されています。
|
||||||
- 2024-04-11 ローカル LLM デプロイメント用に [Xinference](./docs/xinference.md) をサポートします。
|
- 2024-04-11 ローカル LLM デプロイメント用に [Xinference](./docs/xinference.md) をサポートします。
|
||||||
- 2024-04-10 メソッド「Laws」に新しいレイアウト認識モデルを追加します。
|
- 2024-04-10 メソッド「Laws」に新しいレイアウト認識モデルを追加します。
|
||||||
- 2024-04-08 [Ollama](./docs/ollama.md) を使用した大規模モデルのローカライズされたデプロイメントをサポートします。
|
- 2024-04-08 [Ollama](./docs/ollama.md) を使用した大規模モデルのローカライズされたデプロイメントをサポートします。
|
||||||
@ -171,7 +173,7 @@
|
|||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/infiniflow/ragflow.git
|
$ git clone https://github.com/infiniflow/ragflow.git
|
||||||
$ cd ragflow/
|
$ cd ragflow/
|
||||||
$ docker build -t infiniflow/ragflow:v1.0 .
|
$ docker build -t infiniflow/ragflow:v0.2.0 .
|
||||||
$ cd ragflow/docker
|
$ cd ragflow/docker
|
||||||
$ chmod +x ./entrypoint.sh
|
$ chmod +x ./entrypoint.sh
|
||||||
$ docker compose up -d
|
$ docker compose up -d
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
<img alt="Static Badge" src="https://img.shields.io/badge/RAGFLOW-LLM-white?&labelColor=dd0af7"></a>
|
||||||
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
<a href="https://hub.docker.com/r/infiniflow/ragflow" target="_blank">
|
||||||
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
<img src="https://img.shields.io/badge/docker_pull-ragflow:v1.0-brightgreen"
|
||||||
alt="docker pull ragflow:v1.0"></a>
|
alt="docker pull infiniflow/ragflow:v0.2.0"></a>
|
||||||
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
<a href="https://github.com/infiniflow/ragflow/blob/main/LICENSE">
|
||||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||||
</a>
|
</a>
|
||||||
@ -55,6 +55,8 @@
|
|||||||
|
|
||||||
## 📌 新增功能
|
## 📌 新增功能
|
||||||
|
|
||||||
|
- 2024-04-16 添加嵌入模型 [BCEmbedding](https://github.com/netease-youdao/BCEmbedding) 。
|
||||||
|
- 2024-04-16 添加 [FastEmbed](https://github.com/qdrant/fastembed) 专为轻型和高速嵌入而设计。
|
||||||
- 2024-04-11 支持用 [Xinference](./docs/xinference.md) 本地化部署大模型。
|
- 2024-04-11 支持用 [Xinference](./docs/xinference.md) 本地化部署大模型。
|
||||||
- 2024-04-10 为‘Laws’版面分析增加了底层模型。
|
- 2024-04-10 为‘Laws’版面分析增加了底层模型。
|
||||||
- 2024-04-08 支持用 [Ollama](./docs/ollama.md) 本地化部署大模型。
|
- 2024-04-08 支持用 [Ollama](./docs/ollama.md) 本地化部署大模型。
|
||||||
@ -171,7 +173,7 @@
|
|||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/infiniflow/ragflow.git
|
$ git clone https://github.com/infiniflow/ragflow.git
|
||||||
$ cd ragflow/
|
$ cd ragflow/
|
||||||
$ docker build -t infiniflow/ragflow:v1.0 .
|
$ docker build -t infiniflow/ragflow:v0.2.0 .
|
||||||
$ cd ragflow/docker
|
$ cd ragflow/docker
|
||||||
$ chmod +x ./entrypoint.sh
|
$ chmod +x ./entrypoint.sh
|
||||||
$ docker compose up -d
|
$ docker compose up -d
|
||||||
|
|||||||
@ -252,7 +252,7 @@ def retrieval_test():
|
|||||||
return get_data_error_result(retmsg="Knowledgebase not found!")
|
return get_data_error_result(retmsg="Knowledgebase not found!")
|
||||||
|
|
||||||
embd_mdl = TenantLLMService.model_instance(
|
embd_mdl = TenantLLMService.model_instance(
|
||||||
kb.tenant_id, LLMType.EMBEDDING.value)
|
kb.tenant_id, LLMType.EMBEDDING.value, llm_name=kb.embd_id)
|
||||||
ranks = retrievaler.retrieval(question, embd_mdl, kb.tenant_id, [kb_id], page, size, similarity_threshold,
|
ranks = retrievaler.retrieval(question, embd_mdl, kb.tenant_id, [kb_id], page, size, similarity_threshold,
|
||||||
vector_similarity_weight, top, doc_ids)
|
vector_similarity_weight, top, doc_ids)
|
||||||
for c in ranks["chunks"]:
|
for c in ranks["chunks"]:
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ def upload():
|
|||||||
if not e:
|
if not e:
|
||||||
return get_data_error_result(
|
return get_data_error_result(
|
||||||
retmsg="Can't find this knowledgebase!")
|
retmsg="Can't find this knowledgebase!")
|
||||||
if DocumentService.get_doc_count(kb.tenant_id) >= 128:
|
if DocumentService.get_doc_count(kb.tenant_id) >= int(os.environ.get('MAX_FILE_NUM_PER_USER', 8192)):
|
||||||
return get_data_error_result(
|
return get_data_error_result(
|
||||||
retmsg="Exceed the maximum file number of a free user!")
|
retmsg="Exceed the maximum file number of a free user!")
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ from rag.llm import EmbeddingModel, ChatModel
|
|||||||
def factories():
|
def factories():
|
||||||
try:
|
try:
|
||||||
fac = LLMFactoriesService.get_all()
|
fac = LLMFactoriesService.get_all()
|
||||||
return get_json_result(data=[f.to_dict() for f in fac])
|
return get_json_result(data=[f.to_dict() for f in fac if f.name not in ["QAnything", "FastEmbed"]])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return server_error_response(e)
|
return server_error_response(e)
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ def list():
|
|||||||
llms = [m.to_dict()
|
llms = [m.to_dict()
|
||||||
for m in llms if m.status == StatusEnum.VALID.value]
|
for m in llms if m.status == StatusEnum.VALID.value]
|
||||||
for m in llms:
|
for m in llms:
|
||||||
m["available"] = m["fid"] in facts or m["llm_name"].lower() == "flag-embedding"
|
m["available"] = m["fid"] in facts or m["llm_name"].lower() == "flag-embedding" or m["fid"] in ["QAnything","FastEmbed"]
|
||||||
|
|
||||||
llm_set = set([m["llm_name"] for m in llms])
|
llm_set = set([m["llm_name"] for m in llms])
|
||||||
for o in objs:
|
for o in objs:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import time
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from api.db import LLMType, UserTenantRole
|
from api.db import LLMType, UserTenantRole
|
||||||
from api.db.db_models import init_database_tables as init_web_db, LLMFactories, LLM
|
from api.db.db_models import init_database_tables as init_web_db, LLMFactories, LLM, TenantLLM
|
||||||
from api.db.services import UserService
|
from api.db.services import UserService
|
||||||
from api.db.services.llm_service import LLMFactoriesService, LLMService, TenantLLMService, LLMBundle
|
from api.db.services.llm_service import LLMFactoriesService, LLMService, TenantLLMService, LLMBundle
|
||||||
from api.db.services.user_service import TenantService, UserTenantService
|
from api.db.services.user_service import TenantService, UserTenantService
|
||||||
@ -114,12 +114,16 @@ factory_infos = [{
|
|||||||
"logo": "",
|
"logo": "",
|
||||||
"tags": "TEXT EMBEDDING",
|
"tags": "TEXT EMBEDDING",
|
||||||
"status": "1",
|
"status": "1",
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"name": "Xinference",
|
"name": "Xinference",
|
||||||
"logo": "",
|
"logo": "",
|
||||||
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
||||||
"status": "1",
|
"status": "1",
|
||||||
|
},{
|
||||||
|
"name": "QAnything",
|
||||||
|
"logo": "",
|
||||||
|
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
||||||
|
"status": "1",
|
||||||
},
|
},
|
||||||
# {
|
# {
|
||||||
# "name": "文心一言",
|
# "name": "文心一言",
|
||||||
@ -254,12 +258,6 @@ def init_llm_factory():
|
|||||||
"tags": "LLM,CHAT,",
|
"tags": "LLM,CHAT,",
|
||||||
"max_tokens": 7900,
|
"max_tokens": 7900,
|
||||||
"model_type": LLMType.CHAT.value
|
"model_type": LLMType.CHAT.value
|
||||||
}, {
|
|
||||||
"fid": factory_infos[4]["name"],
|
|
||||||
"llm_name": "flag-embedding",
|
|
||||||
"tags": "TEXT EMBEDDING,",
|
|
||||||
"max_tokens": 128 * 1000,
|
|
||||||
"model_type": LLMType.EMBEDDING.value
|
|
||||||
}, {
|
}, {
|
||||||
"fid": factory_infos[4]["name"],
|
"fid": factory_infos[4]["name"],
|
||||||
"llm_name": "moonshot-v1-32k",
|
"llm_name": "moonshot-v1-32k",
|
||||||
@ -325,6 +323,14 @@ def init_llm_factory():
|
|||||||
"max_tokens": 2147483648,
|
"max_tokens": 2147483648,
|
||||||
"model_type": LLMType.EMBEDDING.value
|
"model_type": LLMType.EMBEDDING.value
|
||||||
},
|
},
|
||||||
|
# ------------------------ QAnything -----------------------
|
||||||
|
{
|
||||||
|
"fid": factory_infos[7]["name"],
|
||||||
|
"llm_name": "maidalun1020/bce-embedding-base_v1",
|
||||||
|
"tags": "TEXT EMBEDDING,",
|
||||||
|
"max_tokens": 512,
|
||||||
|
"model_type": LLMType.EMBEDDING.value
|
||||||
|
},
|
||||||
]
|
]
|
||||||
for info in factory_infos:
|
for info in factory_infos:
|
||||||
try:
|
try:
|
||||||
@ -337,8 +343,10 @@ def init_llm_factory():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
LLMFactoriesService.filter_delete([LLMFactories.name=="Local"])
|
LLMFactoriesService.filter_delete([LLMFactories.name == "Local"])
|
||||||
LLMService.filter_delete([LLM.fid=="Local"])
|
LLMService.filter_delete([LLM.fid == "Local"])
|
||||||
|
LLMService.filter_delete([LLM.fid == "Moonshot", LLM.llm_name == "flag-embedding"])
|
||||||
|
TenantLLMService.filter_delete([TenantLLM.llm_factory == "Moonshot", TenantLLM.llm_name == "flag-embedding"])
|
||||||
|
|
||||||
"""
|
"""
|
||||||
drop table llm;
|
drop table llm;
|
||||||
|
|||||||
@ -80,8 +80,12 @@ def chat(dialog, messages, **kwargs):
|
|||||||
raise LookupError("LLM(%s) not found" % dialog.llm_id)
|
raise LookupError("LLM(%s) not found" % dialog.llm_id)
|
||||||
max_tokens = 1024
|
max_tokens = 1024
|
||||||
else: max_tokens = llm[0].max_tokens
|
else: max_tokens = llm[0].max_tokens
|
||||||
|
kbs = KnowledgebaseService.get_by_ids(dialog.kb_ids)
|
||||||
|
embd_nms = list(set([kb.embd_id for kb in kbs]))
|
||||||
|
assert len(embd_nms) == 1, "Knowledge bases use different embedding models."
|
||||||
|
|
||||||
questions = [m["content"] for m in messages if m["role"] == "user"]
|
questions = [m["content"] for m in messages if m["role"] == "user"]
|
||||||
embd_mdl = LLMBundle(dialog.tenant_id, LLMType.EMBEDDING)
|
embd_mdl = LLMBundle(dialog.tenant_id, LLMType.EMBEDDING, embd_nms[0])
|
||||||
chat_mdl = LLMBundle(dialog.tenant_id, LLMType.CHAT, dialog.llm_id)
|
chat_mdl = LLMBundle(dialog.tenant_id, LLMType.CHAT, dialog.llm_id)
|
||||||
|
|
||||||
prompt_config = dialog.prompt_config
|
prompt_config = dialog.prompt_config
|
||||||
|
|||||||
@ -66,7 +66,7 @@ class TenantLLMService(CommonService):
|
|||||||
raise LookupError("Tenant not found")
|
raise LookupError("Tenant not found")
|
||||||
|
|
||||||
if llm_type == LLMType.EMBEDDING.value:
|
if llm_type == LLMType.EMBEDDING.value:
|
||||||
mdlnm = tenant.embd_id
|
mdlnm = tenant.embd_id if not llm_name else llm_name
|
||||||
elif llm_type == LLMType.SPEECH2TEXT.value:
|
elif llm_type == LLMType.SPEECH2TEXT.value:
|
||||||
mdlnm = tenant.asr_id
|
mdlnm = tenant.asr_id
|
||||||
elif llm_type == LLMType.IMAGE2TEXT.value:
|
elif llm_type == LLMType.IMAGE2TEXT.value:
|
||||||
@ -77,9 +77,19 @@ class TenantLLMService(CommonService):
|
|||||||
assert False, "LLM type error"
|
assert False, "LLM type error"
|
||||||
|
|
||||||
model_config = cls.get_api_key(tenant_id, mdlnm)
|
model_config = cls.get_api_key(tenant_id, mdlnm)
|
||||||
|
if model_config: model_config = model_config.to_dict()
|
||||||
if not model_config:
|
if not model_config:
|
||||||
raise LookupError("Model({}) not authorized".format(mdlnm))
|
if llm_type == LLMType.EMBEDDING.value:
|
||||||
model_config = model_config.to_dict()
|
llm = LLMService.query(llm_name=llm_name)
|
||||||
|
if llm and llm[0].fid in ["QAnything", "FastEmbed"]:
|
||||||
|
model_config = {"llm_factory": llm[0].fid, "api_key":"", "llm_name": llm_name, "api_base": ""}
|
||||||
|
if not model_config:
|
||||||
|
if llm_name == "flag-embedding":
|
||||||
|
model_config = {"llm_factory": "Tongyi-Qianwen", "api_key": "",
|
||||||
|
"llm_name": llm_name, "api_base": ""}
|
||||||
|
else:
|
||||||
|
raise LookupError("Model({}) not authorized".format(mdlnm))
|
||||||
|
|
||||||
if llm_type == LLMType.EMBEDDING.value:
|
if llm_type == LLMType.EMBEDDING.value:
|
||||||
if model_config["llm_factory"] not in EmbeddingModel:
|
if model_config["llm_factory"] not in EmbeddingModel:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class TaskService(CommonService):
|
|||||||
Document.size,
|
Document.size,
|
||||||
Knowledgebase.tenant_id,
|
Knowledgebase.tenant_id,
|
||||||
Knowledgebase.language,
|
Knowledgebase.language,
|
||||||
Tenant.embd_id,
|
Knowledgebase.embd_id,
|
||||||
Tenant.img2txt_id,
|
Tenant.img2txt_id,
|
||||||
Tenant.asr_id,
|
Tenant.asr_id,
|
||||||
cls.model.update_time]
|
cls.model.update_time]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import re, copy, time, datetime, demjson, \
|
import re, copy, time, datetime, demjson3, \
|
||||||
traceback, signal
|
traceback, signal
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from deepdoc.parser.resume.entities import degrees, schools, corporations
|
from deepdoc.parser.resume.entities import degrees, schools, corporations
|
||||||
@ -197,7 +197,7 @@ def forProj(cv):
|
|||||||
|
|
||||||
|
|
||||||
def json_loads(line):
|
def json_loads(line):
|
||||||
return demjson.decode(re.sub(r": *(True|False)", r": '\1'", line))
|
return demjson3.decode(re.sub(r": *(True|False)", r": '\1'", line))
|
||||||
|
|
||||||
|
|
||||||
def forWork(cv):
|
def forWork(cv):
|
||||||
|
|||||||
@ -9,7 +9,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
es01:
|
es01:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v1.0
|
image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.2.0
|
||||||
container_name: ragflow-server
|
container_name: ragflow-server
|
||||||
ports:
|
ports:
|
||||||
- ${SVR_HTTP_PORT}:9380
|
- ${SVR_HTTP_PORT}:9380
|
||||||
|
|||||||
@ -9,7 +9,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
es01:
|
es01:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
image: infiniflow/ragflow:v1.0
|
image: infiniflow/ragflow:v0.2.0
|
||||||
container_name: ragflow-server
|
container_name: ragflow-server
|
||||||
ports:
|
ports:
|
||||||
- ${SVR_HTTP_PORT}:9380
|
- ${SVR_HTTP_PORT}:9380
|
||||||
@ -23,7 +23,7 @@ services:
|
|||||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||||
environment:
|
environment:
|
||||||
- TZ=${TIMEZONE}
|
- TZ=${TIMEZONE}
|
||||||
- HF_ENDPOINT=https://huggingface.com
|
- HF_ENDPOINT=https://huggingface.co
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
13
docs/faq.md
13
docs/faq.md
@ -90,6 +90,17 @@ Ignore this warning and continue. All system warnings can be ignored.
|
|||||||
|
|
||||||
Parsing requests have to wait in queue due to limited server resources. We are currently enhancing our algorithms and increasing computing power.
|
Parsing requests have to wait in queue due to limited server resources. We are currently enhancing our algorithms and increasing computing power.
|
||||||
|
|
||||||
|
### Why does my document parsing stall at under one percent?
|
||||||
|
|
||||||
|
If your RAGFlow is deployed *locally*, try the following:
|
||||||
|
|
||||||
|
1. Check the log of your RAGFlow server to see if it is running properly:
|
||||||
|
```bash
|
||||||
|
docker logs -f ragflow-server
|
||||||
|
```
|
||||||
|
2. Check if the **tast_executor.py** process exist.
|
||||||
|
3. Check if your RAGFlow server can access hf-mirror.com or huggingface.com.
|
||||||
|
|
||||||
### How to handle `Index failure`?
|
### How to handle `Index failure`?
|
||||||
|
|
||||||
An index failure usually indicates an unavailable Elasticsearch service.
|
An index failure usually indicates an unavailable Elasticsearch service.
|
||||||
@ -108,7 +119,7 @@ $ docker ps
|
|||||||
*The system displays the following if all your RAGFlow components are running properly:*
|
*The system displays the following if all your RAGFlow components are running properly:*
|
||||||
|
|
||||||
```
|
```
|
||||||
5bc45806b680 infiniflow/ragflow:v1.0 "./entrypoint.sh" 11 hours ago Up 11 hours 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:9380->9380/tcp, :::9380->9380/tcp ragflow-server
|
5bc45806b680 infiniflow/ragflow:v0.2.0 "./entrypoint.sh" 11 hours ago Up 11 hours 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:9380->9380/tcp, :::9380->9380/tcp ragflow-server
|
||||||
91220e3285dd docker.elastic.co/elasticsearch/elasticsearch:8.11.3 "/bin/tini -- /usr/l…" 11 hours ago Up 11 hours (healthy) 9300/tcp, 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp ragflow-es-01
|
91220e3285dd docker.elastic.co/elasticsearch/elasticsearch:8.11.3 "/bin/tini -- /usr/l…" 11 hours ago Up 11 hours (healthy) 9300/tcp, 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp ragflow-es-01
|
||||||
d8c86f06c56b mysql:5.7.18 "docker-entrypoint.s…" 7 days ago Up 16 seconds (healthy) 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp ragflow-mysql
|
d8c86f06c56b mysql:5.7.18 "docker-entrypoint.s…" 7 days ago Up 16 seconds (healthy) 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp ragflow-mysql
|
||||||
cd29bcb254bc quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z "/usr/bin/docker-ent…" 2 weeks ago Up 11 hours 0.0.0.0:9001->9001/tcp, :::9001->9001/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp ragflow-minio
|
cd29bcb254bc quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z "/usr/bin/docker-ent…" 2 weeks ago Up 11 hours 0.0.0.0:9001->9001/tcp, :::9001->9001/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp ragflow-minio
|
||||||
|
|||||||
@ -31,7 +31,7 @@ $ docker exec -it ollama ollama run mistral
|
|||||||
<img src="https://github.com/infiniflow/ragflow/assets/12318111/a9df198a-226d-4f30-b8d7-829f00256d46" width="1300"/>
|
<img src="https://github.com/infiniflow/ragflow/assets/12318111/a9df198a-226d-4f30-b8d7-829f00256d46" width="1300"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> Base URL: Enter the base URL where the Ollama service is accessible, like, http://<your-ollama-endpoint-domain>:11434
|
> Base URL: Enter the base URL where the Ollama service is accessible, like, `http://<your-ollama-endpoint-domain>:11434`.
|
||||||
|
|
||||||
- Use Ollama Models.
|
- Use Ollama Models.
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ $ xinference launch -u mistral --model-name mistral-v0.1 --size-in-billions 7 --
|
|||||||
<img src="https://github.com/infiniflow/ragflow/assets/12318111/bcbf4d7a-ade6-44c7-ad5f-0a92c8a73789" width="1300"/>
|
<img src="https://github.com/infiniflow/ragflow/assets/12318111/bcbf4d7a-ade6-44c7-ad5f-0a92c8a73789" width="1300"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> Base URL: Enter the base URL where the Xinference service is accessible, like, http://<your-xinference-endpoint-domain>:9997/v1
|
> Base URL: Enter the base URL where the Xinference service is accessible, like, `http://<your-xinference-endpoint-domain>:9997/v1`.
|
||||||
|
|
||||||
- Use Xinference Models.
|
- Use Xinference Models.
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,8 @@ EmbeddingModel = {
|
|||||||
"Xinference": XinferenceEmbed,
|
"Xinference": XinferenceEmbed,
|
||||||
"Tongyi-Qianwen": HuEmbedding, #QWenEmbed,
|
"Tongyi-Qianwen": HuEmbedding, #QWenEmbed,
|
||||||
"ZHIPU-AI": ZhipuEmbed,
|
"ZHIPU-AI": ZhipuEmbed,
|
||||||
"Moonshot": HuEmbedding,
|
"FastEmbed": FastEmbed,
|
||||||
"FastEmbed": FastEmbed
|
"QAnything": QAnythingEmbed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ from abc import ABC
|
|||||||
from ollama import Client
|
from ollama import Client
|
||||||
import dashscope
|
import dashscope
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from fastembed import TextEmbedding
|
|
||||||
from FlagEmbedding import FlagModel
|
from FlagEmbedding import FlagModel
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -28,16 +27,17 @@ import numpy as np
|
|||||||
from api.utils.file_utils import get_project_base_directory
|
from api.utils.file_utils import get_project_base_directory
|
||||||
from rag.utils import num_tokens_from_string
|
from rag.utils import num_tokens_from_string
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
flag_model = FlagModel(os.path.join(
|
flag_model = FlagModel(os.path.join(
|
||||||
get_project_base_directory(),
|
get_project_base_directory(),
|
||||||
"rag/res/bge-large-zh-v1.5"),
|
"rag/res/bge-large-zh-v1.5"),
|
||||||
query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:",
|
query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:",
|
||||||
use_fp16=torch.cuda.is_available())
|
use_fp16=torch.cuda.is_available())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flag_model = FlagModel("BAAI/bge-large-zh-v1.5",
|
flag_model = FlagModel("BAAI/bge-large-zh-v1.5",
|
||||||
query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:",
|
query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:",
|
||||||
use_fp16=torch.cuda.is_available())
|
use_fp16=torch.cuda.is_available())
|
||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
@ -82,8 +82,10 @@ class HuEmbedding(Base):
|
|||||||
|
|
||||||
|
|
||||||
class OpenAIEmbed(Base):
|
class OpenAIEmbed(Base):
|
||||||
def __init__(self, key, model_name="text-embedding-ada-002", base_url="https://api.openai.com/v1"):
|
def __init__(self, key, model_name="text-embedding-ada-002",
|
||||||
if not base_url: base_url="https://api.openai.com/v1"
|
base_url="https://api.openai.com/v1"):
|
||||||
|
if not base_url:
|
||||||
|
base_url = "https://api.openai.com/v1"
|
||||||
self.client = OpenAI(api_key=key, base_url=base_url)
|
self.client = OpenAI(api_key=key, base_url=base_url)
|
||||||
self.model_name = model_name
|
self.model_name = model_name
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ class ZhipuEmbed(Base):
|
|||||||
tks_num = 0
|
tks_num = 0
|
||||||
for txt in texts:
|
for txt in texts:
|
||||||
res = self.client.embeddings.create(input=txt,
|
res = self.client.embeddings.create(input=txt,
|
||||||
model=self.model_name)
|
model=self.model_name)
|
||||||
arr.append(res.data[0].embedding)
|
arr.append(res.data[0].embedding)
|
||||||
tks_num += res.usage.total_tokens
|
tks_num += res.usage.total_tokens
|
||||||
return np.array(arr), tks_num
|
return np.array(arr), tks_num
|
||||||
@ -163,14 +165,14 @@ class OllamaEmbed(Base):
|
|||||||
tks_num = 0
|
tks_num = 0
|
||||||
for txt in texts:
|
for txt in texts:
|
||||||
res = self.client.embeddings(prompt=txt,
|
res = self.client.embeddings(prompt=txt,
|
||||||
model=self.model_name)
|
model=self.model_name)
|
||||||
arr.append(res["embedding"])
|
arr.append(res["embedding"])
|
||||||
tks_num += 128
|
tks_num += 128
|
||||||
return np.array(arr), tks_num
|
return np.array(arr), tks_num
|
||||||
|
|
||||||
def encode_queries(self, text):
|
def encode_queries(self, text):
|
||||||
res = self.client.embeddings(prompt=text,
|
res = self.client.embeddings(prompt=text,
|
||||||
model=self.model_name)
|
model=self.model_name)
|
||||||
return np.array(res["embedding"]), 128
|
return np.array(res["embedding"]), 128
|
||||||
|
|
||||||
|
|
||||||
@ -183,10 +185,12 @@ class FastEmbed(Base):
|
|||||||
threads: Optional[int] = None,
|
threads: Optional[int] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
from fastembed import TextEmbedding
|
||||||
self._model = TextEmbedding(model_name, cache_dir, threads, **kwargs)
|
self._model = TextEmbedding(model_name, cache_dir, threads, **kwargs)
|
||||||
|
|
||||||
def encode(self, texts: list, batch_size=32):
|
def encode(self, texts: list, batch_size=32):
|
||||||
# Using the internal tokenizer to encode the texts and get the total number of tokens
|
# Using the internal tokenizer to encode the texts and get the total
|
||||||
|
# number of tokens
|
||||||
encodings = self._model.model.tokenizer.encode_batch(texts)
|
encodings = self._model.model.tokenizer.encode_batch(texts)
|
||||||
total_tokens = sum(len(e) for e in encodings)
|
total_tokens = sum(len(e) for e in encodings)
|
||||||
|
|
||||||
@ -195,7 +199,8 @@ class FastEmbed(Base):
|
|||||||
return np.array(embeddings), total_tokens
|
return np.array(embeddings), total_tokens
|
||||||
|
|
||||||
def encode_queries(self, text: str):
|
def encode_queries(self, text: str):
|
||||||
# Using the internal tokenizer to encode the texts and get the total number of tokens
|
# Using the internal tokenizer to encode the texts and get the total
|
||||||
|
# number of tokens
|
||||||
encoding = self._model.model.tokenizer.encode(text)
|
encoding = self._model.model.tokenizer.encode(text)
|
||||||
embedding = next(self._model.query_embed(text)).tolist()
|
embedding = next(self._model.query_embed(text)).tolist()
|
||||||
|
|
||||||
@ -218,3 +223,33 @@ class XinferenceEmbed(Base):
|
|||||||
model=self.model_name)
|
model=self.model_name)
|
||||||
return np.array(res.data[0].embedding), res.usage.total_tokens
|
return np.array(res.data[0].embedding), res.usage.total_tokens
|
||||||
|
|
||||||
|
|
||||||
|
class QAnythingEmbed(Base):
|
||||||
|
_client = None
|
||||||
|
|
||||||
|
def __init__(self, key=None, model_name="maidalun1020/bce-embedding-base_v1", **kwargs):
|
||||||
|
from BCEmbedding import EmbeddingModel as qanthing
|
||||||
|
if not QAnythingEmbed._client:
|
||||||
|
try:
|
||||||
|
print("LOADING BCE...")
|
||||||
|
QAnythingEmbed._client = qanthing(model_name_or_path=os.path.join(
|
||||||
|
get_project_base_directory(),
|
||||||
|
"rag/res/bce-embedding-base_v1"))
|
||||||
|
except Exception as e:
|
||||||
|
QAnythingEmbed._client = qanthing(
|
||||||
|
model_name_or_path=model_name.replace(
|
||||||
|
"maidalun1020", "InfiniFlow"))
|
||||||
|
|
||||||
|
def encode(self, texts: list, batch_size=10):
|
||||||
|
res = []
|
||||||
|
token_count = 0
|
||||||
|
for t in texts:
|
||||||
|
token_count += num_tokens_from_string(t)
|
||||||
|
for i in range(0, len(texts), batch_size):
|
||||||
|
embds = QAnythingEmbed._client.encode(texts[i:i + batch_size])
|
||||||
|
res.extend(embds)
|
||||||
|
return np.array(res), token_count
|
||||||
|
|
||||||
|
def encode_queries(self, text):
|
||||||
|
embds = QAnythingEmbed._client.encode([text])
|
||||||
|
return np.array(embds[0]), num_tokens_from_string(text)
|
||||||
|
|||||||
@ -46,7 +46,7 @@ class Dealer:
|
|||||||
"k": topk,
|
"k": topk,
|
||||||
"similarity": sim,
|
"similarity": sim,
|
||||||
"num_candidates": topk * 2,
|
"num_candidates": topk * 2,
|
||||||
"query_vector": list(qv)
|
"query_vector": [float(v) for v in qv]
|
||||||
}
|
}
|
||||||
|
|
||||||
def search(self, req, idxnm, emb_mdl=None):
|
def search(self, req, idxnm, emb_mdl=None):
|
||||||
|
|||||||
@ -244,8 +244,9 @@ def main(comm, mod):
|
|||||||
for _, r in rows.iterrows():
|
for _, r in rows.iterrows():
|
||||||
callback = partial(set_progress, r["id"], r["from_page"], r["to_page"])
|
callback = partial(set_progress, r["id"], r["from_page"], r["to_page"])
|
||||||
try:
|
try:
|
||||||
embd_mdl = LLMBundle(r["tenant_id"], LLMType.EMBEDDING)
|
embd_mdl = LLMBundle(r["tenant_id"], LLMType.EMBEDDING, llm_name=r["embd_id"], lang=r["language"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
traceback.print_stack(e)
|
||||||
callback(prog=-1, msg=str(e))
|
callback(prog=-1, msg=str(e))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ cryptography==42.0.5
|
|||||||
dashscope==1.14.1
|
dashscope==1.14.1
|
||||||
datasets==2.17.1
|
datasets==2.17.1
|
||||||
datrie==0.8.2
|
datrie==0.8.2
|
||||||
demjson==2.2.4
|
demjson3==3.0.6
|
||||||
dill==0.3.8
|
dill==0.3.8
|
||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
elastic-transport==8.12.0
|
elastic-transport==8.12.0
|
||||||
@ -132,3 +132,5 @@ xpinyin==0.7.6
|
|||||||
xxhash==3.4.1
|
xxhash==3.4.1
|
||||||
yarl==1.9.4
|
yarl==1.9.4
|
||||||
zhipuai==2.0.1
|
zhipuai==2.0.1
|
||||||
|
BCEmbedding
|
||||||
|
loguru==0.7.2
|
||||||
|
|||||||
345
web/package-lock.json
generated
345
web/package-lock.json
generated
@ -13,19 +13,21 @@
|
|||||||
"antd": "^5.12.7",
|
"antd": "^5.12.7",
|
||||||
"axios": "^1.6.3",
|
"axios": "^1.6.3",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"i18next": "^23.7.16",
|
"i18next": "^23.7.16",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.30.1",
|
|
||||||
"rc-tween-one": "^3.0.6",
|
"rc-tween-one": "^3.0.6",
|
||||||
"react-chat-elements": "^12.0.13",
|
"react-chat-elements": "^12.0.13",
|
||||||
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-i18next": "^14.0.0",
|
"react-i18next": "^14.0.0",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-pdf-highlighter": "^6.1.0",
|
"react-pdf-highlighter": "^6.1.0",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
|
"recharts": "^2.12.4",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.0",
|
||||||
"umi": "^4.0.90",
|
"umi": "^4.0.90",
|
||||||
"umi-request": "^1.4.0",
|
"umi-request": "^1.4.0",
|
||||||
@ -36,6 +38,7 @@
|
|||||||
"@react-dev-inspector/umi4-plugin": "^2.0.1",
|
"@react-dev-inspector/umi4-plugin": "^2.0.1",
|
||||||
"@types/lodash": "^4.14.202",
|
"@types/lodash": "^4.14.202",
|
||||||
"@types/react": "^18.0.33",
|
"@types/react": "^18.0.33",
|
||||||
|
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@types/react-syntax-highlighter": "^15.5.11",
|
"@types/react-syntax-highlighter": "^15.5.11",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
@ -2676,6 +2679,60 @@
|
|||||||
"@babel/types": "^7.20.7"
|
"@babel/types": "^7.20.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/d3-array": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-color": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-ease": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-interpolate": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-color": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-path": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-time": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-shape": {
|
||||||
|
"version": "3.1.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.6.tgz",
|
||||||
|
"integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-path": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-timer": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
|
||||||
|
},
|
||||||
"node_modules/@types/debug": {
|
"node_modules/@types/debug": {
|
||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz",
|
||||||
@ -2884,6 +2941,15 @@
|
|||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-copy-to-clipboard": {
|
||||||
|
"version": "5.0.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.7.tgz",
|
||||||
|
"integrity": "sha512-Gft19D+as4M+9Whq1oglhmK49vqPhcLzk8WfvfLvaYMIPYanyfLy0+CwFucMJfdKoSFyySPmkkWn8/E6voQXjQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react-dom": {
|
"node_modules/@types/react-dom": {
|
||||||
"version": "18.2.18",
|
"version": "18.2.18",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.2.18.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.2.18.tgz",
|
||||||
@ -5832,6 +5898,14 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/coa": {
|
"node_modules/coa": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/coa/-/coa-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/coa/-/coa-2.0.2.tgz",
|
||||||
@ -6640,11 +6714,132 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-1.2.4.tgz",
|
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-1.2.4.tgz",
|
||||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-color": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-ease": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-format": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-interpolate": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-path": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/d3-polygon": {
|
"node_modules/d3-polygon": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-1.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-1.0.6.tgz",
|
||||||
"integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ=="
|
"integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-scale": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2.10.0 - 3",
|
||||||
|
"d3-format": "1 - 3",
|
||||||
|
"d3-interpolate": "1.2.0 - 3",
|
||||||
|
"d3-time": "2.1.1 - 3",
|
||||||
|
"d3-time-format": "2 - 4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-scale/node_modules/d3-array": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"internmap": "1 - 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-shape": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-path": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time-format": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-time": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time/node_modules/d3-array": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"internmap": "1 - 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-timer": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
"node_modules/data-uri-to-buffer": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||||
@ -6705,6 +6900,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decimal.js-light": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
|
||||||
|
},
|
||||||
"node_modules/decode-named-character-reference": {
|
"node_modules/decode-named-character-reference": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
||||||
@ -7032,6 +7232,15 @@
|
|||||||
"utila": "~0.4"
|
"utila": "~0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-helpers": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.7",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dom-serializer": {
|
"node_modules/dom-serializer": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||||
@ -8151,6 +8360,11 @@
|
|||||||
"es5-ext": "~0.10.14"
|
"es5-ext": "~0.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||||
|
},
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
|
||||||
@ -8356,6 +8570,14 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-equals": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.2.12",
|
"version": "3.2.12",
|
||||||
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz",
|
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||||
@ -9693,6 +9915,14 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/internmap": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/intersection-observer": {
|
"node_modules/intersection-observer": {
|
||||||
"version": "0.12.2",
|
"version": "0.12.2",
|
||||||
"resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz",
|
"resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz",
|
||||||
@ -11925,6 +12155,7 @@
|
|||||||
"version": "2.30.1",
|
"version": "2.30.1",
|
||||||
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
|
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
|
||||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
@ -14356,6 +14587,18 @@
|
|||||||
"react-dom": "18.2.0"
|
"react-dom": "18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-copy-to-clipboard": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"copy-to-clipboard": "^3.3.1",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^15.3.0 || 16 || 17 || 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dev-inspector": {
|
"node_modules/react-dev-inspector": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/react-dev-inspector/-/react-dev-inspector-2.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/react-dev-inspector/-/react-dev-inspector-2.0.1.tgz",
|
||||||
@ -14934,6 +15177,20 @@
|
|||||||
"react": ">=15"
|
"react": ">=15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-smooth": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/react-smooth/-/react-smooth-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-equals": "^5.0.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-transition-group": "^4.4.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-spinkit": {
|
"node_modules/react-spinkit": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/react-spinkit/-/react-spinkit-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/react-spinkit/-/react-spinkit-3.0.0.tgz",
|
||||||
@ -14968,6 +15225,21 @@
|
|||||||
"react": ">= 0.14.0"
|
"react": ">= 0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-transition-group": {
|
||||||
|
"version": "4.4.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"dom-helpers": "^5.0.1",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.6.0",
|
||||||
|
"react-dom": ">=16.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reactcss": {
|
"node_modules/reactcss": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
@ -15145,6 +15417,41 @@
|
|||||||
"node": ">= 12.13.0"
|
"node": ">= 12.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/recharts": {
|
||||||
|
"version": "2.12.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/recharts/-/recharts-2.12.4.tgz",
|
||||||
|
"integrity": "sha512-dM4skmk4fDKEDjL9MNunxv6zcTxePGVEzRnLDXALRpfJ85JoQ0P0APJ/CoJlmnQI0gPjBlOkjzrwrfQrRST3KA==",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"eventemitter3": "^4.0.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"react-is": "^16.10.2",
|
||||||
|
"react-smooth": "^4.0.0",
|
||||||
|
"recharts-scale": "^0.4.4",
|
||||||
|
"tiny-invariant": "^1.3.1",
|
||||||
|
"victory-vendor": "^36.6.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/recharts-scale": {
|
||||||
|
"version": "0.4.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||||
|
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"decimal.js-light": "^2.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/recharts/node_modules/react-is": {
|
||||||
|
"version": "16.13.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
|
},
|
||||||
"node_modules/recursive-readdir": {
|
"node_modules/recursive-readdir": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
||||||
@ -17000,9 +17307,7 @@
|
|||||||
"node_modules/tiny-invariant": {
|
"node_modules/tiny-invariant": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
|
||||||
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
|
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
|
||||||
"dev": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tiny-warning": {
|
"node_modules/tiny-warning": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@ -18221,6 +18526,38 @@
|
|||||||
"unist-util-stringify-position": "^4.0.0"
|
"unist-util-stringify-position": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/victory-vendor": {
|
||||||
|
"version": "36.9.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||||
|
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "^3.0.3",
|
||||||
|
"@types/d3-ease": "^3.0.0",
|
||||||
|
"@types/d3-interpolate": "^3.0.1",
|
||||||
|
"@types/d3-scale": "^4.0.2",
|
||||||
|
"@types/d3-shape": "^3.1.0",
|
||||||
|
"@types/d3-time": "^3.0.0",
|
||||||
|
"@types/d3-timer": "^3.0.0",
|
||||||
|
"d3-array": "^3.1.6",
|
||||||
|
"d3-ease": "^3.0.1",
|
||||||
|
"d3-interpolate": "^3.0.1",
|
||||||
|
"d3-scale": "^4.0.2",
|
||||||
|
"d3-shape": "^3.1.0",
|
||||||
|
"d3-time": "^3.0.0",
|
||||||
|
"d3-timer": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/victory-vendor/node_modules/d3-array": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"internmap": "1 - 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.3.1.tgz",
|
||||||
|
|||||||
@ -17,19 +17,21 @@
|
|||||||
"antd": "^5.12.7",
|
"antd": "^5.12.7",
|
||||||
"axios": "^1.6.3",
|
"axios": "^1.6.3",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"i18next": "^23.7.16",
|
"i18next": "^23.7.16",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.30.1",
|
|
||||||
"rc-tween-one": "^3.0.6",
|
"rc-tween-one": "^3.0.6",
|
||||||
"react-chat-elements": "^12.0.13",
|
"react-chat-elements": "^12.0.13",
|
||||||
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-i18next": "^14.0.0",
|
"react-i18next": "^14.0.0",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-pdf-highlighter": "^6.1.0",
|
"react-pdf-highlighter": "^6.1.0",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
|
"recharts": "^2.12.4",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.0",
|
||||||
"umi": "^4.0.90",
|
"umi": "^4.0.90",
|
||||||
"umi-request": "^1.4.0",
|
"umi-request": "^1.4.0",
|
||||||
@ -40,6 +42,7 @@
|
|||||||
"@react-dev-inspector/umi4-plugin": "^2.0.1",
|
"@react-dev-inspector/umi4-plugin": "^2.0.1",
|
||||||
"@types/lodash": "^4.14.202",
|
"@types/lodash": "^4.14.202",
|
||||||
"@types/react": "^18.0.33",
|
"@types/react": "^18.0.33",
|
||||||
|
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@types/react-syntax-highlighter": "^15.5.11",
|
"@types/react-syntax-highlighter": "^15.5.11",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
|
|||||||
@ -6,6 +6,21 @@ import zh_HK from 'antd/locale/zh_HK';
|
|||||||
import React, { ReactNode, useEffect, useState } from 'react';
|
import React, { ReactNode, useEffect, useState } from 'react';
|
||||||
import storage from './utils/authorizationUtil';
|
import storage from './utils/authorizationUtil';
|
||||||
|
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||||
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||||
|
import localeData from 'dayjs/plugin/localeData';
|
||||||
|
import weekday from 'dayjs/plugin/weekday';
|
||||||
|
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||||
|
import weekYear from 'dayjs/plugin/weekYear';
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
dayjs.extend(advancedFormat);
|
||||||
|
dayjs.extend(weekday);
|
||||||
|
dayjs.extend(localeData);
|
||||||
|
dayjs.extend(weekOfYear);
|
||||||
|
dayjs.extend(weekYear);
|
||||||
|
|
||||||
const AntLanguageMap = {
|
const AntLanguageMap = {
|
||||||
en: enUS,
|
en: enUS,
|
||||||
zh: zhCN,
|
zh: zhCN,
|
||||||
|
|||||||
27
web/src/components/copy-to-clipboard.tsx
Normal file
27
web/src/components/copy-to-clipboard.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { CopyToClipboard as Clipboard, Props } from 'react-copy-to-clipboard';
|
||||||
|
|
||||||
|
const CopyToClipboard = ({ text }: Props) => {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const { t } = useTranslate('common');
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopied(false);
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={copied ? t('copied') : t('copy')}>
|
||||||
|
<Clipboard text={text} onCopy={handleCopy}>
|
||||||
|
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
||||||
|
</Clipboard>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CopyToClipboard;
|
||||||
88
web/src/components/line-chart/index.tsx
Normal file
88
web/src/components/line-chart/index.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
CartesianGrid,
|
||||||
|
Legend,
|
||||||
|
Line,
|
||||||
|
LineChart,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
} from 'recharts';
|
||||||
|
import { CategoricalChartProps } from 'recharts/types/chart/generateCategoricalChart';
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: 'Page A',
|
||||||
|
uv: 4000,
|
||||||
|
pv: 2400,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page B',
|
||||||
|
uv: 3000,
|
||||||
|
pv: 1398,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page C',
|
||||||
|
uv: 2000,
|
||||||
|
pv: 9800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page D',
|
||||||
|
uv: 2780,
|
||||||
|
pv: 3908,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page E',
|
||||||
|
uv: 1890,
|
||||||
|
pv: 4800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page F',
|
||||||
|
uv: 2390,
|
||||||
|
pv: 3800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page G',
|
||||||
|
uv: 3490,
|
||||||
|
pv: 4300,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface IProps extends CategoricalChartProps {
|
||||||
|
data?: Array<{ xAxis: string; yAxis: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RagLineChart = ({ data }: IProps) => {
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<LineChart
|
||||||
|
// width={500}
|
||||||
|
// height={300}
|
||||||
|
data={data}
|
||||||
|
margin={
|
||||||
|
{
|
||||||
|
// top: 5,
|
||||||
|
// right: 30,
|
||||||
|
// left: 20,
|
||||||
|
// bottom: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis dataKey="xAxis" />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
<Legend />
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="yAxis"
|
||||||
|
stroke="#8884d8"
|
||||||
|
activeDot={{ r: 8 }}
|
||||||
|
/>
|
||||||
|
{/* <Line type="monotone" dataKey="uv" stroke="#82ca9d" /> */}
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RagLineChart;
|
||||||
@ -1,4 +1,9 @@
|
|||||||
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
import {
|
||||||
|
IConversation,
|
||||||
|
IDialog,
|
||||||
|
IStats,
|
||||||
|
IToken,
|
||||||
|
} from '@/interfaces/database/chat';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
@ -164,3 +169,82 @@ export const useCompleteConversation = () => {
|
|||||||
|
|
||||||
return completeConversation;
|
return completeConversation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #region API provided for external calls
|
||||||
|
|
||||||
|
export const useCreateToken = (dialogId: string) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const createToken = useCallback(() => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'chatModel/createToken',
|
||||||
|
payload: { dialogId },
|
||||||
|
});
|
||||||
|
}, [dispatch, dialogId]);
|
||||||
|
|
||||||
|
return createToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useListToken = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const listToken = useCallback(
|
||||||
|
(dialogId: string) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'chatModel/listToken',
|
||||||
|
payload: { dialogId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return listToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectTokenList = () => {
|
||||||
|
const tokenList: IToken[] = useSelector(
|
||||||
|
(state: any) => state.chatModel.tokenList,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tokenList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveToken = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const removeToken = useCallback(
|
||||||
|
(payload: { tenantId: string; dialogId: string; tokens: string[] }) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'chatModel/removeToken',
|
||||||
|
payload: payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return removeToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchStats = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const fetchStats = useCallback(
|
||||||
|
(payload: any) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'chatModel/getStats',
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return fetchStats;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectStats = () => {
|
||||||
|
const stats: IStats = useSelector((state: any) => state.chatModel.stats);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -91,3 +91,21 @@ export interface Docagg {
|
|||||||
// term_similarity: number;
|
// term_similarity: number;
|
||||||
// vector_similarity: number;
|
// vector_similarity: number;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
export interface IToken {
|
||||||
|
create_date: string;
|
||||||
|
create_time: number;
|
||||||
|
tenant_id: string;
|
||||||
|
token: string;
|
||||||
|
update_date?: any;
|
||||||
|
update_time?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStats {
|
||||||
|
pv: [string, number][];
|
||||||
|
uv: [string, number][];
|
||||||
|
speed: [string, number][];
|
||||||
|
tokens: [string, number][];
|
||||||
|
round: [string, number][];
|
||||||
|
thumb_up: [string, number][];
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ export default {
|
|||||||
language: 'Language',
|
language: 'Language',
|
||||||
languageMessage: 'Please input your language!',
|
languageMessage: 'Please input your language!',
|
||||||
languagePlaceholder: 'select your language',
|
languagePlaceholder: 'select your language',
|
||||||
|
copy: 'Copy',
|
||||||
|
copied: 'Copied',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
@ -335,6 +337,24 @@ export default {
|
|||||||
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
||||||
quote: 'Show Quote',
|
quote: 'Show Quote',
|
||||||
quoteTip: 'Should the source of the original text be displayed?',
|
quoteTip: 'Should the source of the original text be displayed?',
|
||||||
|
overview: 'Overview',
|
||||||
|
pv: 'Number of messages',
|
||||||
|
uv: 'Active user number',
|
||||||
|
speed: 'Token output speed',
|
||||||
|
tokens: 'Consume the token number',
|
||||||
|
round: 'Session Interaction Number',
|
||||||
|
thumbUp: 'customer satisfaction',
|
||||||
|
publicUrl: 'Public URL',
|
||||||
|
preview: 'Preview',
|
||||||
|
embedded: 'Embedded',
|
||||||
|
serviceApiEndpoint: 'Service API Endpoint',
|
||||||
|
apiKey: 'Api Key',
|
||||||
|
apiReference: 'Api Reference',
|
||||||
|
dateRange: 'Date Range:',
|
||||||
|
backendServiceApi: 'Backend service API',
|
||||||
|
createNewKey: 'Create new key',
|
||||||
|
created: 'Created',
|
||||||
|
action: 'Action',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
|||||||
@ -15,11 +15,13 @@ export default {
|
|||||||
edit: '編輯',
|
edit: '編輯',
|
||||||
upload: '上傳',
|
upload: '上傳',
|
||||||
english: '英語',
|
english: '英語',
|
||||||
chinese: '中文簡體',
|
chinese: '簡體中文',
|
||||||
traditionalChinese: '中文繁體',
|
traditionalChinese: '繁體中文',
|
||||||
language: '語言',
|
language: '語言',
|
||||||
languageMessage: '請輸入語言',
|
languageMessage: '請輸入語言',
|
||||||
languagePlaceholder: '請選擇語言',
|
languagePlaceholder: '請選擇語言',
|
||||||
|
copy: '複製',
|
||||||
|
copied: '複製成功',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: '登入',
|
login: '登入',
|
||||||
@ -269,7 +271,7 @@ export default {
|
|||||||
systemMessage: '請輸入',
|
systemMessage: '請輸入',
|
||||||
systemTip:
|
systemTip:
|
||||||
'當LLM回答問題時,你需要LLM遵循的說明,比如角色設計、答案長度和答案語言等。',
|
'當LLM回答問題時,你需要LLM遵循的說明,比如角色設計、答案長度和答案語言等。',
|
||||||
topN: 'top n',
|
topN: 'Top N',
|
||||||
topNTip: `並非所有相似度得分高於“相似度閾值”的塊都會被提供給法學碩士。LLM 只能看到這些“Top N”塊。`,
|
topNTip: `並非所有相似度得分高於“相似度閾值”的塊都會被提供給法學碩士。LLM 只能看到這些“Top N”塊。`,
|
||||||
variable: '變量',
|
variable: '變量',
|
||||||
variableTip: `如果您使用对话 API,变量可能会帮助您使用不同的策略与客户聊天。
|
variableTip: `如果您使用对话 API,变量可能会帮助您使用不同的策略与客户聊天。
|
||||||
@ -310,6 +312,24 @@ export default {
|
|||||||
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
||||||
quote: '顯示引文',
|
quote: '顯示引文',
|
||||||
quoteTip: '是否應該顯示原文出處?',
|
quoteTip: '是否應該顯示原文出處?',
|
||||||
|
overview: '概覽',
|
||||||
|
pv: '消息數',
|
||||||
|
uv: '活躍用戶數',
|
||||||
|
speed: 'Token 輸出速度',
|
||||||
|
tokens: '消耗Token數',
|
||||||
|
round: '會話互動數',
|
||||||
|
thumbUp: '用戶滿意度',
|
||||||
|
publicUrl: '公共url',
|
||||||
|
preview: '預覽',
|
||||||
|
embedded: '嵌入',
|
||||||
|
serviceApiEndpoint: '服務API端點',
|
||||||
|
apiKey: 'API鍵',
|
||||||
|
apiReference: 'API參考',
|
||||||
|
dateRange: '日期範圍:',
|
||||||
|
backendServiceApi: '後端服務API',
|
||||||
|
createNewKey: '創建新密鑰',
|
||||||
|
created: '創建於',
|
||||||
|
action: '操作',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: '概述',
|
profile: '概述',
|
||||||
|
|||||||
@ -15,11 +15,13 @@ export default {
|
|||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
upload: '上传',
|
upload: '上传',
|
||||||
english: '英文',
|
english: '英文',
|
||||||
chinese: '中文简体',
|
chinese: '简体中文',
|
||||||
traditionalChinese: '中文繁体',
|
traditionalChinese: '繁体中文',
|
||||||
language: '语言',
|
language: '语言',
|
||||||
languageMessage: '请输入语言',
|
languageMessage: '请输入语言',
|
||||||
languagePlaceholder: '请选择语言',
|
languagePlaceholder: '请选择语言',
|
||||||
|
copy: '复制',
|
||||||
|
copied: '复制成功',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
@ -326,6 +328,24 @@ export default {
|
|||||||
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
||||||
quote: '显示引文',
|
quote: '显示引文',
|
||||||
quoteTip: '是否应该显示原文出处?',
|
quoteTip: '是否应该显示原文出处?',
|
||||||
|
overview: '概览',
|
||||||
|
pv: '消息数',
|
||||||
|
uv: '活跃用户数',
|
||||||
|
speed: 'Token 输出速度',
|
||||||
|
tokens: '消耗Token数',
|
||||||
|
round: '会话互动数',
|
||||||
|
thumbUp: '用户满意度',
|
||||||
|
publicUrl: '公共Url',
|
||||||
|
preview: '预览',
|
||||||
|
embedded: '嵌入',
|
||||||
|
serviceApiEndpoint: '服务API端点',
|
||||||
|
apiKey: 'API键',
|
||||||
|
apiReference: 'API参考',
|
||||||
|
dateRange: '日期范围:',
|
||||||
|
backendServiceApi: '后端服务API',
|
||||||
|
createNewKey: '创建新密钥',
|
||||||
|
created: '创建于',
|
||||||
|
action: '操作',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: '概要',
|
profile: '概要',
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import ParsingActionCell from './parsing-action-cell';
|
|||||||
import ParsingStatusCell from './parsing-status-cell';
|
import ParsingStatusCell from './parsing-status-cell';
|
||||||
import RenameModal from './rename-modal';
|
import RenameModal from './rename-modal';
|
||||||
|
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const KnowledgeFile = () => {
|
const KnowledgeFile = () => {
|
||||||
@ -94,6 +95,9 @@ const KnowledgeFile = () => {
|
|||||||
title: t('uploadDate'),
|
title: t('uploadDate'),
|
||||||
dataIndex: 'create_date',
|
dataIndex: 'create_date',
|
||||||
key: 'create_date',
|
key: 'create_date',
|
||||||
|
render(value) {
|
||||||
|
return formatDate(value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('chunkMethod'),
|
title: t('chunkMethod'),
|
||||||
|
|||||||
70
web/src/pages/chat/chat-api-key-modal/index.tsx
Normal file
70
web/src/pages/chat/chat-api-key-modal/index.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||||
|
import { useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { IToken } from '@/interfaces/database/chat';
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
|
import type { TableProps } from 'antd';
|
||||||
|
import { Button, Modal, Space, Table } from 'antd';
|
||||||
|
import { useOperateApiKey } from '../hooks';
|
||||||
|
|
||||||
|
const ChatApiKeyModal = ({
|
||||||
|
visible,
|
||||||
|
dialogId,
|
||||||
|
hideModal,
|
||||||
|
}: IModalProps<any> & { dialogId: string }) => {
|
||||||
|
const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
|
||||||
|
useOperateApiKey(visible, dialogId);
|
||||||
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
|
const columns: TableProps<IToken>['columns'] = [
|
||||||
|
{
|
||||||
|
title: 'Token',
|
||||||
|
dataIndex: 'token',
|
||||||
|
key: 'token',
|
||||||
|
render: (text) => <a>{text}</a>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('created'),
|
||||||
|
dataIndex: 'create_date',
|
||||||
|
key: 'create_date',
|
||||||
|
render: (text) => formatDate(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('action'),
|
||||||
|
key: 'action',
|
||||||
|
render: (_, record) => (
|
||||||
|
<Space size="middle">
|
||||||
|
<CopyToClipboard text={record.token}></CopyToClipboard>
|
||||||
|
<DeleteOutlined
|
||||||
|
onClick={() => removeToken(record.token, record.tenant_id)}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title={t('apiKey')}
|
||||||
|
open={visible}
|
||||||
|
onCancel={hideModal}
|
||||||
|
style={{ top: 300 }}
|
||||||
|
width={'50vw'}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tokenList}
|
||||||
|
rowKey={'token'}
|
||||||
|
loading={listLoading}
|
||||||
|
/>
|
||||||
|
<Button onClick={createToken} loading={creatingLoading}>
|
||||||
|
{t('createNewKey')}
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatApiKeyModal;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Form, Input, Select, Upload } from 'antd';
|
import { Form, Input, Select, Switch, Upload } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ISegmentedContentProps } from '../interface';
|
import { ISegmentedContentProps } from '../interface';
|
||||||
|
|
||||||
@ -83,6 +83,15 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
|||||||
>
|
>
|
||||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
<Input.TextArea autoSize={{ minRows: 5 }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('quote')}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={['prompt_config', 'quote']}
|
||||||
|
tooltip={t('quoteTip')}
|
||||||
|
initialValue={true}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('knowledgeBases')}
|
label={t('knowledgeBases')}
|
||||||
name="kb_ids"
|
name="kb_ids"
|
||||||
|
|||||||
@ -172,15 +172,7 @@ const PromptEngine = (
|
|||||||
>
|
>
|
||||||
<Slider max={30} />
|
<Slider max={30} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t('quote')}
|
|
||||||
valuePropName="checked"
|
|
||||||
name={['prompt_config', 'quote']}
|
|
||||||
tooltip={t('quoteTip')}
|
|
||||||
initialValue={true}
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<section className={classNames(styles.variableContainer)}>
|
<section className={classNames(styles.variableContainer)}>
|
||||||
<Row align={'middle'} justify="end">
|
<Row align={'middle'} justify="end">
|
||||||
<Col span={7} className={styles.variableAlign}>
|
<Col span={7} className={styles.variableAlign}>
|
||||||
|
|||||||
21
web/src/pages/chat/chat-overview-modal/index.less
Normal file
21
web/src/pages/chat/chat-overview-modal/index.less
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.chartWrapper {
|
||||||
|
height: 40vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartItem {
|
||||||
|
height: 300px;
|
||||||
|
padding: 10px 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartLabel {
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 60px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
.linkText {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: #eff8ff;
|
||||||
|
border: 1px;
|
||||||
|
}
|
||||||
97
web/src/pages/chat/chat-overview-modal/index.tsx
Normal file
97
web/src/pages/chat/chat-overview-modal/index.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import LineChart from '@/components/line-chart';
|
||||||
|
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { IDialog, IStats } from '@/interfaces/database/chat';
|
||||||
|
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
||||||
|
import { RangePickerProps } from 'antd/es/date-picker';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import camelCase from 'lodash/camelCase';
|
||||||
|
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||||
|
import { useFetchStatsOnMount, useSelectChartStatsList } from '../hooks';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
const { Paragraph } = Typography;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const ChatOverviewModal = ({
|
||||||
|
visible,
|
||||||
|
hideModal,
|
||||||
|
dialog,
|
||||||
|
}: IModalProps<any> & { dialog: IDialog }) => {
|
||||||
|
const { t } = useTranslate('chat');
|
||||||
|
const chartList = useSelectChartStatsList();
|
||||||
|
|
||||||
|
const {
|
||||||
|
visible: apiKeyVisible,
|
||||||
|
hideModal: hideApiKeyModal,
|
||||||
|
showModal: showApiKeyModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
|
||||||
|
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
||||||
|
|
||||||
|
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
||||||
|
return current && current > dayjs().endOf('day');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title={t('overview')}
|
||||||
|
open={visible}
|
||||||
|
onCancel={hideModal}
|
||||||
|
width={'100vw'}
|
||||||
|
>
|
||||||
|
<Flex vertical gap={'middle'}>
|
||||||
|
<Card title={dialog.name}>
|
||||||
|
<Flex gap={8} vertical>
|
||||||
|
{t('publicUrl')}
|
||||||
|
<Paragraph copyable className={styles.linkText}>
|
||||||
|
This is a copyable text.
|
||||||
|
</Paragraph>
|
||||||
|
</Flex>
|
||||||
|
<Space size={'middle'}>
|
||||||
|
<Button>{t('preview')}</Button>
|
||||||
|
<Button>{t('embedded')}</Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
<Card title={t('backendServiceApi')}>
|
||||||
|
<Flex gap={8} vertical>
|
||||||
|
{t('serviceApiEndpoint')}
|
||||||
|
<Paragraph copyable className={styles.linkText}>
|
||||||
|
This is a copyable text.
|
||||||
|
</Paragraph>
|
||||||
|
</Flex>
|
||||||
|
<Space size={'middle'}>
|
||||||
|
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
||||||
|
<Button>{t('apiReference')}</Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
<Space>
|
||||||
|
<b>{t('dateRange')}</b>
|
||||||
|
<RangePicker
|
||||||
|
disabledDate={disabledDate}
|
||||||
|
value={pickerValue}
|
||||||
|
onChange={setPickerValue}
|
||||||
|
allowClear={false}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<div className={styles.chartWrapper}>
|
||||||
|
{Object.keys(chartList).map((x) => (
|
||||||
|
<div key={x} className={styles.chartItem}>
|
||||||
|
<b className={styles.chartLabel}>{t(camelCase(x))}</b>
|
||||||
|
<LineChart data={chartList[x as keyof IStats]}></LineChart>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
<ChatApiKeyModal
|
||||||
|
visible={apiKeyVisible}
|
||||||
|
hideModal={hideApiKeyModal}
|
||||||
|
dialogId={dialog.id}
|
||||||
|
></ChatApiKeyModal>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatOverviewModal;
|
||||||
@ -2,22 +2,28 @@ import { MessageType } from '@/constants/chat';
|
|||||||
import { fileIconMap } from '@/constants/common';
|
import { fileIconMap } from '@/constants/common';
|
||||||
import {
|
import {
|
||||||
useCompleteConversation,
|
useCompleteConversation,
|
||||||
|
useCreateToken,
|
||||||
useFetchConversation,
|
useFetchConversation,
|
||||||
useFetchConversationList,
|
useFetchConversationList,
|
||||||
useFetchDialog,
|
useFetchDialog,
|
||||||
useFetchDialogList,
|
useFetchDialogList,
|
||||||
|
useFetchStats,
|
||||||
|
useListToken,
|
||||||
useRemoveConversation,
|
useRemoveConversation,
|
||||||
useRemoveDialog,
|
useRemoveDialog,
|
||||||
|
useRemoveToken,
|
||||||
useSelectConversationList,
|
useSelectConversationList,
|
||||||
useSelectDialogList,
|
useSelectDialogList,
|
||||||
|
useSelectTokenList,
|
||||||
useSetDialog,
|
useSetDialog,
|
||||||
useUpdateConversation,
|
useUpdateConversation,
|
||||||
} from '@/hooks/chatHooks';
|
} from '@/hooks/chatHooks';
|
||||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
|
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
import { IConversation, IDialog, IStats } from '@/interfaces/database/chat';
|
||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
import { getFileExtension } from '@/utils';
|
import { getFileExtension } from '@/utils';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import {
|
import {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
@ -704,3 +710,108 @@ export const useGetSendButtonDisabled = () => {
|
|||||||
return dialogId === '' && conversationId === '';
|
return dialogId === '' && conversationId === '';
|
||||||
};
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region API provided for external calls
|
||||||
|
|
||||||
|
type RangeValue = [Dayjs | null, Dayjs | null] | null;
|
||||||
|
|
||||||
|
export const useFetchStatsOnMount = (visible: boolean) => {
|
||||||
|
const fetchStats = useFetchStats();
|
||||||
|
const [pickerValue, setPickerValue] = useState<RangeValue>([
|
||||||
|
dayjs(),
|
||||||
|
dayjs().subtract(7, 'day'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
|
||||||
|
fetchStats({ fromDate: pickerValue[0], toDate: pickerValue[1] });
|
||||||
|
}
|
||||||
|
}, [fetchStats, pickerValue, visible]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pickerValue,
|
||||||
|
setPickerValue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOperateApiKey = (visible: boolean, dialogId: string) => {
|
||||||
|
const removeToken = useRemoveToken();
|
||||||
|
const createToken = useCreateToken(dialogId);
|
||||||
|
const listToken = useListToken();
|
||||||
|
const tokenList = useSelectTokenList();
|
||||||
|
const creatingLoading = useOneNamespaceEffectsLoading('chatModel', [
|
||||||
|
'createToken',
|
||||||
|
]);
|
||||||
|
const listLoading = useOneNamespaceEffectsLoading('chatModel', ['list']);
|
||||||
|
|
||||||
|
const showDeleteConfirm = useShowDeleteConfirm();
|
||||||
|
|
||||||
|
const onRemoveToken = (token: string, tenantId: string) => {
|
||||||
|
showDeleteConfirm({
|
||||||
|
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && dialogId) {
|
||||||
|
listToken(dialogId);
|
||||||
|
}
|
||||||
|
}, [listToken, dialogId, visible]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
removeToken: onRemoveToken,
|
||||||
|
createToken,
|
||||||
|
tokenList,
|
||||||
|
creatingLoading,
|
||||||
|
listLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChartStatsType = {
|
||||||
|
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectChartStatsList = (): ChartStatsType => {
|
||||||
|
// const stats: IStats = useSelectStats();
|
||||||
|
const stats = {
|
||||||
|
pv: [
|
||||||
|
['2024-06-01', 1],
|
||||||
|
['2024-07-24', 3],
|
||||||
|
['2024-09-01', 10],
|
||||||
|
],
|
||||||
|
uv: [
|
||||||
|
['2024-02-01', 0],
|
||||||
|
['2024-03-01', 99],
|
||||||
|
['2024-05-01', 3],
|
||||||
|
],
|
||||||
|
speed: [
|
||||||
|
['2024-09-01', 2],
|
||||||
|
['2024-09-01', 3],
|
||||||
|
],
|
||||||
|
tokens: [
|
||||||
|
['2024-09-01', 1],
|
||||||
|
['2024-09-01', 3],
|
||||||
|
],
|
||||||
|
round: [
|
||||||
|
['2024-09-01', 0],
|
||||||
|
['2024-09-01', 3],
|
||||||
|
],
|
||||||
|
thumb_up: [
|
||||||
|
['2024-09-01', 3],
|
||||||
|
['2024-09-01', 9],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.keys(stats).reduce((pre, cur) => {
|
||||||
|
const item = stats[cur as keyof IStats];
|
||||||
|
if (item.length > 0) {
|
||||||
|
pre[cur as keyof IStats] = item.map((x) => ({
|
||||||
|
xAxis: x[0] as string,
|
||||||
|
yAxis: x[1] as number,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}, {} as ChartStatsType);
|
||||||
|
};
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -35,7 +35,10 @@ import {
|
|||||||
useSelectFirstDialogOnMount,
|
useSelectFirstDialogOnMount,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
import { useTranslate } from '@/hooks/commonHooks';
|
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { useSetSelectedRecord } from '@/hooks/logicHooks';
|
||||||
|
import { IDialog } from '@/interfaces/database/chat';
|
||||||
|
import ChatOverviewModal from './chat-overview-modal';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const Chat = () => {
|
const Chat = () => {
|
||||||
@ -73,6 +76,12 @@ const Chat = () => {
|
|||||||
const dialogLoading = useSelectDialogListLoading();
|
const dialogLoading = useSelectDialogListLoading();
|
||||||
const conversationLoading = useSelectConversationListLoading();
|
const conversationLoading = useSelectConversationListLoading();
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
|
const {
|
||||||
|
visible: overviewVisible,
|
||||||
|
hideModal: hideOverviewModal,
|
||||||
|
showModal: showOverviewModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
|
||||||
|
|
||||||
useFetchDialogOnMount(dialogId, true);
|
useFetchDialogOnMount(dialogId, true);
|
||||||
|
|
||||||
@ -100,6 +109,15 @@ const Chat = () => {
|
|||||||
onRemoveDialog([dialogId]);
|
onRemoveDialog([dialogId]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShowOverviewModal =
|
||||||
|
(dialog: IDialog): any =>
|
||||||
|
(info: any) => {
|
||||||
|
info?.domEvent?.preventDefault();
|
||||||
|
info?.domEvent?.stopPropagation();
|
||||||
|
setRecord(dialog);
|
||||||
|
showOverviewModal();
|
||||||
|
};
|
||||||
|
|
||||||
const handleRemoveConversation =
|
const handleRemoveConversation =
|
||||||
(conversationId: string): MenuItemProps['onClick'] =>
|
(conversationId: string): MenuItemProps['onClick'] =>
|
||||||
({ domEvent }) => {
|
({ domEvent }) => {
|
||||||
@ -141,7 +159,9 @@ const Chat = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const buildAppItems = (dialogId: string) => {
|
const buildAppItems = (dialog: IDialog) => {
|
||||||
|
const dialogId = dialog.id;
|
||||||
|
|
||||||
const appItems: MenuProps['items'] = [
|
const appItems: MenuProps['items'] = [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
@ -164,6 +184,17 @@ const Chat = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
// {
|
||||||
|
// key: '3',
|
||||||
|
// onClick: handleShowOverviewModal(dialog),
|
||||||
|
// label: (
|
||||||
|
// <Space>
|
||||||
|
// <ProfileOutlined />
|
||||||
|
// {t('overview')}
|
||||||
|
// </Space>
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
return appItems;
|
return appItems;
|
||||||
@ -230,7 +261,7 @@ const Chat = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
{activated === x.id && (
|
{activated === x.id && (
|
||||||
<section>
|
<section>
|
||||||
<Dropdown menu={{ items: buildAppItems(x.id) }}>
|
<Dropdown menu={{ items: buildAppItems(x) }}>
|
||||||
<ChatAppCube
|
<ChatAppCube
|
||||||
className={styles.cubeIcon}
|
className={styles.cubeIcon}
|
||||||
></ChatAppCube>
|
></ChatAppCube>
|
||||||
@ -315,6 +346,11 @@ const Chat = () => {
|
|||||||
initialName={initialConversationName}
|
initialName={initialConversationName}
|
||||||
loading={conversationRenameLoading}
|
loading={conversationRenameLoading}
|
||||||
></RenameModal>
|
></RenameModal>
|
||||||
|
<ChatOverviewModal
|
||||||
|
visible={overviewVisible}
|
||||||
|
hideModal={hideOverviewModal}
|
||||||
|
dialog={currentRecord}
|
||||||
|
></ChatOverviewModal>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
import {
|
||||||
|
IConversation,
|
||||||
|
IDialog,
|
||||||
|
IStats,
|
||||||
|
IToken,
|
||||||
|
Message,
|
||||||
|
} from '@/interfaces/database/chat';
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import chatService from '@/services/chatService';
|
import chatService from '@/services/chatService';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { IClientConversation, IMessage } from './interface';
|
import { IClientConversation, IMessage } from './interface';
|
||||||
@ -13,6 +20,8 @@ export interface ChatModelState {
|
|||||||
currentDialog: IDialog;
|
currentDialog: IDialog;
|
||||||
conversationList: IConversation[];
|
conversationList: IConversation[];
|
||||||
currentConversation: IClientConversation;
|
currentConversation: IClientConversation;
|
||||||
|
tokenList: IToken[];
|
||||||
|
stats: IStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: DvaModel<ChatModelState> = {
|
const model: DvaModel<ChatModelState> = {
|
||||||
@ -23,6 +32,8 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
currentDialog: <IDialog>{},
|
currentDialog: <IDialog>{},
|
||||||
conversationList: [],
|
conversationList: [],
|
||||||
currentConversation: {} as IClientConversation,
|
currentConversation: {} as IClientConversation,
|
||||||
|
tokenList: [],
|
||||||
|
stats: {} as IStats,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
save(state, action) {
|
save(state, action) {
|
||||||
@ -60,6 +71,18 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
currentConversation: { ...payload, message: messageList },
|
currentConversation: { ...payload, message: messageList },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
setTokenList(state, { payload }) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tokenList: payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setStats(state, { payload }) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
stats: payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
@ -160,6 +183,78 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
}
|
}
|
||||||
return data.retcode;
|
return data.retcode;
|
||||||
},
|
},
|
||||||
|
*createToken({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.createToken, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'listToken',
|
||||||
|
payload: payload,
|
||||||
|
});
|
||||||
|
message.success(i18n.t('message.created'));
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*listToken({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.listToken, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'setTokenList',
|
||||||
|
payload: data.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*removeToken({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(
|
||||||
|
chatService.removeToken,
|
||||||
|
omit(payload, ['dialogId']),
|
||||||
|
);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'listToken',
|
||||||
|
payload: { dialog_id: payload.dialogId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*getStats({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(chatService.getStats, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'setStats',
|
||||||
|
payload: data.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*createExternalConversation({ payload }, { call, put }) {
|
||||||
|
const { data } = yield call(
|
||||||
|
chatService.createExternalConversation,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'getExternalConversation',
|
||||||
|
payload: { conversation_id: payload.conversationId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*getExternalConversation({ payload }, { call }) {
|
||||||
|
const { data } = yield call(
|
||||||
|
chatService.getExternalConversation,
|
||||||
|
null,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*completeExternalConversation({ payload }, { call }) {
|
||||||
|
const { data } = yield call(
|
||||||
|
chatService.completeExternalConversation,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,13 @@ const {
|
|||||||
completeConversation,
|
completeConversation,
|
||||||
listConversation,
|
listConversation,
|
||||||
removeConversation,
|
removeConversation,
|
||||||
|
createToken,
|
||||||
|
listToken,
|
||||||
|
removeToken,
|
||||||
|
getStats,
|
||||||
|
createExternalConversation,
|
||||||
|
getExternalConversation,
|
||||||
|
completeExternalConversation,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -51,6 +58,34 @@ const methods = {
|
|||||||
url: removeConversation,
|
url: removeConversation,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
},
|
},
|
||||||
|
createToken: {
|
||||||
|
url: createToken,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
listToken: {
|
||||||
|
url: listToken,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
removeToken: {
|
||||||
|
url: removeToken,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
getStats: {
|
||||||
|
url: getStats,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
createExternalConversation: {
|
||||||
|
url: createExternalConversation,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
getExternalConversation: {
|
||||||
|
url: getExternalConversation,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
completeExternalConversation: {
|
||||||
|
url: completeExternalConversation,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const chatService = registerServer<keyof typeof methods>(methods, request);
|
const chatService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ let api_host = `/v1`;
|
|||||||
export { api_host };
|
export { api_host };
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 用户
|
// user
|
||||||
login: `${api_host}/user/login`,
|
login: `${api_host}/user/login`,
|
||||||
logout: `${api_host}/user/logout`,
|
logout: `${api_host}/user/logout`,
|
||||||
register: `${api_host}/user/register`,
|
register: `${api_host}/user/register`,
|
||||||
@ -12,21 +12,21 @@ export default {
|
|||||||
tenant_info: `${api_host}/user/tenant_info`,
|
tenant_info: `${api_host}/user/tenant_info`,
|
||||||
set_tenant_info: `${api_host}/user/set_tenant_info`,
|
set_tenant_info: `${api_host}/user/set_tenant_info`,
|
||||||
|
|
||||||
// 模型管理
|
// llm model
|
||||||
factories_list: `${api_host}/llm/factories`,
|
factories_list: `${api_host}/llm/factories`,
|
||||||
llm_list: `${api_host}/llm/list`,
|
llm_list: `${api_host}/llm/list`,
|
||||||
my_llm: `${api_host}/llm/my_llms`,
|
my_llm: `${api_host}/llm/my_llms`,
|
||||||
set_api_key: `${api_host}/llm/set_api_key`,
|
set_api_key: `${api_host}/llm/set_api_key`,
|
||||||
add_llm: `${api_host}/llm/add_llm`,
|
add_llm: `${api_host}/llm/add_llm`,
|
||||||
|
|
||||||
//知识库管理
|
// knowledge base
|
||||||
kb_list: `${api_host}/kb/list`,
|
kb_list: `${api_host}/kb/list`,
|
||||||
create_kb: `${api_host}/kb/create`,
|
create_kb: `${api_host}/kb/create`,
|
||||||
update_kb: `${api_host}/kb/update`,
|
update_kb: `${api_host}/kb/update`,
|
||||||
rm_kb: `${api_host}/kb/rm`,
|
rm_kb: `${api_host}/kb/rm`,
|
||||||
get_kb_detail: `${api_host}/kb/detail`,
|
get_kb_detail: `${api_host}/kb/detail`,
|
||||||
|
|
||||||
// chunk管理
|
// chunk
|
||||||
chunk_list: `${api_host}/chunk/list`,
|
chunk_list: `${api_host}/chunk/list`,
|
||||||
create_chunk: `${api_host}/chunk/create`,
|
create_chunk: `${api_host}/chunk/create`,
|
||||||
set_chunk: `${api_host}/chunk/set`,
|
set_chunk: `${api_host}/chunk/set`,
|
||||||
@ -35,7 +35,7 @@ export default {
|
|||||||
rm_chunk: `${api_host}/chunk/rm`,
|
rm_chunk: `${api_host}/chunk/rm`,
|
||||||
retrieval_test: `${api_host}/chunk/retrieval_test`,
|
retrieval_test: `${api_host}/chunk/retrieval_test`,
|
||||||
|
|
||||||
// 文件管理
|
// document
|
||||||
upload: `${api_host}/document/upload`,
|
upload: `${api_host}/document/upload`,
|
||||||
get_document_list: `${api_host}/document/list`,
|
get_document_list: `${api_host}/document/list`,
|
||||||
document_change_status: `${api_host}/document/change_status`,
|
document_change_status: `${api_host}/document/change_status`,
|
||||||
@ -48,14 +48,22 @@ export default {
|
|||||||
get_document_file: `${api_host}/document/get`,
|
get_document_file: `${api_host}/document/get`,
|
||||||
document_upload: `${api_host}/document/upload`,
|
document_upload: `${api_host}/document/upload`,
|
||||||
|
|
||||||
|
// chat
|
||||||
setDialog: `${api_host}/dialog/set`,
|
setDialog: `${api_host}/dialog/set`,
|
||||||
getDialog: `${api_host}/dialog/get`,
|
getDialog: `${api_host}/dialog/get`,
|
||||||
removeDialog: `${api_host}/dialog/rm`,
|
removeDialog: `${api_host}/dialog/rm`,
|
||||||
listDialog: `${api_host}/dialog/list`,
|
listDialog: `${api_host}/dialog/list`,
|
||||||
|
|
||||||
setConversation: `${api_host}/conversation/set`,
|
setConversation: `${api_host}/conversation/set`,
|
||||||
getConversation: `${api_host}/conversation/get`,
|
getConversation: `${api_host}/conversation/get`,
|
||||||
listConversation: `${api_host}/conversation/list`,
|
listConversation: `${api_host}/conversation/list`,
|
||||||
removeConversation: `${api_host}/conversation/rm`,
|
removeConversation: `${api_host}/conversation/rm`,
|
||||||
completeConversation: `${api_host}/conversation/completion`,
|
completeConversation: `${api_host}/conversation/completion`,
|
||||||
|
// chat for external
|
||||||
|
createToken: `${api_host}/api/new_token`,
|
||||||
|
listToken: `${api_host}/api/token_list`,
|
||||||
|
removeToken: `${api_host}/api/rm`,
|
||||||
|
getStats: `${api_host}/api/stats`,
|
||||||
|
createExternalConversation: `${api_host}/api/new_conversation`,
|
||||||
|
getExternalConversation: `${api_host}/api/conversation`,
|
||||||
|
completeExternalConversation: `${api_host}/api/completion`,
|
||||||
};
|
};
|
||||||
|
|||||||
17
web/src/utils/commonUtil.ts
Normal file
17
web/src/utils/commonUtil.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import isObject from 'lodash/isObject';
|
||||||
|
import snakeCase from 'lodash/snakeCase';
|
||||||
|
|
||||||
|
export const isFormData = (data: unknown): data is FormData => {
|
||||||
|
return data instanceof FormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
|
||||||
|
if (isObject(data) && !isFormData(data)) {
|
||||||
|
return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
|
||||||
|
const value = (data as Record<string, any>)[cur];
|
||||||
|
pre[isFormData(value) ? cur : snakeCase(cur)] = value;
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@ -1,20 +1,20 @@
|
|||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export function today() {
|
export function today() {
|
||||||
return formatDate(moment());
|
return formatDate(dayjs());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lastDay() {
|
export function lastDay() {
|
||||||
return formatDate(moment().subtract(1, 'days'));
|
return formatDate(dayjs().subtract(1, 'days'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lastWeek() {
|
export function lastWeek() {
|
||||||
return formatDate(moment().subtract(1, 'weeks'));
|
return formatDate(dayjs().subtract(1, 'weeks'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatDate(date: any) {
|
export function formatDate(date: any) {
|
||||||
if (!date) {
|
if (!date) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return moment(date).format('DD/MM/YYYY');
|
return dayjs(date).format('DD/MM/YYYY');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,16 +8,20 @@ const registerServer = <T extends string>(
|
|||||||
) => {
|
) => {
|
||||||
const server: Service<T> = {} as Service<T>;
|
const server: Service<T> = {} as Service<T>;
|
||||||
for (let key in opt) {
|
for (let key in opt) {
|
||||||
server[key] = (params) => {
|
server[key] = (params: any, urlAppendix?: string) => {
|
||||||
|
let url = opt[key].url;
|
||||||
|
if (urlAppendix) {
|
||||||
|
url = url + '/' + urlAppendix;
|
||||||
|
}
|
||||||
if (opt[key].method === 'post' || opt[key].method === 'POST') {
|
if (opt[key].method === 'post' || opt[key].method === 'POST') {
|
||||||
return request(opt[key].url, {
|
return request(url, {
|
||||||
method: opt[key].method,
|
method: opt[key].method,
|
||||||
data: params,
|
data: params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt[key].method === 'get' || opt[key].method === 'GET') {
|
if (opt[key].method === 'get' || opt[key].method === 'GET') {
|
||||||
return request.get(opt[key].url, {
|
return request.get(url, {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import authorizationUtil from '@/utils/authorizationUtil';
|
|||||||
import { message, notification } from 'antd';
|
import { message, notification } from 'antd';
|
||||||
import { history } from 'umi';
|
import { history } from 'umi';
|
||||||
import { RequestMethod, extend } from 'umi-request';
|
import { RequestMethod, extend } from 'umi-request';
|
||||||
|
import { convertTheKeysOfTheObjectToSnake } from './commonUtil';
|
||||||
|
|
||||||
const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message
|
const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message
|
||||||
|
|
||||||
@ -87,10 +88,15 @@ const request: RequestMethod = extend({
|
|||||||
|
|
||||||
request.interceptors.request.use((url: string, options: any) => {
|
request.interceptors.request.use((url: string, options: any) => {
|
||||||
const authorization = authorizationUtil.getAuthorization();
|
const authorization = authorizationUtil.getAuthorization();
|
||||||
|
const data = convertTheKeysOfTheObjectToSnake(options.data);
|
||||||
|
const params = convertTheKeysOfTheObjectToSnake(options.params);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
options: {
|
options: {
|
||||||
...options,
|
...options,
|
||||||
|
// data,
|
||||||
|
// params,
|
||||||
headers: {
|
headers: {
|
||||||
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
||||||
...options.headers,
|
...options.headers,
|
||||||
|
|||||||
Reference in New Issue
Block a user