|
| 1 | +from enum import Enum |
| 2 | + |
| 3 | +import orjson as json |
| 4 | +from asgiref.sync import async_to_sync |
| 5 | +from jinja2 import Template |
| 6 | +from pydantic import Field |
| 7 | + |
| 8 | +from llmstack.processors.providers.api_processor_interface import ( |
| 9 | + ApiProcessorInterface, |
| 10 | + ApiProcessorSchema, |
| 11 | +) |
| 12 | + |
| 13 | + |
| 14 | +class HTTPMethod(str, Enum): |
| 15 | + GET = 'GET' |
| 16 | + POST = 'POST' |
| 17 | + PUT = 'PUT' |
| 18 | + |
| 19 | + def __str__(self): |
| 20 | + return self.value |
| 21 | + |
| 22 | + |
| 23 | +class RestApiInput(ApiProcessorSchema): |
| 24 | + input: str = Field( |
| 25 | + default='{}', description='JSON dictionary of key value pairs to use in the API call', widget='textarea') |
| 26 | + |
| 27 | + |
| 28 | +class RestApiOutput(ApiProcessorSchema): |
| 29 | + text: str = Field( |
| 30 | + default='', description='Text returned by the API call', widget='textarea') |
| 31 | + json_data: dict = Field( |
| 32 | + default={}, description='JSON returned by the API call', widget='textarea', alias='json') |
| 33 | + code: int = Field( |
| 34 | + default=200, description='HTTP status code returned by the API call') |
| 35 | + |
| 36 | + |
| 37 | +class RestApiConfiguration(ApiProcessorSchema): |
| 38 | + url: str = Field( |
| 39 | + default='https://api.apollo.io/v1/', description='URL of the API endpoint', advanced_parameter=False) |
| 40 | + method: HTTPMethod = Field( |
| 41 | + default=HTTPMethod.GET, description='HTTP method to use', advanced_parameter=False) |
| 42 | + body: str = Field( |
| 43 | + default='', description='Body of the request in JSON', widget='textarea') |
| 44 | + connection_id: str = Field( |
| 45 | + default='', description='Connection to use for the API call', widget='connection', advanced_parameter=False) |
| 46 | + |
| 47 | + |
| 48 | +class RestApiProcessor(ApiProcessorInterface[RestApiInput, RestApiOutput, RestApiConfiguration]): |
| 49 | + """ |
| 50 | + REST API processor |
| 51 | + """ |
| 52 | + @staticmethod |
| 53 | + def name() -> str: |
| 54 | + return 'REST API' |
| 55 | + |
| 56 | + @staticmethod |
| 57 | + def slug() -> str: |
| 58 | + return 'rest_api' |
| 59 | + |
| 60 | + @staticmethod |
| 61 | + def description() -> str: |
| 62 | + return 'Call Apollo REST API' |
| 63 | + |
| 64 | + @staticmethod |
| 65 | + def provider_slug() -> str: |
| 66 | + return 'apollo' |
| 67 | + |
| 68 | + def process(self) -> dict: |
| 69 | + import requests |
| 70 | + |
| 71 | + url = self._config.url |
| 72 | + method = self._config.method |
| 73 | + |
| 74 | + headers = { |
| 75 | + 'Content-Type': 'application/json', |
| 76 | + 'Cache-Control': 'no-cache', |
| 77 | + } |
| 78 | + |
| 79 | + # Treat url and body as a templates and replace any variables with values from the input |
| 80 | + url_template = Template(url) |
| 81 | + url = url_template.render(json.loads(self._input.input)) |
| 82 | + |
| 83 | + body_template = Template(self._config.body) |
| 84 | + body = body_template.render(json.loads(self._input.input)) |
| 85 | + |
| 86 | + response = None |
| 87 | + api_key = self._env['connections'][self._config.connection_id]['configuration']['api_key'] |
| 88 | + if method == HTTPMethod.GET: |
| 89 | + if '?' in url: |
| 90 | + url += f'&api_key={api_key}' |
| 91 | + else: |
| 92 | + url += f'?api_key={api_key}' |
| 93 | + response = requests.request( |
| 94 | + method, url, headers=headers) |
| 95 | + else: |
| 96 | + body = self._input.input |
| 97 | + try: |
| 98 | + body = json.loads(body) |
| 99 | + body['api_key'] = api_key |
| 100 | + except: |
| 101 | + pass |
| 102 | + response = requests.request( |
| 103 | + method, url, headers=headers, json=body) |
| 104 | + |
| 105 | + async_to_sync(self._output_stream.write)(RestApiOutput( |
| 106 | + text=response.text, json=response.json(), code=response.status_code)) |
| 107 | + |
| 108 | + return self._output_stream.finalize() |
0 commit comments