“One API for Every NLU Framework”

Building a chatbot for Telegram. Rasa is powerful but takes real work to set up and train. DialogFlow is easier but vendor-locked. IBM Watson has great enterprise features. Azure Bot Services integrates with the Microsoft ecosystem. What if you could swap the NLU backend without rewriting your bot?

That is what this project does. A single FastAPI REST API that communicates with four different NLU providers through a unified interface. Your bot sends a message, the adapter routes it to the configured provider, and the response comes back in a consistent format.

Architecture

The API exposes endpoints for sending messages, managing conversations, and configuring which NLU provider handles which intent. Each provider (Rasa, DialogFlow, IBM Watson, Azure) has its own adapter module that implements the same interface. The dispatcher selects the right adapter based on configuration and routes the request.

The project runs in Docker with a PostgreSQL database for conversation state. It was deployed on Heroku with a GitLab CI/CD pipeline (before I migrated everything to GitHub Actions). A docker-compose.yml makes local development trivial: one command and the full stack is running.

What I would do differently today

This project is archived. It served its purpose: it powered two Telegram bots and taught me how to build a production REST API with FastAPI, Docker, and CI/CD. Looking back with a few more years of experience:

Async support throughout. FastAPI supports async natively, but the adapter used synchronous HTTP calls to the NLU providers. Today I would make every provider adapter fully async with httpx.AsyncClient and connection pooling.

Replace Heroku with Kubernetes. The Heroku deployment worked, but the free tier had cold starts and limited resources. Today this would run on my K3s cluster with proper health checks, rolling updates, and observability.

Add OpenTelemetry tracing. The adapter routes requests to four different providers. Without tracing, debugging a slow or failed response means grepping through logs from multiple systems. Distributed tracing would show exactly where time is spent.

API versioning from the start. The endpoints changed as new providers were added. A /v1/ prefix would have saved the downstream bots from breaking changes.

Tests. This project was built before I adopted test-driven development. It has 182 commits and no test suite. Today every project I write starts with tests.

View on GitHub