Skip to content

Commit 066de1f

Browse files
authored
Merge pull request #1 from Addepto/feat/docker-fastapi
Feat/docker fastapi
2 parents fa3c06d + be37e61 commit 066de1f

File tree

19 files changed

+657
-36
lines changed

19 files changed

+657
-36
lines changed

.env_example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
API_PORT=9000
2+
DEBUG=True
3+
LOG_LEVEL=debug
4+
API_BASE_URL=http://localhost:9000
5+
API_KEY=xxx

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ data/
44
*.ipynb_checkpoint*
55
notebooks/test*
66

7-
.venv
7+
.venv
8+
.env
9+
10+
.DS_store

README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# graph_builder
2-
Graph builder package.
2+
3+
Graph builder package.
34

45
## Installation
6+
57
Following good practices we suggest you create a separate virtual environment for working with Graph builder package.
68

79
Note that the graph_builder requires python 3.12 or higher.
@@ -14,6 +16,7 @@ poetry install
1416

1517
Note that this is a short example taken from `examples/example1.ipynb`, for more information please
1618
refer to it.
19+
1720
```python
1821
from entity_graph.graph_extractor.entities_graph_extractor import EntitiesGraphExtractor
1922

@@ -32,8 +35,30 @@ config = {
3235
# Load table to graph
3336
extractor.load_table_from_file(
3437
config,
35-
"coffee_machines.pdf",
36-
"Machines",
38+
"coffee_machines.pdf",
39+
"Machines",
3740
"instances",
3841
)
3942
```
43+
44+
## graph_builder FastApi
45+
46+
In the folder containing the `docker-compose.yml` file, run the commands:
47+
48+
```
49+
docker compose build
50+
```
51+
52+
Once the image is built:
53+
54+
```
55+
docker compose up
56+
```
57+
58+
Make sure to create the `.env` file in the directory based on the `.env_example` file with the needed environmental values.
59+
60+
## Important notes
61+
62+
In the current version of the application, graphs are retained between requests but not preserved across API restarts.
63+
64+
This means that each time the API is restarted, the graphs must be rebuilt.

docker-compose.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
services:
2+
entity_graph_api:
3+
build:
4+
context: .
5+
dockerfile: ./src/entity_graph_api/Dockerfile
6+
restart: unless-stopped
7+
env_file:
8+
- .env
9+
volumes:
10+
- ./src:/app/src
11+
ports:
12+
- "${API_PORT:-8000}:8000"
13+
environment:
14+
API_PORT: ${API_PORT:-8000}
15+
DEBUG: ${DEBUG:-true}
16+
LOG_LEVEL: ${LOG_LEVEL}
17+
PYTHONPATH: /app:/app/src
18+
healthcheck:
19+
test: ["CMD", "curl", "-f", "http://localhost:8000/health/live"]
20+
interval: 1m
21+
timeout: 10s
22+
retries: 10
23+
start_period: 30s
24+
command: poetry run uvicorn entity_graph_api.main:app --host 0.0.0.0 --port 8000 --log-level ${LOG_LEVEL:-debug} --reload

poetry.lock

Lines changed: 247 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ pymupdf = "^1.26.3"
1717
msoffcrypto-tool = "^5.4.2"
1818
xlrd = "^2.0.2"
1919
openpyxl = "^3.1.5"
20+
fastapi = "^0.116.1"
21+
uvicorn = "^0.35.0"
22+
python-multipart = "^0.0.20"
2023

2124

2225
[tool.poetry.group.dev.dependencies]

src/entity_graph_api/Dockerfile

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
########
2+
# BASE #
3+
########
4+
FROM python:3.12-slim
5+
6+
WORKDIR /app/
7+
8+
# Set environment variables - fixed Poetry configuration
9+
ENV PYTHONDONTWRITEBYTECODE=1 \
10+
PYTHONUNBUFFERED=1 \
11+
POETRY_VERSION=1.5.1 \
12+
POETRY_VIRTUALENVS_CREATE=false \
13+
POETRY_VIRTUALENVS_IN_PROJECT=false \
14+
POETRY_NO_INTERACTION=1 \
15+
POETRY_CACHE_DIR=/tmp/poetry_cache \
16+
PYTHONPATH=/app
17+
18+
# Install system dependencies
19+
RUN apt-get update && apt-get install -y --no-install-recommends \
20+
curl \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
# Install Poetry
24+
RUN pip install "poetry==$POETRY_VERSION"
25+
26+
# Copy only requirements to cache them in docker layer
27+
COPY pyproject.toml ./
28+
# Copy poetry.lock if it exists
29+
COPY poetry.lock* ./
30+
31+
# Install dependencies globally since we're not using virtualenvs
32+
RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-root --no-ansi --no-interaction
33+
34+
ARG commit
35+
ARG version
36+
ARG build_time
37+
38+
# Create build info
39+
RUN echo "COMMIT: ${commit:-unknown}" > build_info && \
40+
echo "VERSION: ${version:-dev}" >> build_info && \
41+
echo "BUILD_TIME: ${build_time:-$(date)}" >> build_info
42+
43+
ENV COMMIT=${commit:-unknown}
44+
ENV VERSION=${version:-dev}
45+
ENV BUILD_TIME=${build_time:-$(date)}
46+
47+
# Copy application code
48+
COPY . .
49+
50+
# Install the local project package into the image so top-level packages
51+
# like `entity_graph` are available at runtime regardless of PYTHONPATH
52+
# set by docker-compose. We run a full poetry install here (no-root omitted
53+
# earlier to cache deps) which will install the package into the image.
54+
RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-ansi --no-interaction

src/entity_graph_api/__init.__.py

Whitespace-only changes.

src/entity_graph_api/config.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import logging
2+
import logging.config
3+
import sys
4+
import warnings
5+
from enum import StrEnum
6+
from typing import Annotated
7+
8+
from loguru import logger as logger_loguru
9+
from pydantic import AfterValidator, AnyUrl
10+
from pydantic_settings import BaseSettings, SettingsConfigDict
11+
from rich.console import Console
12+
13+
from entity_graph_api.config_logger import config_logger
14+
15+
console = Console()
16+
logger_loguru.level("INFO", color="<blue>")
17+
logger_loguru.level("DEBUG", color="<cyan>")
18+
19+
def setup_logger(name: str) -> logging.Logger:
20+
21+
logging.config.dictConfig(config_logger)
22+
23+
warnings.filterwarnings("ignore")
24+
25+
logger_loguru.configure(
26+
handlers=[
27+
{
28+
"sink": sys.stdout,
29+
"level": CONFIG.LOGGING_LEVEL,
30+
}
31+
]
32+
)
33+
34+
return logger_loguru.bind(name=name)
35+
36+
37+
URL = Annotated[AnyUrl, AfterValidator(str)]
38+
39+
UPLOAD_DIR = "/app/uploads"
40+
41+
# Custom StrEnum is more handy than using integers that represent logging levels
42+
class LoggingLevels(StrEnum):
43+
DEBUG = "DEBUG"
44+
INFO = "INFO"
45+
WARNING = "WARNING"
46+
ERROR = "ERROR"
47+
CRITICAL = "CRITICAL"
48+
49+
class Settings(BaseSettings):
50+
model_config = SettingsConfigDict(
51+
env_file=(".env"), env_nested_delimiter="__", extra="ignore"
52+
)
53+
54+
API_BASE_URL: URL | None = None
55+
56+
LOGGING_LEVEL: LoggingLevels = LoggingLevels.DEBUG
57+
58+
API_KEY: str | None = None
59+
60+
61+
CONFIG = Settings()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import importlib.util
2+
3+
config_logger = {
4+
"version": 1,
5+
"disable_existing_loggers": False,
6+
"root": {"handlers": ["console"], "level": "DEBUG"},
7+
"handlers": {
8+
"console": {
9+
"formatter": "std_out",
10+
"class": "logging.StreamHandler",
11+
"level": "DEBUG",
12+
"stream": "ext://sys.stdout",
13+
},
14+
"console_uvi": (
15+
{
16+
"formatter": "uvi",
17+
"class": "logging.StreamHandler",
18+
"level": "DEBUG",
19+
"stream": "ext://sys.stdout",
20+
}
21+
),
22+
},
23+
"formatters": {
24+
"std_out": {
25+
"format": "%(name)s : %(asctime)s : %(levelname)s : %(module)s : %(funcName)s : %(lineno)d : %(message)s",
26+
"datefmt": "%d-%m-%Y %I:%M:%S",
27+
},
28+
"uvi": (
29+
{
30+
"()": ("uvicorn.logging.DefaultFormatter"),
31+
"fmt": "%(levelprefix)s %(asctime)s - %(message)s",
32+
"use_colors": True,
33+
}
34+
),
35+
},
36+
"loggers": {
37+
"entity_graph": {
38+
"level": "DEBUG",
39+
},
40+
"openai": {
41+
"level": "WARNING",
42+
},
43+
"watchfiles": {
44+
"level": "WARNING",
45+
},
46+
"httpcore": {
47+
"level": "WARNING",
48+
},
49+
"uvicorn": {
50+
"level": "DEBUG",
51+
"propagate": False,
52+
"handlers": ["console_uvi"]
53+
},
54+
},
55+
}
56+
57+
# Removing uvicorn-related logger config if there is no uvicorn module
58+
if not importlib.util.find_spec("uvicorn"):
59+
config_logger["handlers"].pop("console_uvi")
60+
config_logger["loggers"].pop("uvicorn")
61+
config_logger["formatters"].pop("uvi")

0 commit comments

Comments
 (0)