import uuid import json import functools from marshmallow import Schema, ValidationError from flask import abort, make_response, jsonify, request from config import config from consts.consts import GPT_4, GPT_4_TURBO, GPT_4O, GPT_4O_MINI, WHITELIST_PUBLIC_KEYS from models.user_preference import UserPreference from utils.credential import get_org_ids_by_pubkey, apikey_has_permission from utils.local import set_local_metrics from utils.logging import logging RBAC_METHOD_MAPPER = { "get": "get", "post": "create", "put": "update", "delete": "delete", } def use_dataservice_resp_format() -> bool: return request.headers.get("X-Resp-Format") == "dataservice" def success(code=200, msg="", result=None): result = result or {} if use_dataservice_resp_format(): return {"code": code, "message": msg, "data": result}, code return {"code": code, "msg": msg, "result": result}, code def fail(code=400, msg="", result=None): result = result or {} if use_dataservice_resp_format(): return {"code": code, "message": msg, "data": result}, code return {"code": code, "msg": msg, "result": result}, code def abort_with(code, msg, result=None): result = result or {} if use_dataservice_resp_format(): abort(make_response(jsonify(code=code, message=msg, data=result), code)) else: abort(make_response(jsonify(code=code, msg=msg, result=result), code)) def check_org_id(org_id_saved: int): if config.DISABLE_PERMISSION: return org_id_saved org_id = get_org_id_from_header() if str(org_id_saved) != str(org_id): abort_with(403, "permission denied: org_id not match", None) return org_id def check_schema(schema: Schema, data: dict): try: return schema().load(data) except ValidationError as e: abort_with(400, str(e.messages), None) def get_endpoint_url(): url_prefixs = ( "/api/v1beta/apps/eda/context", "/api/v1beta/apps/", "/inner/eda/api/v1beta/app//endpoint", ) url_replacements = ( ("", "}") app_id = get_app_id() action = RBAC_METHOD_MAPPER[request.method.lower()] api_key = get_access_key() if endpoint_url == "/v2/executeQuery": endpoint_url = "/v2/executeSQL" if endpoint_url == "/v2/feedback": endpoint_url = "/v2/chat2data" # assemble endpoint_url + app_id to resource_uri # for example: /dataapi/dataapps/{app_id}/v2/dataSummaries:create resource_uri = f"/dataapi/dataapps/{app_id}{endpoint_url}:{action}" if not apikey_has_permission(api_key, resource_uri): return fail(403, "permission denied: please check your api key has property role and api key is in your request app") override_ai_model = get_override_ai_model() if override_ai_model: app_preference = UserPreference.load(app_id) if app_preference.llm_api_key == "" and override_ai_model in (GPT_4, GPT_4_TURBO): return fail(403, "permission denied: to use gpt-4 series model, please set LLM API Key in chat2query settings first") return func(*args, **kwargs) return wrapper def inject_metrics_for_oldapi(session_context_id, data_context_id): access_key = get_access_key() org_ids = get_org_ids_by_pubkey(access_key) org_id = str(org_ids[0]) if len(org_ids) else None metrics = { "org_id": org_id, "app_id": get_app_id(), "endpoint": get_endpoint(), "app_key": get_access_key(), "job_id": "", "debug_action": get_debug_action(), "session_context_id": str(session_context_id), "data_context_id": str(data_context_id), } set_local_metrics(metrics) def inject_metrics(session_context_id, data_context_id): metrics = { "org_id": get_org_id_from_header(), "app_id": get_app_id(), "endpoint": get_endpoint(), "app_key": get_access_key(), "job_id": "", "debug_action": get_debug_action(), "session_context_id": str(session_context_id), "data_context_id": str(data_context_id), } set_local_metrics(metrics) def get_limit_offset(): page = request.args.get("page", 1, type=int) per_page = request.args.get("per_page", 200, type=int) if not (1 <= page <= 1000): abort_with(400, "page must be in [1, 1000]", None) if not (1 <= per_page <= 200): abort_with(400, "per_page must be in [1, 200]", None) limit = per_page offset = (page - 1) * per_page return limit, offset def get_session_id(): session_id = request.headers.get("X-Session-ID", "", type=str) if not session_id: session_id = uuid.uuid4().hex logging.info("using new session_id: %s", session_id) else: logging.info("using session_id from header: %s", session_id) return session_id def get_trace_id() -> str: return request.headers.get("B3", "", type=str)