### What problem does this PR solve? feat: API access key management https://github.com/infiniflow/ragflow/issues/2846 feat: Render markdown file with remark-loader https://github.com/infiniflow/ragflow/issues/2846 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.13.0
| from flask import request, Response | from flask import request, Response | ||||
| from flask_login import login_required, current_user | from flask_login import login_required, current_user | ||||
| from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService | from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService | ||||
| from api.db.services.dialog_service import full_question | |||||
| from api.db.services.user_service import TenantService | |||||
| from api.settings import RetCode | from api.settings import RetCode | ||||
| from api.utils import get_uuid | from api.utils import get_uuid | ||||
| from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result | from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result | ||||
| if "message" in req: | if "message" in req: | ||||
| canvas.messages.append({"role": "user", "content": req["message"], "id": message_id}) | canvas.messages.append({"role": "user", "content": req["message"], "id": message_id}) | ||||
| if len([m for m in canvas.messages if m["role"] == "user"]) > 1: | if len([m for m in canvas.messages if m["role"] == "user"]) > 1: | ||||
| ten = TenantService.get_by_user_id(current_user.id)[0] | |||||
| #ten = TenantService.get_info_by(current_user.id)[0] | |||||
| #req["message"] = full_question(ten["tenant_id"], ten["llm_id"], canvas.messages) | #req["message"] = full_question(ten["tenant_id"], ten["llm_id"], canvas.messages) | ||||
| pass | |||||
| canvas.add_user_input(req["message"]) | canvas.add_user_input(req["message"]) | ||||
| answer = canvas.run(stream=stream) | answer = canvas.run(stream=stream) | ||||
| print(canvas) | print(canvas) |
| req = request.json | req = request.json | ||||
| text = req["text"] | text = req["text"] | ||||
| tenants = TenantService.get_by_user_id(current_user.id) | |||||
| tenants = TenantService.get_info_by(current_user.id) | |||||
| if not tenants: | if not tenants: | ||||
| return get_data_error_result(retmsg="Tenant not found!") | return get_data_error_result(retmsg="Tenant not found!") | ||||
| return server_error_response(e) | return server_error_response(e) | ||||
| @manager.route('/rm', methods=['POST']) | |||||
| @validate_request("tokens", "tenant_id") | |||||
| @manager.route('/token/<token>', methods=['DELETE']) | |||||
| @login_required | @login_required | ||||
| def rm(): | |||||
| req = request.json | |||||
| try: | |||||
| for token in req["tokens"]: | |||||
| APITokenService.filter_delete( | |||||
| [APIToken.tenant_id == req["tenant_id"], APIToken.token == token]) | |||||
| return get_json_result(data=True) | |||||
| except Exception as e: | |||||
| return server_error_response(e) | |||||
| def rm(token): | |||||
| APITokenService.filter_delete( | |||||
| [APIToken.tenant_id == current_user.id, APIToken.token == token]) | |||||
| return get_json_result(data=True) |
| # | # | ||||
| from flask import request | from flask import request | ||||
| from flask_login import current_user, login_required | |||||
| from flask_login import login_required, current_user | |||||
| from api.db import UserTenantRole, StatusEnum | from api.db import UserTenantRole, StatusEnum | ||||
| from api.db.db_models import UserTenant | from api.db.db_models import UserTenant | ||||
| from api.db.services.user_service import TenantService, UserTenantService | |||||
| from api.settings import RetCode | |||||
| from api.db.services.user_service import UserTenantService, UserService | |||||
| from api.utils import get_uuid | |||||
| from api.utils.api_utils import get_json_result, validate_request, server_error_response | |||||
| @manager.route("/list", methods=["GET"]) | |||||
| @login_required | |||||
| def tenant_list(): | |||||
| try: | |||||
| tenants = TenantService.get_by_user_id(current_user.id) | |||||
| return get_json_result(data=tenants) | |||||
| except Exception as e: | |||||
| return server_error_response(e) | |||||
| from api.utils import get_uuid, delta_seconds | |||||
| from api.utils.api_utils import get_json_result, validate_request, server_error_response, get_data_error_result | |||||
| @manager.route("/<tenant_id>/user/list", methods=["GET"]) | @manager.route("/<tenant_id>/user/list", methods=["GET"]) | ||||
| def user_list(tenant_id): | def user_list(tenant_id): | ||||
| try: | try: | ||||
| users = UserTenantService.get_by_tenant_id(tenant_id) | users = UserTenantService.get_by_tenant_id(tenant_id) | ||||
| for u in users: | |||||
| u["delta_seconds"] = delta_seconds(u["update_date"]) | |||||
| return get_json_result(data=users) | return get_json_result(data=users) | ||||
| except Exception as e: | except Exception as e: | ||||
| return server_error_response(e) | return server_error_response(e) | ||||
| @manager.route('/<tenant_id>/user', methods=['POST']) | @manager.route('/<tenant_id>/user', methods=['POST']) | ||||
| @login_required | @login_required | ||||
| @validate_request("user_id") | |||||
| @validate_request("email") | |||||
| def create(tenant_id): | def create(tenant_id): | ||||
| user_id = request.json.get("user_id") | |||||
| if not user_id: | |||||
| return get_json_result( | |||||
| data=False, retmsg='Lack of "USER ID"', retcode=RetCode.ARGUMENT_ERROR) | |||||
| req = request.json | |||||
| usrs = UserService.query(email=req["email"]) | |||||
| if not usrs: | |||||
| return get_data_error_result(retmsg="User not found.") | |||||
| try: | |||||
| user_tenants = UserTenantService.query(user_id=user_id, tenant_id=tenant_id) | |||||
| if user_tenants: | |||||
| uuid = user_tenants[0].id | |||||
| return get_json_result(data={"id": uuid}) | |||||
| uuid = get_uuid() | |||||
| UserTenantService.save( | |||||
| id = uuid, | |||||
| user_id = user_id, | |||||
| tenant_id = tenant_id, | |||||
| role = UserTenantRole.NORMAL.value, | |||||
| status = StatusEnum.VALID.value) | |||||
| return get_json_result(data={"id": uuid}) | |||||
| except Exception as e: | |||||
| return server_error_response(e) | |||||
| user_id = usrs[0].id | |||||
| user_tenants = UserTenantService.query(user_id=user_id, tenant_id=tenant_id) | |||||
| if user_tenants: | |||||
| if user_tenants[0].status == UserTenantRole.NORMAL.value: | |||||
| return get_data_error_result(retmsg="This user is in the team already.") | |||||
| return get_data_error_result(retmsg="Invitation notification is sent.") | |||||
| UserTenantService.save( | |||||
| id=get_uuid(), | |||||
| user_id=user_id, | |||||
| tenant_id=tenant_id, | |||||
| role=UserTenantRole.INVITE, | |||||
| status=StatusEnum.VALID.value) | |||||
| usr = list(usrs.dicts())[0] | |||||
| usr = {k: v for k, v in usr.items() if k in ["id", "avatar", "email", "nickname"]} | |||||
| return get_json_result(data=usr) | |||||
| @manager.route('/<tenant_id>/user/<user_id>', methods=['DELETE']) | @manager.route('/<tenant_id>/user/<user_id>', methods=['DELETE']) | ||||
| return get_json_result(data=True) | return get_json_result(data=True) | ||||
| except Exception as e: | except Exception as e: | ||||
| return server_error_response(e) | return server_error_response(e) | ||||
| @manager.route("/list", methods=["GET"]) | |||||
| @login_required | |||||
| def tenant_list(): | |||||
| try: | |||||
| users = UserTenantService.get_tenants_by_user_id(current_user.id) | |||||
| for u in users: | |||||
| u["delta_seconds"] = delta_seconds(u["update_date"]) | |||||
| return get_json_result(data=users) | |||||
| except Exception as e: | |||||
| return server_error_response(e) | |||||
| @manager.route("/agree/<tenant_id>", methods=["GET"]) | |||||
| @login_required | |||||
| def agree(tenant_id): | |||||
| try: | |||||
| UserTenantService.filter_update([UserTenant.tenant_id == tenant_id, UserTenant.user_id == current_user.id], {"role": UserTenantRole.NORMAL}) | |||||
| return get_json_result(data=True) | |||||
| except Exception as e: | |||||
| return server_error_response(e) |
| from api.db.db_models import TenantLLM | from api.db.db_models import TenantLLM | ||||
| from api.db.services.llm_service import TenantLLMService, LLMService | from api.db.services.llm_service import TenantLLMService, LLMService | ||||
| from api.utils.api_utils import server_error_response, validate_request | |||||
| from api.utils.api_utils import server_error_response, validate_request, get_data_error_result | |||||
| from api.utils import get_uuid, get_format_time, decrypt, download_img, current_timestamp, datetime_format | from api.utils import get_uuid, get_format_time, decrypt, download_img, current_timestamp, datetime_format | ||||
| from api.db import UserTenantRole, LLMType, FileType | from api.db import UserTenantRole, LLMType, FileType | ||||
| from api.settings import RetCode, GITHUB_OAUTH, FEISHU_OAUTH, CHAT_MDL, EMBEDDING_MDL, ASR_MDL, IMAGE2TEXT_MDL, PARSERS, \ | from api.settings import RetCode, GITHUB_OAUTH, FEISHU_OAUTH, CHAT_MDL, EMBEDDING_MDL, ASR_MDL, IMAGE2TEXT_MDL, PARSERS, \ | ||||
| @login_required | @login_required | ||||
| def tenant_info(): | def tenant_info(): | ||||
| try: | try: | ||||
| tenants = TenantService.get_by_user_id(current_user.id)[0] | |||||
| return get_json_result(data=tenants) | |||||
| tenants = TenantService.get_info_by(current_user.id) | |||||
| if not tenants: | |||||
| return get_data_error_result(retmsg="Tenant not found!") | |||||
| return get_json_result(data=tenants[0]) | |||||
| except Exception as e: | except Exception as e: | ||||
| return server_error_response(e) | return server_error_response(e) | ||||
| OWNER = 'owner' | OWNER = 'owner' | ||||
| ADMIN = 'admin' | ADMIN = 'admin' | ||||
| NORMAL = 'normal' | NORMAL = 'normal' | ||||
| INVITE = 'invite' | |||||
| class TenantPermission(StrEnum): | class TenantPermission(StrEnum): |
| elif finished: | elif finished: | ||||
| if d["parser_config"].get("raptor", {}).get("use_raptor") and d["progress_msg"].lower().find(" raptor")<0: | if d["parser_config"].get("raptor", {}).get("use_raptor") and d["progress_msg"].lower().find(" raptor")<0: | ||||
| queue_raptor_tasks(d) | queue_raptor_tasks(d) | ||||
| prg *= 0.98 | |||||
| prg = 0.98 * len(tsks)/(len(tsks)+1) | |||||
| msg.append("------ RAPTOR -------") | msg.append("------ RAPTOR -------") | ||||
| else: | else: | ||||
| status = TaskStatus.DONE.value | status = TaskStatus.DONE.value | ||||
| info["progress_msg"] = msg | info["progress_msg"] = msg | ||||
| cls.update_by_id(d["id"], info) | cls.update_by_id(d["id"], info) | ||||
| except Exception as e: | except Exception as e: | ||||
| stat_logger.error("fetch task exception:" + str(e)) | |||||
| if str(e).find("'0'") < 0: | |||||
| stat_logger.error("fetch task exception:" + str(e)) | |||||
| @classmethod | @classmethod | ||||
| @DB.connection_context() | @DB.connection_context() |
| @classmethod | @classmethod | ||||
| @DB.connection_context() | @DB.connection_context() | ||||
| def get_by_user_id(cls, user_id): | |||||
| def get_info_by(cls, user_id): | |||||
| fields = [ | fields = [ | ||||
| cls.model.id.alias("tenant_id"), | cls.model.id.alias("tenant_id"), | ||||
| cls.model.name, | cls.model.name, | ||||
| cls.model.parser_ids, | cls.model.parser_ids, | ||||
| UserTenant.role] | UserTenant.role] | ||||
| return list(cls.model.select(*fields) | return list(cls.model.select(*fields) | ||||
| .join(UserTenant, on=((cls.model.id == UserTenant.tenant_id) & (UserTenant.user_id == user_id) & (UserTenant.status == StatusEnum.VALID.value))) | |||||
| .join(UserTenant, on=((cls.model.id == UserTenant.tenant_id) & (UserTenant.user_id == user_id) & (UserTenant.status == StatusEnum.VALID.value) & (UserTenant.role == UserTenantRole.OWNER))) | |||||
| .where(cls.model.status == StatusEnum.VALID.value).dicts()) | .where(cls.model.status == StatusEnum.VALID.value).dicts()) | ||||
| @classmethod | @classmethod | ||||
| cls.model.img2txt_id, | cls.model.img2txt_id, | ||||
| UserTenant.role] | UserTenant.role] | ||||
| return list(cls.model.select(*fields) | return list(cls.model.select(*fields) | ||||
| .join(UserTenant, on=((cls.model.id == UserTenant.tenant_id) & (UserTenant.user_id == user_id) & (UserTenant.status == StatusEnum.VALID.value) & (UserTenant.role == UserTenantRole.NORMAL.value))) | |||||
| .join(UserTenant, on=((cls.model.id == UserTenant.tenant_id) & (UserTenant.user_id == user_id) & (UserTenant.status == StatusEnum.VALID.value) & (UserTenant.role == UserTenantRole.NORMAL))) | |||||
| .where(cls.model.status == StatusEnum.VALID.value).dicts()) | .where(cls.model.status == StatusEnum.VALID.value).dicts()) | ||||
| @classmethod | @classmethod | ||||
| def get_by_tenant_id(cls, tenant_id): | def get_by_tenant_id(cls, tenant_id): | ||||
| fields = [ | fields = [ | ||||
| cls.model.user_id, | cls.model.user_id, | ||||
| cls.model.tenant_id, | |||||
| cls.model.role, | |||||
| cls.model.status, | cls.model.status, | ||||
| cls.model.role, | |||||
| User.nickname, | User.nickname, | ||||
| User.email, | User.email, | ||||
| User.avatar, | User.avatar, | ||||
| User.is_active, | User.is_active, | ||||
| User.is_anonymous, | User.is_anonymous, | ||||
| User.status, | User.status, | ||||
| User.update_date, | |||||
| User.is_superuser] | User.is_superuser] | ||||
| return list(cls.model.select(*fields) | return list(cls.model.select(*fields) | ||||
| .join(User, on=((cls.model.user_id == User.id) & (cls.model.status == StatusEnum.VALID.value))) | |||||
| .join(User, on=((cls.model.user_id == User.id) & (cls.model.status == StatusEnum.VALID.value) & (cls.model.role != UserTenantRole.OWNER))) | |||||
| .where(cls.model.tenant_id == tenant_id) | .where(cls.model.tenant_id == tenant_id) | ||||
| .dicts()) | |||||
| .dicts()) | |||||
| @classmethod | |||||
| @DB.connection_context() | |||||
| def get_tenants_by_user_id(cls, user_id): | |||||
| fields = [ | |||||
| cls.model.tenant_id, | |||||
| cls.model.role, | |||||
| User.nickname, | |||||
| User.email, | |||||
| User.avatar, | |||||
| User.update_date | |||||
| ] | |||||
| return list(cls.model.select(*fields) | |||||
| .join(User, on=((cls.model.tenant_id == User.id) & (UserTenant.user_id == user_id) & (UserTenant.status == StatusEnum.VALID.value))) | |||||
| .where(cls.model.status == StatusEnum.VALID.value).dicts()) |
| def update_progress(): | def update_progress(): | ||||
| while True: | while True: | ||||
| time.sleep(1) | |||||
| time.sleep(3) | |||||
| try: | try: | ||||
| DocumentService.update_progress() | DocumentService.update_progress() | ||||
| except Exception as e: | except Exception as e: |
| return "data:" + \ | return "data:" + \ | ||||
| response.headers.get('Content-Type', 'image/jpg') + ";" + \ | response.headers.get('Content-Type', 'image/jpg') + ";" + \ | ||||
| "base64," + base64.b64encode(response.content).decode("utf-8") | "base64," + base64.b64encode(response.content).decode("utf-8") | ||||
| def delta_seconds(date_string: str): | |||||
| dt = datetime.datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S") | |||||
| return (datetime.datetime.now() - dt).total_seconds() |
| layers = [(0, len(chunks))] | layers = [(0, len(chunks))] | ||||
| start, end = 0, len(chunks) | start, end = 0, len(chunks) | ||||
| if len(chunks) <= 1: return | if len(chunks) <= 1: return | ||||
| chunks = [(s, a) for s, a in chunks if len(a) > 0] | |||||
| def summarize(ck_idx, lock): | def summarize(ck_idx, lock): | ||||
| nonlocal chunks | nonlocal chunks | ||||
| print("SUM:", cnt) | print("SUM:", cnt) | ||||
| embds, _ = self._embd_model.encode([cnt]) | embds, _ = self._embd_model.encode([cnt]) | ||||
| with lock: | with lock: | ||||
| if not len(embds[0]): return | |||||
| chunks.append((cnt, embds[0])) | chunks.append((cnt, embds[0])) | ||||
| except Exception as e: | except Exception as e: | ||||
| print(e, flush=True) | print(e, flush=True) |