OpenAI v1 API-compatible HTTP service backed by HuggingFace Transformers
Project description
OpenAI HTTP API Service
OpenAI v1 API compatible HTTP service with pluggable inference backends. Built with FastAPI and supports streaming responses, request queueing, and OpenTelemetry metrics.
Features
- ✅ OpenAI v1 API compatibility (Models, Chat Completions, Completions, Embeddings)
- ✅ Server-Sent Events (SSE) streaming support
- ✅ Request queue with GPU serialization (prevents concurrent GPU access)
- ✅ TOML + environment variable configuration
- ✅ Structured JSON logging with request IDs
- ✅ OpenTelemetry metrics (Prometheus endpoint)
- ✅ Mock backend for testing (1536-dim embeddings, streaming chat, tool calls)
- ✅ Transformers backend example (
examples/transformers-backend/— uses Qwen3.5-0.8B) - ✅ Custom backend SDK (
BackendBaseABC,run_server()entry point) - 🚧 Audio (speech, transcriptions, translations) endpoints
- 🚧 Image generation / editing endpoints
- ✅ Tool calling (function definitions in chat) — supported in transformers backend
Quick Start
Prerequisites
- Python 3.12+ (see
.python-version) - uv package manager
Installation
# Clone and enter directory
git clone <repo>
cd openai-http
# Create venv and install dependencies
uv sync --all-extras
Run the Server
# Start with mock backend (no GPU required)
uv run -m openai_http
# Server runs on http://0.0.0.0:8000
Test with curl
# List models
curl http://localhost:8000/v1/models
# Chat completion (non-streaming)
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mock-gpt",
"messages": [{"role": "user", "content": "Hello"}]
}'
# Chat completion (streaming)
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mock-gpt",
"messages": [{"role": "user", "content": "Hello"}],
"stream": true
}'
# Health check
curl http://localhost:8000/health
Run with a real Transformers backend
# Install heavy deps (not included in default install)
uv pip install torch transformers accelerate
# Start with Qwen3.5-0.8B (downloads ~1.6 GB on first run)
uv run python examples/transformers-backend/transformers_backend.py
# Customize model, temperature, or enable thinking mode
uv run python examples/transformers-backend/transformers_backend.py \
--model Qwen/Qwen2.5-0.5B-Instruct \
--temperature 0.7 \
--thinking \
--port 8000
See examples/transformers-backend/README.md for details.
Configuration
Configuration uses config.toml with environment variable overrides.
config.toml Example
[server]
host = "0.0.0.0"
port = 8000
[auth]
enabled = false
api_keys = [] # ["sk-..."] when enabled
[queue]
depth = 32 # max queued requests
[observability]
log_level = "info"
log_format = "json" # "json" or "text"
metrics_enabled = true
metrics_port = 9464
Environment Variables
Override any config with OPENAI_HTTP__ prefix (double underscore for nesting):
OPENAI_HTTP__SERVER__PORT=9000
OPENAI_HTTP__AUTH__ENABLED=true
OPENAI_HTTP__AUTH__API_KEYS=["sk-test-key"]
API Endpoints
Implemented
GET /v1/models— List available modelsGET /v1/models/{model_id}— Get model infoPOST /v1/chat/completions— Chat completion (streaming + non-streaming)POST /v1/completions— Text completionPOST /v1/embeddings— Text embeddingsGET /health— Health check
Stub endpoints (registered but return 501)
POST /v1/audio/speech,/v1/audio/transcriptions,/v1/audio/translationsPOST /v1/images/generations,/v1/images/edits,/v1/images/variations
Planned
- Audio / image generation (currently return 501)
POST /v1/moderationsPOST /v1/files, file managementPOST /v1/fine_tuning/jobs- Batch API
Development
Run Tests
# All tests
uv run pytest tests/ -v
# SDK tests only (auto-starts server)
uv run pytest tests/sdk/ -v
# Unit tests only
uv run pytest tests/unit/ -v
# Single test
uv run pytest tests/sdk/test_models.py::TestModelsAPI::test_models_list -v
Note: SDK tests (tests/sdk/) automatically start a mock server on port 8000 in a background thread. Don't start the server manually before running tests.
Linting & Type Checking
uv run ruff check openai_http/ tests/
uv run mypy openai_http/
Project Structure
openai_http/
├── __init__.py # Public API: BackendBase, run_server, setup_logging
├── _server.py # run_server() entry point (library mode)
├── app.py # FastAPI factory, lifespan, middleware
├── config.py # pydantic-settings configuration
├── errors.py # OpenAI-format error handlers
├── queue.py # Request queue (asyncio.Semaphore)
├── routers/ # API endpoints
│ ├── chat.py # /v1/chat/completions
│ ├── completions.py # /v1/completions
│ ├── embeddings.py # /v1/embeddings
│ ├── models.py # /v1/models
│ ├── audio.py # /v1/audio/* (stub)
│ ├── images.py # /v1/images/* (stub)
│ └── health.py # /health
├── schemas/ # Pydantic v2 models
├── backends/ # Inference backends
│ ├── base.py # BackendBase ABC
│ └── mock_backend.py # Mock implementation
└── observability/ # Logging + metrics
examples/
└── transformers-backend/ # Real Transformers backend using Qwen2.5-0.5B
tests/
├── conftest.py # Async httpx fixtures
├── unit/ # Unit tests
└── sdk/ # OpenAI SDK compatibility tests
└── conftest.py # Sync OpenAI client fixtures
config.toml # Configuration
specs/ # Feature specs & plans
Architecture
Request Flow
- Request arrives at FastAPI router
RequestIDMiddlewareassigns unique request IDRequestLoggingMiddlewarelogs request metadata- Router calls
queue.acquire()(waits if GPU busy) - Backend executes inference (mock or transformers)
- Response formatted to OpenAI v1 schema
- Error handlers catch exceptions and format
{"error": {...}}
Custom backends
Create your own backend by subclassing BackendBase and plugging it into run_server():
import openai_http
class MyBackend(openai_http.BackendBase):
async def generate(self, prompt, **kwargs):
return {"generated_text": "...", "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}}
async def generate_stream(self, prompt, **kwargs):
yield "Hello"
yield " world"
async def list_models(self):
return [{"id": "my-model", "object": "model", "created": 0, "owned_by": "me"}]
async def get_model(self, model_id):
return {"id": model_id, "object": "model", "created": 0, "owned_by": "me"} if model_id == "my-model" else None
openai_http.run_server(MyBackend(), port=8000)
See examples/transformers-backend/ for a full working implementation.
Built-in Backend Protocol
All backends subclass BackendBase (defined in backends/base.py):
| Method | Required | Purpose |
|---|---|---|
generate() |
✅ | Non-streaming completion |
generate_stream() |
✅ | Streaming completion (yields str chunks) |
list_models() |
✅ | List available models |
get_model() |
✅ | Get a model by ID |
embed() |
❌ | Embeddings (default → HTTP 501) |
generate_tool_calls() |
❌ | Tool/function calling (default → HTTP 501) |
setup() / teardown() |
❌ | Lifecycle hooks (default no-op) |
Request Queue
GPU inference is serialized using asyncio.Semaphore(1) to prevent out-of-memory errors. When the queue reaches queue.depth, new requests receive HTTP 429 (Too Many Requests).
Testing Strategy
- Unit tests (
tests/unit/): Test individual functions/classes in isolation - Integration tests (
tests/integration/): Test endpoint behavior with httpx - Contract tests (
tests/contract/): Verify OpenAI API contract compliance - SDK tests (
tests/sdk/): Use officialopenaiPython SDK against running server
Unimplemented endpoints use pytest.skip() via _call_or_skip() helper and auto-activate when routes are added.
License
MIT
Contributing
See specs/001-openai-http-api/plan.md for implementation roadmap and AGENTS.md for development guidelines.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openai_http-0.4.2.tar.gz.
File metadata
- Download URL: openai_http-0.4.2.tar.gz
- Upload date:
- Size: 45.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d25e6e2d500695b937d81729d2f2c4136a35715aa7ca18a6ef0f6bd57090d7e
|
|
| MD5 |
02c81a60b793b58a2eedd83d5ac1d0bf
|
|
| BLAKE2b-256 |
01829cbf3d7be32cec464c1d2713cc1dcb05ec895c5506583cdc542818c03a53
|
File details
Details for the file openai_http-0.4.2-py3-none-any.whl.
File metadata
- Download URL: openai_http-0.4.2-py3-none-any.whl
- Upload date:
- Size: 68.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f5e72763f7f5e627e38e0874cf6674b3b6404789992ccd1c32585269f45b6e0
|
|
| MD5 |
12c4b2ae250b814697aa8406f04c9b2b
|
|
| BLAKE2b-256 |
a696c067b809f2c39a647c73bcc3f3f7b645d56b9f2d6b0d627263a4d5603dd5
|