KevinHuSh
commited on
Commit
·
58d441f
1
Parent(s):
7943d52
refine error response, add set api-key MD (#178)
Browse files- api/apps/conversation_app.py +2 -0
- api/apps/kb_app.py +17 -2
- api/db/services/knowledgebase_service.py +1 -1
- api/utils/api_utils.py +3 -0
- docker/docker-compose-CN.yml +133 -0
- docs/llm_api_key_setup.md +19 -0
- rag/llm/chat_model.py +1 -1
- rag/llm/rpc_server.py +18 -0
api/apps/conversation_app.py
CHANGED
@@ -253,6 +253,8 @@ def chat(dialog, messages, **kwargs):
|
|
253 |
for c in kbinfos["chunks"]:
|
254 |
if c.get("vector"):
|
255 |
del c["vector"]
|
|
|
|
|
256 |
return {"answer": answer, "reference": kbinfos}
|
257 |
|
258 |
|
|
|
253 |
for c in kbinfos["chunks"]:
|
254 |
if c.get("vector"):
|
255 |
del c["vector"]
|
256 |
+
if answer.lower().find("invalid key") >= 0 or answer.lower().find("invalid api")>=0:
|
257 |
+
answer += " Please set LLM API-Key in 'User Setting -> Model Providers -> API-Key'"
|
258 |
return {"answer": answer, "reference": kbinfos}
|
259 |
|
260 |
|
api/apps/kb_app.py
CHANGED
@@ -13,10 +13,12 @@
|
|
13 |
# See the License for the specific language governing permissions and
|
14 |
# limitations under the License.
|
15 |
#
|
|
|
16 |
from flask import request
|
17 |
from flask_login import login_required, current_user
|
18 |
|
19 |
from api.db.services import duplicate_name
|
|
|
20 |
from api.db.services.user_service import TenantService, UserTenantService
|
21 |
from api.utils.api_utils import server_error_response, get_data_error_result, validate_request
|
22 |
from api.utils import get_uuid, get_format_time
|
@@ -25,6 +27,8 @@ from api.db.services.knowledgebase_service import KnowledgebaseService
|
|
25 |
from api.db.db_models import Knowledgebase
|
26 |
from api.settings import stat_logger, RetCode
|
27 |
from api.utils.api_utils import get_json_result
|
|
|
|
|
28 |
|
29 |
|
30 |
@manager.route('/create', methods=['post'])
|
@@ -125,11 +129,22 @@ def list():
|
|
125 |
def rm():
|
126 |
req = request.json
|
127 |
try:
|
128 |
-
|
129 |
-
created_by=current_user.id, id=req["kb_id"])
|
|
|
130 |
return get_json_result(
|
131 |
data=False, retmsg=f'Only owner of knowledgebase authorized for this operation.', retcode=RetCode.OPERATING_ERROR)
|
132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
if not KnowledgebaseService.update_by_id(
|
134 |
req["kb_id"], {"status": StatusEnum.INVALID.value}):
|
135 |
return get_data_error_result(
|
|
|
13 |
# See the License for the specific language governing permissions and
|
14 |
# limitations under the License.
|
15 |
#
|
16 |
+
from elasticsearch_dsl import Q
|
17 |
from flask import request
|
18 |
from flask_login import login_required, current_user
|
19 |
|
20 |
from api.db.services import duplicate_name
|
21 |
+
from api.db.services.document_service import DocumentService
|
22 |
from api.db.services.user_service import TenantService, UserTenantService
|
23 |
from api.utils.api_utils import server_error_response, get_data_error_result, validate_request
|
24 |
from api.utils import get_uuid, get_format_time
|
|
|
27 |
from api.db.db_models import Knowledgebase
|
28 |
from api.settings import stat_logger, RetCode
|
29 |
from api.utils.api_utils import get_json_result
|
30 |
+
from rag.nlp import search
|
31 |
+
from rag.utils import ELASTICSEARCH
|
32 |
|
33 |
|
34 |
@manager.route('/create', methods=['post'])
|
|
|
129 |
def rm():
|
130 |
req = request.json
|
131 |
try:
|
132 |
+
kbs = KnowledgebaseService.query(
|
133 |
+
created_by=current_user.id, id=req["kb_id"])
|
134 |
+
if not kbs:
|
135 |
return get_json_result(
|
136 |
data=False, retmsg=f'Only owner of knowledgebase authorized for this operation.', retcode=RetCode.OPERATING_ERROR)
|
137 |
|
138 |
+
for doc in DocumentService.query(kb_id=req["kb_id"]):
|
139 |
+
ELASTICSEARCH.deleteByQuery(
|
140 |
+
Q("match", doc_id=doc.id), idxnm=search.index_name(kbs[0].tenant_id))
|
141 |
+
|
142 |
+
DocumentService.increment_chunk_num(
|
143 |
+
doc.id, doc.kb_id, doc.token_num * -1, doc.chunk_num * -1, 0)
|
144 |
+
if not DocumentService.delete(doc):
|
145 |
+
return get_data_error_result(
|
146 |
+
retmsg="Database error (Document removal)!")
|
147 |
+
|
148 |
if not KnowledgebaseService.update_by_id(
|
149 |
req["kb_id"], {"status": StatusEnum.INVALID.value}):
|
150 |
return get_data_error_result(
|
api/db/services/knowledgebase_service.py
CHANGED
@@ -62,7 +62,7 @@ class KnowledgebaseService(CommonService):
|
|
62 |
if not kbs:
|
63 |
return
|
64 |
d = kbs[0].to_dict()
|
65 |
-
d["embd_id"] = kbs[0].tenant.embd_id
|
66 |
return d
|
67 |
|
68 |
@classmethod
|
|
|
62 |
if not kbs:
|
63 |
return
|
64 |
d = kbs[0].to_dict()
|
65 |
+
#d["embd_id"] = kbs[0].tenant.embd_id
|
66 |
return d
|
67 |
|
68 |
@classmethod
|
api/utils/api_utils.py
CHANGED
@@ -149,6 +149,9 @@ def server_error_response(e):
|
|
149 |
if len(e.args) > 1:
|
150 |
return get_json_result(
|
151 |
retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e.args[0]), data=e.args[1])
|
|
|
|
|
|
|
152 |
return get_json_result(retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e))
|
153 |
|
154 |
|
|
|
149 |
if len(e.args) > 1:
|
150 |
return get_json_result(
|
151 |
retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e.args[0]), data=e.args[1])
|
152 |
+
if repr(e).find("index_not_found_exception") >=0:
|
153 |
+
return get_json_result(retcode=RetCode.EXCEPTION_ERROR, retmsg="No chunk found, please upload file and parse it.")
|
154 |
+
|
155 |
return get_json_result(retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e))
|
156 |
|
157 |
|
docker/docker-compose-CN.yml
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '2.2'
|
2 |
+
services:
|
3 |
+
es01:
|
4 |
+
container_name: ragflow-es-01
|
5 |
+
image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
|
6 |
+
volumes:
|
7 |
+
- esdata01:/usr/share/elasticsearch/data
|
8 |
+
ports:
|
9 |
+
- ${ES_PORT}:9200
|
10 |
+
environment:
|
11 |
+
- node.name=es01
|
12 |
+
- cluster.name=${CLUSTER_NAME}
|
13 |
+
- cluster.initial_master_nodes=es01
|
14 |
+
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
|
15 |
+
- bootstrap.memory_lock=false
|
16 |
+
- xpack.security.enabled=false
|
17 |
+
- TZ=${TIMEZONE}
|
18 |
+
mem_limit: ${MEM_LIMIT}
|
19 |
+
ulimits:
|
20 |
+
memlock:
|
21 |
+
soft: -1
|
22 |
+
hard: -1
|
23 |
+
healthcheck:
|
24 |
+
test: ["CMD-SHELL", "curl http://localhost:9200"]
|
25 |
+
interval: 10s
|
26 |
+
timeout: 10s
|
27 |
+
retries: 120
|
28 |
+
networks:
|
29 |
+
- ragflow
|
30 |
+
restart: always
|
31 |
+
|
32 |
+
kibana:
|
33 |
+
depends_on:
|
34 |
+
es01:
|
35 |
+
condition: service_healthy
|
36 |
+
image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
|
37 |
+
container_name: ragflow-kibana
|
38 |
+
volumes:
|
39 |
+
- kibanadata:/usr/share/kibana/data
|
40 |
+
ports:
|
41 |
+
- ${KIBANA_PORT}:5601
|
42 |
+
environment:
|
43 |
+
- SERVERNAME=kibana
|
44 |
+
- ELASTICSEARCH_HOSTS=http://es01:9200
|
45 |
+
- TZ=${TIMEZONE}
|
46 |
+
mem_limit: ${MEM_LIMIT}
|
47 |
+
networks:
|
48 |
+
- ragflow
|
49 |
+
|
50 |
+
mysql:
|
51 |
+
image: mysql:5.7.18
|
52 |
+
container_name: ragflow-mysql
|
53 |
+
environment:
|
54 |
+
- MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
|
55 |
+
- TZ=${TIMEZONE}
|
56 |
+
command:
|
57 |
+
--max_connections=1000
|
58 |
+
--character-set-server=utf8mb4
|
59 |
+
--collation-server=utf8mb4_general_ci
|
60 |
+
--default-authentication-plugin=mysql_native_password
|
61 |
+
--tls_version="TLSv1.2,TLSv1.3"
|
62 |
+
--init-file /data/application/init.sql
|
63 |
+
ports:
|
64 |
+
- ${MYSQL_PORT}:3306
|
65 |
+
volumes:
|
66 |
+
- mysql_data:/var/lib/mysql
|
67 |
+
- ./init.sql:/data/application/init.sql
|
68 |
+
networks:
|
69 |
+
- ragflow
|
70 |
+
healthcheck:
|
71 |
+
test: ["CMD", "mysqladmin" ,"ping", "-uroot", "-p${MYSQL_PASSWORD}"]
|
72 |
+
interval: 10s
|
73 |
+
timeout: 10s
|
74 |
+
retries: 3
|
75 |
+
restart: always
|
76 |
+
|
77 |
+
|
78 |
+
minio:
|
79 |
+
image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z
|
80 |
+
container_name: ragflow-minio
|
81 |
+
command: server --console-address ":9001" /data
|
82 |
+
ports:
|
83 |
+
- 9000:9000
|
84 |
+
- 9001:9001
|
85 |
+
environment:
|
86 |
+
- MINIO_ROOT_USER=${MINIO_USER}
|
87 |
+
- MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}
|
88 |
+
- TZ=${TIMEZONE}
|
89 |
+
volumes:
|
90 |
+
- minio_data:/data
|
91 |
+
networks:
|
92 |
+
- ragflow
|
93 |
+
restart: always
|
94 |
+
|
95 |
+
|
96 |
+
ragflow:
|
97 |
+
depends_on:
|
98 |
+
mysql:
|
99 |
+
condition: service_healthy
|
100 |
+
es01:
|
101 |
+
condition: service_healthy
|
102 |
+
image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v1.0
|
103 |
+
container_name: ragflow-server
|
104 |
+
ports:
|
105 |
+
- ${SVR_HTTP_PORT}:9380
|
106 |
+
- 80:80
|
107 |
+
- 443:443
|
108 |
+
volumes:
|
109 |
+
- ./service_conf.yaml:/ragflow/conf/service_conf.yaml
|
110 |
+
- ./ragflow-logs:/ragflow/logs
|
111 |
+
- ./nginx/ragflow.conf:/etc/nginx/conf.d/ragflow.conf
|
112 |
+
- ./nginx/proxy.conf:/etc/nginx/proxy.conf
|
113 |
+
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
114 |
+
environment:
|
115 |
+
- TZ=${TIMEZONE}
|
116 |
+
networks:
|
117 |
+
- ragflow
|
118 |
+
restart: always
|
119 |
+
|
120 |
+
|
121 |
+
volumes:
|
122 |
+
esdata01:
|
123 |
+
driver: local
|
124 |
+
kibanadata:
|
125 |
+
driver: local
|
126 |
+
mysql_data:
|
127 |
+
driver: local
|
128 |
+
minio_data:
|
129 |
+
driver: local
|
130 |
+
|
131 |
+
networks:
|
132 |
+
ragflow:
|
133 |
+
driver: bridge
|
docs/llm_api_key_setup.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
## Set Before Starting The System
|
3 |
+
|
4 |
+
In **user_default_llm** of [service_conf.yaml](./docker/service_conf.yaml), you need to specify LLM factory and your own _API_KEY_.
|
5 |
+
RagFlow supports the flowing LLM factory, and with more coming in the pipeline:
|
6 |
+
|
7 |
+
> [OpenAI](https://platform.openai.com/login?launch), [Tongyi-Qianwen](https://dashscope.console.aliyun.com/model),
|
8 |
+
> [ZHIPU-AI](https://open.bigmodel.cn/), [Moonshot](https://platform.moonshot.cn/docs/docs)
|
9 |
+
|
10 |
+
After sign in these LLM suppliers, create your own API-Key, they all have a certain amount of free quota.
|
11 |
+
|
12 |
+
## After Starting The System
|
13 |
+
|
14 |
+
You can also set API-Key in **User Setting** as following:
|
15 |
+
|
16 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
17 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/e4e4066c-e964-45ff-bd56-c3fc7fb18bd3" width="1000"/>
|
18 |
+
</div>
|
19 |
+
|
rag/llm/chat_model.py
CHANGED
@@ -156,7 +156,7 @@ class LocalLLM(Base):
|
|
156 |
|
157 |
return do_rpc
|
158 |
|
159 |
-
def __init__(self, **kwargs):
|
160 |
self.client = LocalLLM.RPCProxy("127.0.0.1", 7860)
|
161 |
|
162 |
def chat(self, system, history, gen_conf):
|
|
|
156 |
|
157 |
return do_rpc
|
158 |
|
159 |
+
def __init__(self, *args, **kwargs):
|
160 |
self.client = LocalLLM.RPCProxy("127.0.0.1", 7860)
|
161 |
|
162 |
def chat(self, system, history, gen_conf):
|
rag/llm/rpc_server.py
CHANGED
@@ -7,6 +7,23 @@ from threading import Thread
|
|
7 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
8 |
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
class RPCHandler:
|
11 |
def __init__(self):
|
12 |
self._functions = {}
|
@@ -49,6 +66,7 @@ def chat(messages, gen_conf):
|
|
49 |
global tokenizer
|
50 |
model = Model()
|
51 |
try:
|
|
|
52 |
conf = {
|
53 |
"max_new_tokens": int(
|
54 |
gen_conf.get(
|
|
|
7 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
8 |
|
9 |
|
10 |
+
def torch_gc():
|
11 |
+
try:
|
12 |
+
import torch
|
13 |
+
if torch.cuda.is_available():
|
14 |
+
# with torch.cuda.device(DEVICE):
|
15 |
+
torch.cuda.empty_cache()
|
16 |
+
torch.cuda.ipc_collect()
|
17 |
+
elif torch.backends.mps.is_available():
|
18 |
+
try:
|
19 |
+
from torch.mps import empty_cache
|
20 |
+
empty_cache()
|
21 |
+
except Exception as e:
|
22 |
+
pass
|
23 |
+
except Exception:
|
24 |
+
pass
|
25 |
+
|
26 |
+
|
27 |
class RPCHandler:
|
28 |
def __init__(self):
|
29 |
self._functions = {}
|
|
|
66 |
global tokenizer
|
67 |
model = Model()
|
68 |
try:
|
69 |
+
torch_gc()
|
70 |
conf = {
|
71 |
"max_new_tokens": int(
|
72 |
gen_conf.get(
|