Skip to content

Commit 2bebf49

Browse files
feat: get function definitions from service (#6)
* add list functions endpoint * update dependencies * fix splitter functions * add dockerfile * number of service workers as env variable * add config file and api_key * add more precise input validation * add api key to list_functions * updated repository name * modify config module * add pre-commit fixes * add pre-commit fixes * update requirements.txt and Dockerfile * add pre-commit fixes * add default config path * provisional extended example in readme file * add config at default location * add example entry to index * Update README.md Co-authored-by: Roberto Pastor Muela <[email protected]> * modify readme and pre-commit files --------- Co-authored-by: Roberto Pastor Muela <[email protected]>
1 parent 6c661dc commit 2bebf49

18 files changed

+762
-99
lines changed

.flake8

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[flake8]
2+
exclude = venv, __init__.py, doc/_build
3+
select = W191, W291, W293, W391, E115, E117, E122, E124, E125, E225, E231, E301, E303, E501, F401, F403
4+
count = True
5+
max-complexity = 10
6+
max-line-length = 120
7+
statistics = True

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ dist/
3535
.vscode/
3636
*.sublime-project
3737
*.sublime-workspace
38+
39+
# Ignore config.yaml globally
40+
config.yaml
41+
42+
# Do not ignore config.yaml in the configs/ directory
43+
!configs/config.yaml

.pre-commit-config.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
repos:
2+
- repo: https://github.com/adamchainz/blacken-docs
3+
rev: 1.16.0
4+
hooks:
5+
- id: blacken-docs
6+
additional_dependencies:
7+
- black==23.12.1
8+
9+
- repo: https://github.com/psf/black
10+
rev: 23.12.1
11+
hooks:
12+
- id: black
13+
14+
- repo: https://github.com/pycqa/isort
15+
rev: 5.13.2
16+
hooks:
17+
- id: isort
18+
19+
- repo: https://github.com/PyCQA/flake8
20+
rev: 6.1.0
21+
hooks:
22+
- id: flake8
23+
24+
- repo: https://github.com/pycqa/pydocstyle
25+
rev: 6.3.0
26+
hooks:
27+
- id: pydocstyle
28+
args: ["--ignore=D205,D100,D213,D203"]
29+
exclude: examples
30+
additional_dependencies: [toml]
31+
32+
- repo: https://github.com/pre-commit/pre-commit-hooks
33+
rev: v4.5.0
34+
hooks:
35+
- id: check-merge-conflict
36+
- id: debug-statements
37+
- id: check-yaml
38+
- id: trailing-whitespace
39+
40+
- repo: https://github.com/python-jsonschema/check-jsonschema
41+
rev: 0.27.3
42+
hooks:
43+
- id: check-github-workflows

README.md

Lines changed: 140 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ Welcome to Allie FlowKit Python. This repository hosts Python functions similar
77
- [Objectives](#objectives)
88
- [How it works](#how-it-works)
99
- [Getting started](#getting-started)
10-
- [Prerequisites](#prerequisites)
11-
- [Installation](#installation)
12-
- [Usage](#usage)
10+
- [Run locally](#run-locally)
11+
- [Prerequisites](#prerequisites)
12+
- [Installation](#installation)
13+
- [Usage](#usage)
14+
- [Run as a Docker container](#run-as-a-docker-container)
1315
- [Adding custom functions](#adding-custom-functions)
16+
- [Example](#example)
1417
- [Example functions](#example-functions)
1518
- [Contributing](#contributing)
1619

@@ -38,13 +41,17 @@ Allie Flowkit Python supports these actions:
3841

3942
## Getting started
4043

41-
### Prerequisites
44+
Allie FlowKit Python can be run locally or as a Docker container. Follow the instructions below to get started.
45+
46+
### Run locally
47+
48+
#### Prerequisites
4249

4350
- Python 3.7 or later
4451
- pip (Python package installer)
4552
- A running instance of the Allie Flowkit
4653

47-
### Installation
54+
#### Installation
4855

4956
1. Clone the repository:
5057
```sh
@@ -57,62 +64,142 @@ Allie Flowkit Python supports these actions:
5764
pip install -r requirements.txt
5865
```
5966

60-
### Usage
67+
#### Usage
6168

6269
1. Start the service:
63-
```sh
64-
uvicorn app.app:app --host 0.0.0.0 --port 8000 --workers 1
65-
```
66-
You can specify the host, port, and number of workers as needed. The service exposes the functions as REST APIs on the specified port. The default is 8000.
70+
```sh
71+
uvicorn app.app:app --host 0.0.0.0 --port 50052 --workers 1
72+
```
73+
You can specify the host, port, and number of workers as needed.
74+
75+
2. The service will expose the functions as REST APIs on the specified port (default: 8000).
76+
77+
3. Integrate these APIs into your Allie workflows as needed.
6778

68-
2. Integrate these APIs into your Allie workflows as needed.
79+
### Run as a Docker container
80+
81+
1. Build the Docker container image with the following command:
82+
83+
```bash
84+
docker build -f docker/Dockerfile . -t allie-flowkit-python:latest
85+
```
86+
87+
2. Run the Docker container and expose the port on your desired endpoint. You can also specify the number of workers as needed:
88+
89+
```bash
90+
docker run -d -e WORKERS=5 --rm --name allie-flowkit-python -p 50052:50052 allie-flowkit-python:latest
91+
```
6992

7093
## Adding custom functions
7194

72-
1. **Create a function.**
95+
1. **Create a New Function:**
7396
- Add your function code as an endpoint to a new Python file in the `app/endpoints` directory.
7497
- Use the `app/endpoints/splitter.py` file and its endpoints as an example.
75-
- Be explicit about the input and output of the function as they are used by the Allie agent
76-
to call the function.
77-
78-
2. **Add the models for the function.**
79-
- Add the models for the input and output of the function in the `app/models` directory.
80-
- Use the `app/models/splitter.py` file its models as an example.
81-
82-
2. **Add the endpoints to the service.**
83-
84-
- Import your module in the `app/app.py` file.
85-
- Add the router to the service:
86-
```python
87-
app.include_router(splitter.router, prefix="/custom_module", tags=["custom_module"])
88-
```
89-
90-
**Example**
91-
```python
92-
from fastapi import FastAPI, APIRouter
93-
from app.models.custom_model import CustomRequest, CustomResponse
94-
95-
app = FastAPI()
96-
router = APIRouter()
97-
98-
@router.post('/custom_function', response_model=CustomResponse)
99-
async def custom_function(request: CustomRequest) -> CustomResponse:
100-
"""Endpoint for custom function.
101-
102-
Parameters
103-
----------
104-
request : CustomRequest
105-
Object containing the input data required for the function.
106-
107-
Returns
108-
-------
109-
CustomResponse
110-
Object containing the output data of the function.
111-
"""
112-
# Your custom processing logic here
113-
result = ...
114-
return result
115-
```
98+
- Explicitly define the input and output of the function using Pydantic models, as these will be used by the Allie Agent to call the function.
99+
100+
2. **Add the models for the function:**
101+
- Create the models for the input and output of the function in the `app/models` directory.
102+
- Use the `app/models/splitter.py` file and its models as an example.
103+
104+
3. **Add the endpoints to the service:**
105+
- Import your module in the `app/app.py` file and add the router to the service.
106+
107+
4. **Add the function to the function map:**
108+
- Add your function to the `function_map` dictionary in the `app/app.py` file.
109+
110+
### Example´
111+
112+
1. **Create a new file for all your custom functions:**
113+
- In the `app/endpoints` directory, create a new Python file named `custom_endpoint.py`.
114+
115+
2. **Create the models for the custom function:**
116+
- In the `app/models` directory, create a new Python file named `custom_model.py`.
117+
118+
**custom_model.py**:
119+
```python
120+
from pydantic import BaseModel
121+
122+
123+
class CustomRequest(BaseModel):
124+
"""Model for the input data required for the custom function.
125+
126+
Parameters
127+
----------
128+
BaseModel : pydantic.BaseModel
129+
Base model for the request.
130+
131+
"""
132+
133+
input_data: str
134+
135+
136+
class CustomResponse(BaseModel):
137+
"""Model for the output data of the custom function.
138+
139+
Parameters
140+
----------
141+
BaseModel : pydantic.BaseModel
142+
Base model for the response.
143+
144+
"""
145+
146+
output_data: str
147+
```
148+
149+
3. **Define your custom function:**
150+
- Add your function to ``custom_endpoint.py``, explicitly defining the input and output using Pydantic models.
151+
152+
**custom_endpoint.py**:
153+
```python
154+
from fastapi import FastAPI, APIRouter
155+
from app.models.custom_model import CustomRequest, CustomResponse
156+
157+
158+
@router.post("/custom_function", response_model=CustomResponse)
159+
async def custom_function(request: CustomRequest) -> CustomResponse:
160+
"""Endpoint for custom function.
161+
162+
Parameters
163+
----------
164+
request : CustomRequest
165+
Object containing the input data required for the function.
166+
167+
Returns
168+
-------
169+
CustomResponse
170+
Object containing the output data of the function.
171+
172+
"""
173+
# Your custom processing logic here
174+
result = ...
175+
return result
176+
```
177+
178+
4. **Import the module and add the router to the service:**
179+
- Import the module in the ``app/app.py`` file and add the router to the service.
180+
181+
**app.py**:
182+
```python
183+
from app.endpoints import custom_endpoint
184+
185+
app.include_router(splitter.router, prefix="/splitter", tags=["splitter"])
186+
app.include_router(
187+
custom_endpoint.router, prefix="/custom_endpoint", tags=["custom_endpoint"]
188+
)
189+
```
190+
191+
5. **Add the function to the function map:**
192+
- Add your function to the ``function_map`` dictionary in the ``app/app.py`` file.
193+
194+
**app.py**:
195+
```python
196+
function_map = {
197+
"split_ppt": splitter.split_ppt,
198+
"split_pdf": splitter.split_pdf,
199+
"split_py": splitter.split_py,
200+
"custom_function": custom_endpoint.custom_function,
201+
}
202+
```
116203

117204
## Example functions
118205

app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# app/__init__.py
1+
"""App package responsible for creating the FastAPI app."""

app/app.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
1-
from fastapi import FastAPI
1+
from typing import List
2+
3+
from app.config._config import CONFIG
24
from app.endpoints import splitter
5+
from app.fastapi_utils import extract_endpoint_info
6+
from app.models.functions import EndpointInfo
7+
from fastapi import FastAPI, Header, HTTPException
38

49
app = FastAPI()
510

611
# Include routers from all endpoints
712
app.include_router(splitter.router, prefix="/splitter", tags=["splitter"])
13+
14+
# Map of function names to function objects
15+
function_map = {
16+
"split_ppt": splitter.split_ppt,
17+
"split_pdf": splitter.split_pdf,
18+
"split_py": splitter.split_py,
19+
}
20+
21+
22+
# Endpoint to list all enpoint information
23+
@app.get("/", response_model=List[EndpointInfo])
24+
async def list_functions(api_key: str = Header(...)) -> List[EndpointInfo]:
25+
"""List all available functions and their endpoints.
26+
27+
Parameters
28+
----------
29+
api_key : str
30+
The API key for authentication.
31+
32+
Returns
33+
-------
34+
List[EndpointInfo]
35+
A list of EndpointInfo objects representing the endpoints.
36+
37+
"""
38+
# Check if the API key is valid
39+
if api_key != CONFIG.flowkit_python_api_key:
40+
raise HTTPException(status_code=401, detail="Invalid API key")
41+
42+
return extract_endpoint_info(function_map, app.routes)

app/config/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Configuration package for the application."""
2+
3+
from app.config._config import CONFIG

0 commit comments

Comments
 (0)