Spaces:
Paused
Paused
| # | |
| # Copyright 2024 The InfiniFlow Authors. All Rights Reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| import json | |
| import random | |
| import time | |
| from functools import wraps | |
| from io import BytesIO | |
| from flask import ( | |
| Response, jsonify, send_file, make_response, | |
| request as flask_request, | |
| ) | |
| from werkzeug.http import HTTP_STATUS_CODES | |
| from api.utils import json_dumps | |
| from api.settings import RetCode | |
| from api.settings import ( | |
| REQUEST_MAX_WAIT_SEC, REQUEST_WAIT_SEC, | |
| stat_logger, CLIENT_AUTHENTICATION, HTTP_APP_KEY, SECRET_KEY | |
| ) | |
| import requests | |
| import functools | |
| from api.utils import CustomJSONEncoder | |
| from uuid import uuid1 | |
| from base64 import b64encode | |
| from hmac import HMAC | |
| from urllib.parse import quote, urlencode | |
| requests.models.complexjson.dumps = functools.partial( | |
| json.dumps, cls=CustomJSONEncoder) | |
| def request(**kwargs): | |
| sess = requests.Session() | |
| stream = kwargs.pop('stream', sess.stream) | |
| timeout = kwargs.pop('timeout', None) | |
| kwargs['headers'] = { | |
| k.replace( | |
| '_', | |
| '-').upper(): v for k, | |
| v in kwargs.get( | |
| 'headers', | |
| {}).items()} | |
| prepped = requests.Request(**kwargs).prepare() | |
| if CLIENT_AUTHENTICATION and HTTP_APP_KEY and SECRET_KEY: | |
| timestamp = str(round(time() * 1000)) | |
| nonce = str(uuid1()) | |
| signature = b64encode(HMAC(SECRET_KEY.encode('ascii'), b'\n'.join([ | |
| timestamp.encode('ascii'), | |
| nonce.encode('ascii'), | |
| HTTP_APP_KEY.encode('ascii'), | |
| prepped.path_url.encode('ascii'), | |
| prepped.body if kwargs.get('json') else b'', | |
| urlencode( | |
| sorted( | |
| kwargs['data'].items()), | |
| quote_via=quote, | |
| safe='-._~').encode('ascii') | |
| if kwargs.get('data') and isinstance(kwargs['data'], dict) else b'', | |
| ]), 'sha1').digest()).decode('ascii') | |
| prepped.headers.update({ | |
| 'TIMESTAMP': timestamp, | |
| 'NONCE': nonce, | |
| 'APP-KEY': HTTP_APP_KEY, | |
| 'SIGNATURE': signature, | |
| }) | |
| return sess.send(prepped, stream=stream, timeout=timeout) | |
| def get_exponential_backoff_interval(retries, full_jitter=False): | |
| """Calculate the exponential backoff wait time.""" | |
| # Will be zero if factor equals 0 | |
| countdown = min(REQUEST_MAX_WAIT_SEC, REQUEST_WAIT_SEC * (2 ** retries)) | |
| # Full jitter according to | |
| # https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ | |
| if full_jitter: | |
| countdown = random.randrange(countdown + 1) | |
| # Adjust according to maximum wait time and account for negative values. | |
| return max(0, countdown) | |
| def get_json_result(retcode=RetCode.SUCCESS, retmsg='success', | |
| data=None, job_id=None, meta=None): | |
| import re | |
| result_dict = { | |
| "retcode": retcode, | |
| "retmsg": retmsg, | |
| # "retmsg": re.sub(r"rag", "seceum", retmsg, flags=re.IGNORECASE), | |
| "data": data, | |
| "jobId": job_id, | |
| "meta": meta, | |
| } | |
| response = {} | |
| for key, value in result_dict.items(): | |
| if value is None and key != "retcode": | |
| continue | |
| else: | |
| response[key] = value | |
| return jsonify(response) | |
| def get_data_error_result(retcode=RetCode.DATA_ERROR, | |
| retmsg='Sorry! Data missing!'): | |
| import re | |
| result_dict = { | |
| "retcode": retcode, | |
| "retmsg": re.sub( | |
| r"rag", | |
| "seceum", | |
| retmsg, | |
| flags=re.IGNORECASE)} | |
| response = {} | |
| for key, value in result_dict.items(): | |
| if value is None and key != "retcode": | |
| continue | |
| else: | |
| response[key] = value | |
| return jsonify(response) | |
| def server_error_response(e): | |
| stat_logger.exception(e) | |
| try: | |
| if e.code == 401: | |
| return get_json_result(retcode=401, retmsg=repr(e)) | |
| except BaseException: | |
| pass | |
| if len(e.args) > 1: | |
| return get_json_result( | |
| retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e.args[0]), data=e.args[1]) | |
| if repr(e).find("index_not_found_exception") >= 0: | |
| return get_json_result(retcode=RetCode.EXCEPTION_ERROR, retmsg="No chunk found, please upload file and parse it.") | |
| return get_json_result(retcode=RetCode.EXCEPTION_ERROR, retmsg=repr(e)) | |
| def error_response(response_code, retmsg=None): | |
| if retmsg is None: | |
| retmsg = HTTP_STATUS_CODES.get(response_code, 'Unknown Error') | |
| return Response(json.dumps({ | |
| 'retmsg': retmsg, | |
| 'retcode': response_code, | |
| }), status=response_code, mimetype='application/json') | |
| def validate_request(*args, **kwargs): | |
| def wrapper(func): | |
| def decorated_function(*_args, **_kwargs): | |
| input_arguments = flask_request.json or flask_request.form.to_dict() | |
| no_arguments = [] | |
| error_arguments = [] | |
| for arg in args: | |
| if arg not in input_arguments: | |
| no_arguments.append(arg) | |
| for k, v in kwargs.items(): | |
| config_value = input_arguments.get(k, None) | |
| if config_value is None: | |
| no_arguments.append(k) | |
| elif isinstance(v, (tuple, list)): | |
| if config_value not in v: | |
| error_arguments.append((k, set(v))) | |
| elif config_value != v: | |
| error_arguments.append((k, v)) | |
| if no_arguments or error_arguments: | |
| error_string = "" | |
| if no_arguments: | |
| error_string += "required argument are missing: {}; ".format( | |
| ",".join(no_arguments)) | |
| if error_arguments: | |
| error_string += "required argument values: {}".format( | |
| ",".join(["{}={}".format(a[0], a[1]) for a in error_arguments])) | |
| return get_json_result( | |
| retcode=RetCode.ARGUMENT_ERROR, retmsg=error_string) | |
| return func(*_args, **_kwargs) | |
| return decorated_function | |
| return wrapper | |
| def is_localhost(ip): | |
| return ip in {'127.0.0.1', '::1', '[::1]', 'localhost'} | |
| def send_file_in_mem(data, filename): | |
| if not isinstance(data, (str, bytes)): | |
| data = json_dumps(data) | |
| if isinstance(data, str): | |
| data = data.encode('utf-8') | |
| f = BytesIO() | |
| f.write(data) | |
| f.seek(0) | |
| return send_file(f, as_attachment=True, attachment_filename=filename) | |
| def get_json_result(retcode=RetCode.SUCCESS, retmsg='success', data=None): | |
| response = {"retcode": retcode, "retmsg": retmsg, "data": data} | |
| return jsonify(response) | |
| def cors_reponse(retcode=RetCode.SUCCESS, | |
| retmsg='success', data=None, auth=None): | |
| result_dict = {"retcode": retcode, "retmsg": retmsg, "data": data} | |
| response_dict = {} | |
| for key, value in result_dict.items(): | |
| if value is None and key != "retcode": | |
| continue | |
| else: | |
| response_dict[key] = value | |
| response = make_response(jsonify(response_dict)) | |
| if auth: | |
| response.headers["Authorization"] = auth | |
| response.headers["Access-Control-Allow-Origin"] = "*" | |
| response.headers["Access-Control-Allow-Method"] = "*" | |
| response.headers["Access-Control-Allow-Headers"] = "*" | |
| response.headers["Access-Control-Allow-Headers"] = "*" | |
| response.headers["Access-Control-Expose-Headers"] = "Authorization" | |
| return response | |
| def construct_result(code=RetCode.DATA_ERROR, message='data is missing'): | |
| import re | |
| result_dict = {"code": code, "message": re.sub(r"rag", "seceum", message, flags=re.IGNORECASE)} | |
| response = {} | |
| for key, value in result_dict.items(): | |
| if value is None and key != "code": | |
| continue | |
| else: | |
| response[key] = value | |
| return jsonify(response) | |
| def construct_json_result(code=RetCode.SUCCESS, message='success', data=None): | |
| if data is None: | |
| return jsonify({"code": code, "message": message}) | |
| else: | |
| return jsonify({"code": code, "message": message, "data": data}) | |
| def construct_error_response(e): | |
| stat_logger.exception(e) | |
| try: | |
| if e.code == 401: | |
| return construct_json_result(code=RetCode.UNAUTHORIZED, message=repr(e)) | |
| except BaseException: | |
| pass | |
| if len(e.args) > 1: | |
| return construct_json_result(code=RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=e.args[1]) | |
| if repr(e).find("index_not_found_exception") >=0: | |
| return construct_json_result(code=RetCode.EXCEPTION_ERROR, message="No chunk found, please upload file and parse it.") | |
| return construct_json_result(code=RetCode.EXCEPTION_ERROR, message=repr(e)) | |