aboutsummaryrefslogtreecommitdiff
path: root/gn3/llms/client.py
blob: fbbddf0e2d76efd10238835c24c6ab7689e82dd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Module  Contains code for making request to fahamu Api"""
# pylint: disable=C0301
import json
import time
from requests import Session
from requests.adapters import HTTPAdapter
from requests.adapters import Retry

from gn3.llms.errors import LLMError


class TimeoutHTTPAdapter(HTTPAdapter):
    """Set a default timeout for HTTP calls """
    def __init__(self, timeout, *args, **kwargs):
        """TimeoutHTTPAdapter constructor."""
        self.timeout = timeout
        super().__init__(*args, **kwargs)

    def send(self, *args, **kwargs):
        """Override :obj:`HTTPAdapter` send method to add a default timeout."""
        kwargs["timeout"] = (
            kwargs["timeout"] if kwargs.get("timeout") else self.timeout
        )
        return super().send(*args, **kwargs)


class GeneNetworkQAClient(Session):
    """GeneNetworkQA Client

    This class provides a client object interface to the GeneNetworkQA API.
    It extends the `requests.Session` class and includes authorization,
    base URL,
    request timeouts, and request retries.

    Args:
        api_key (str): API key.
        timeout (int, optional): Timeout value, defaults to 5.
        total_retries (int, optional): Total retries value, defaults to 5.
        backoff_factor (int, optional): Retry backoff factor value,
    defaults to 30.

    Usage:
        from genenetworkqa import GeneNetworkQAClient
        gnqa = GeneNetworkQAClient(account="account-name",
    api_key="XXXXXXXXXXXXXXXXXXX...")
    """

    def __init__(self, api_key, timeout=30,
                 total_retries=5, backoff_factor=2):
        super().__init__()
        self.headers.update(
            {"Authorization": "Bearer " + api_key})
        self.base_url = "https://genenetwork.fahamuai.com/api/tasks"
        self.answer_url = f"{self.base_url}/answers"
        self.feedback_url = f"{self.base_url}/feedback"
        self.query = ""

        adapter = TimeoutHTTPAdapter(
            timeout=timeout,
            max_retries=Retry(
                total=total_retries,
                status_forcelist=[429, 500, 502, 503, 504],
                backoff_factor=backoff_factor,
            ),
        )

        self.mount("https://", adapter)
        self.mount("http://", adapter)

    @staticmethod
    def negative_status_msg(response):
        """ handler for non 200 response from fahamu api"""
        return f"Error: Status code -{response.status_code}- Reason::{response.reason}"

    def ask(self, ex_url, query,  *args, **kwargs):
        """fahamu ask api interface"""
        self.query = query
        res = self.custom_request('POST', f"{self.base_url}{ex_url}", *args, **kwargs)
        return res, json.loads(res.text)

    def get_answer(self, task_obj, *args, **kwargs):
        """Fahamu get answer interface"""
        query = f"{self.answer_url}?task_id={task_obj['task_id']}"
        res = self.custom_request('GET', query, *args, **kwargs)
        return res, 1

    def custom_request(self, method, url, *args, **kwargs):
        """
        Make a custom request to the Fahamu API
        Note: We cannot use the adapter  above because Fahamu does not
        have a task_id status tracking system.
        If you query for a Fahamu task_id,
        you might receive `{message: No data}` with a 200 status code.
        This response is not conclusive since the task might be incomplete
        and the status code is incorrect as well.
        """
        max_retries = 50
        retry_delay = 3
        response_msg = {
            404: "Api endpoint Does not exist",
            500: "Use of Invalid Token/or the Fahamu Api is currently  down",
            400: "You sent a bad Fahamu request",
            401: "You do not have authorization to perform the request",
        }
        for _i in range(max_retries):
            response = super().request(method, url, *args, **kwargs)
            if response.ok:
                if method.lower() == "get" and not response.json().get("data"):
                    # note this is a dirty trick to check if fahamu has returned the results
                    # the issue is that the api only returns 500 or 200 satus code
                    # TODO: fix this on their end
                    time.sleep(retry_delay)
                    continue
                return response
            else:
                raise LLMError(f"Request error with code:\
                {response.status_code} occurred with reason:\
                {response_msg.get(response.status_code,response.reason)}",
                               self.query)
                #time.sleep(retry_delay)
        raise LLMError("Timeout error: We couldn't provide a response,Please try\
        to rephrase your question to receive feedback",
                       self.query)