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)) | |