A modular Python server that provides an OpenAI-compatible chat completion API. You can add hooks that modify the LLM response, such as the chart matplotlib hook that automatically adds generated charts to responses.
openai-compatible-endpoint/
├── main.py # Entry point for running the server
├── server.py # FastAPI application and endpoints
├── config.py # Configuration and settings management
├── models.py # Pydantic models for API requests
├── utils.py # Utility functions for working with responses
├── pyproject.toml # Project configuration and dependencies
├── Dockerfile # Docker configuration
├── .dockerignore # Files to exclude from Docker build
├── hooks/
│ ├── __init__.py # Hook system (register_post_hook, apply_post_hooks)
│ └── chart.py # Chart generation hook
├── images/
│ └── chart-hook.png # Example screenshot of chart hook in action
└── README.md
-
Install uv (if not already installed):
# macOS/Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Or via pip pip install uv
-
Install dependencies:
uv sync
This will create a virtual environment and install all dependencies from
pyproject.toml. -
Set your OpenAI API key:
export OPENAI_API_KEY=your-api-key-hereOr create a
.envfile:OPENAI_API_KEY=your-api-key-here
Run the main script using uv:
uv run python main.pyOr activate the virtual environment first:
source .venv/bin/activate # On macOS/Linux
# or
.venv\Scripts\activate # On Windows
python main.pyThe server will start on http://localhost:8000
-
Build the Docker image:
docker build -t openai-compatible-endpoint . -
Run the container:
docker run -p 8000:8000 \ -e OPENAI_API_KEY=your-api-key-here \ openai-compatible-endpoint
Or use a
.envfile:docker run -p 8000:8000 \ --env-file .env \ openai-compatible-endpoint
-
The server will be available at:
http://localhost:8000
Health check:
curl http://localhost:8000/healthChat completion (with chart):
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Show me quarterly sales data"}],
"stream": false
}'The response will include:
- Text content from the LLM
- A base64-encoded PNG chart image
main.py: Entry point that starts the serverserver.py: FastAPI application with OpenAI-compatible endpointsconfig.py: Settings management using Pydantic Settingsmodels.py: Pydantic models for request validation
The server uses a flexible hook system for modifying responses:
hooks/__init__.py: Core hook registration and application logichooks/chart.py: Example hook that adds matplotlib charts to responses
To add your own hook, create a new file in the hooks/ directory:
# hooks/my_hook.py
from typing import Dict, Any
def my_hook(response: Dict[str, Any]) -> Dict[str, Any]:
# Modify response here
return responseThen register it in server.py:
from hooks.my_hook import my_hook
register_post_hook(my_hook)utils.py: Helper functions for extracting charts and text from responses, making requests, etc.
To expose the server publicly (e.g., for use with LangSmith or external clients):
Download from ngrok.com or install via package manager:
# macOS
brew install ngrok
# Or download from https://ngrok.com/downloaduv run python main.pyIn a separate terminal:
ngrok http 8000Ngrok will provide a public URL like:
https://abc123.ngrok-free.app
For LangSmith or other clients:
- Base URL:
https://abc123.ngrok-free.app/v1/ - Endpoint:
https://abc123.ngrok-free.app/v1/chat/completions
curl -X POST https://abc123.ngrok-free.app/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "hello"}],
"stream": false
}'The server returns responses in OpenAI-compatible format with multimodal content:
{
"choices": [{
"message": {
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello! Here's the quarterly sales data..."
},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,iVBORw0KGgo..."
}
}
]
}
}]
}The utils.py module provides helper functions for working with responses:
from utils import (
extract_chart_from_response,
extract_text_from_response,
make_chat_request
)
# Make a request
response = make_chat_request(
"http://localhost:8000",
[{"role": "user", "content": "hello"}]
)
# Extract components
chart = extract_chart_from_response(response)
text = extract_text_from_response(response)
# Chart URL can be used directly in HTML:
# <img src="{chart['image_url']['url']}" />- ✅ OpenAI API compatible endpoint (
/v1/chat/completions) - ✅ Modular architecture with separated concerns
- ✅ Flexible hook system for customizing responses
- ✅ Automatic chart generation with every response (via hook)
- ✅ Streaming support (set
"stream": true) - ✅ CORS enabled for cross-origin requests
- ✅ Production-ready error handling
- ✅ LangSmith compatible
"No module named 'fastapi'"
- Install dependencies using
uv:uv sync - Or install manually:
uv pip install -e .
"OPENAI_API_KEY not set"
- Export the environment variable:
export OPENAI_API_KEY=your-key - Or create a
.envfile withOPENAI_API_KEY=your-key
Ngrok connection refused
- Make sure the server is running on port 8000
- Check that ngrok is forwarding to
localhost:8000
Chart not appearing in response
- Check that the response contains a
contentarray with both text and image_url items - Verify the chart hook is registered in
server.py
Import errors
- Make sure you're running from the project root directory
- Check that all files are in the correct locations
This script is provided as-is for demonstration purposes.
