Skip to content

Commit 5d636a2

Browse files
committed
Add Apollo REST API processor
1 parent 248a951 commit 5d636a2

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

llmstack/processors/providers/apollo/__init__.py

Whitespace-only changes.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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()

llmstack/server/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,11 @@
420420
'name': 'LinkedIn',
421421
'processor_packages': ['llmstack.processors.providers.linkedin'],
422422
'slug': 'linkedin',
423+
},
424+
{
425+
'name': 'Apollo',
426+
'processor_packages': ['llmstack.processors.providers.apollo'],
427+
'slug': 'apollo',
423428
}
424429
]
425430

0 commit comments

Comments
 (0)