Files
2026-10-14 09:18:19 +03:00

211 lines
9.5 KiB
Markdown

# Dynagate LLM gateway (C++ port)
Dynagate is a lightweight, high-performance LLM gateway that acts as a proxy for OpenAI-compatible APIs. This directory is a modern **C++20** port of the Go reference implementation (`dynagate`), using a native RFC 8259 JSON engine (`json.cpp`) and **libcurl** for HTTP networking.
It provides seamless model routing, automatic key and model failover, live configuration hot-reloading without restarts, token authentication, and custom header injection.
## Features
1. **Multi-API Protocol Endpoints (Stateless)**:
- `/v1/models` and `/models` (GET): Dynamically lists unique active model IDs.
- `/v1/chat/completions` and `/chat/completions` (POST): Proxies non-streaming and streaming (`text/event-stream`) completions.
- `/v1/responses` and `/responses` (POST): Stateless-style OpenAI Responses API endpoint (automatically routed to upstream chat completions endpoints).
- `/v1/messages` and `/messages` (POST): Anthropic-compatible Messages API endpoint (automatically routed to upstream chat completions endpoints).
- `/v1/images/generations` and `/images/generations` (POST): Proxies image generation requests.
> **Note** Dynagate operates in a completely stateless manner across all API formats (`/v1/chat/completions`, `/v1/responses`, `/v1/messages`). Dynagate does not store or persist server-side conversation state. Clients must include the full conversation history in each request for multi-turn dialogues.
2. **Zero-downtime live-reloading**:
- Watches `models.csv` (overridable via command-line flags) continuously using a background thread and automatically reloads configuration updates without dropping active connections.
3. **Fallback and retry orchestration**:
- Attempts keys configured for the requested model sequentially using Fibonacci backoff delays (`-retry-delay`).
- Cascades automatically to the next model in the priority chain (wrapping around to cover all entries) if all keys for the requested model fail.
- Updates the `"model"` field dynamically in the outgoing request body before proxying, ensuring upstream compatibility.
4. **Flexible authorization**:
- Optional gateway token authentication via command-line key file.
- Key options (`""`, `"-"`) to strip authorization headers when proxying to local or open upstreams.
- Special `"-blank-"` key config to pass an empty `Authorization: Bearer` header.
5. **External config updater**:
- Executes a periodic CLI command (`-csv-updater`) at a custom interval (`-csv-update-interval`) to fetch, generate, or download fresh model listings.
6. **Custom header injection (`extra` field)**:
- Sets or overrides custom headers dynamically per model deployment using a JSON configuration string.
## Tech stack
- **Language**: C++20 (`-std=c++20`)
- **Dependencies**:
- `libcurl` (HTTP/HTTPS upstream requests and SSE streaming)
- Native RFC 8259 JSON parser and serializer in `json.cpp` (no extra or vendored third-party JSON dependency)
- POSIX sockets and standard C++20 threads/atomics/filesystem
- **Target Platforms**: Linux, FreeBSD, macOS, Haiku, and any POSIX-compliant OS (64-bit and 32-bit hosts)
## Repository layout
```text
dynagate.hpp Shared declarations, data structures, and interfaces
json.cpp Native RFC 8259 JSON parser and serializer
config.cpp RFC 4180 CSV parser, ConfigManager, hot-reloader, CSV updater
handler.cpp Auth, normalization, protocol conversions, libcurl proxying
server.cpp Multi-threaded POSIX HTTP/1.1 server and streaming writer
main.cpp CLI flag parsing, signal handling, server lifecycle
gateway_test.cpp Comprehensive unit and end-to-end test suite
Makefile POSIX-compliant Makefile (bmake, gmake, smake compatible)
models.csv Example model configuration file
free-model-collector.sh Free model discovery and verification script
freekeys.sh Helper script for collecting free keys
```
## Prerequisites
- A C++20 compiler (`c++`, `g++`, or `clang++`)
- Any POSIX `make` (`bmake`, `gmake`, `make`)
- `libcurl` development headers and library:
```bash
# Debian / Ubuntu
apt-get install build-essential libcurl4-openssl-dev
# Fedora / RHEL
dnf install gcc-c++ libcurl-devel
# FreeBSD
pkg install curl
# macOS (Xcode Command Line Tools include libcurl, or via Homebrew)
xcode-select --install
```
## Getting started
### 1. Building from source
```bash
make # builds ./dynagate
make 32bit # builds a 32-bit ./dynagate binary with large file support
make debug # builds ./dynagate with AddressSanitizer + UBSan
make test # builds and runs the test suite
make clean # removes build artifacts
make install # installs to $(DESTDIR)/usr/local/bin/dynagate
```
If `libcurl` or custom headers live in a non-standard prefix, override flags on the `make` command line:
```bash
make CXX=clang++ CPPFLAGS="-I/opt/homebrew/include -I." LDLIBS="-L/opt/homebrew/lib -lcurl -pthread"
```
### 2. Configure models
Create a `models.csv` file in the same directory. The file supports both header and header-less formats.
**Example `models.csv` with Header**:
```csv
model,key,endpoint,extra
gpt-4o,sk-proj-key1,https://api.openai.com/v1,"{""Openai-Organization"":""org-123""}"
gpt-4o,sk-proj-key2,https://api.openai.com/v1,
gpt-3.5-turbo,-blank-,https://api.openai.com/v1,
deepseek-chat,sk-ds-key,https://api.deepseek.com/v1,"{""X-Custom-Env"":""production""}"
ollama-llama3,-,http://localhost:11434/v1,
```
### 3. Setup gateway key (optional)
To restrict access to your gateway, write one or more plaintext keys (one per line) to a file (e.g. `gatekey.txt`):
```text
my-secure-gateway-token-1
my-secure-gateway-token-2
```
### 4. Run the server
```bash
./dynagate -port 8080 -csv models.csv -key gatekey.txt
```
## Configuration reference
### Command line flags
| Flag | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `-port` | int | `8080` | Port the gateway listens on. |
| `-csv` | string | `models.csv` | Path to the model configuration CSV. |
| `-key` | string | `""` | Path to a file containing the gateway's access tokens (one per line). If blank, authentication is disabled. |
| `-csv-updater` | string | `""` | Command/script to run periodically to update `models.csv`. |
| `-csv-update-interval`| int | `10` | Frequency in minutes to invoke `-csv-updater`. |
| `-retry-delay` | int | `100` | Base delay in milliseconds for Fibonacci failover retries (0 to disable). |
### Model CSV format rules
The gateway maps columns dynamically by looking at the header row. If no header is present, it defaults to:
- Column 0: `model` (Model ID)
- Column 1: `key` (API Key / Authorization Token)
- Column 2: `endpoint` (Upstream base URL endpoint)
- Column 3: `extra` (Optional JSON string object containing header mappings)
#### Auth override key values:
- **`""` or `"-"`**: The gateway will strip the `Authorization` header completely (useful for local LLM endpoints or unauthenticated proxies).
- **`"-blank-"`**: The gateway will attach `Authorization: Bearer` without any key appended.
- **Any other string**: The gateway will attach `Authorization: Bearer <key>`.
### Stateless Operation & Multi-Turn Conversations
Dynagate is designed as a lightweight, zero-state proxy gateway. All supported request formats (`/v1/chat/completions`, `/v1/responses`, `/v1/messages`) are processed statelessly without storing session context or conversation state on disk or in memory.
To maintain context in multi-turn conversations, clients **must include the entire conversation history** in every request payload:
- **OpenAI Chat Completions (`/v1/chat/completions`)**: Pass the full array of system, user, and assistant messages in `messages`.
- **OpenAI Responses API (`/v1/responses`)**: Pass previous conversation turns in `input` (or `messages`) along with `instructions`.
- **Anthropic Messages API (`/v1/messages`)**: Pass the full history of user and assistant messages in `messages` along with `system`.
### Manual testing with curl
#### 1. Model listing (authenticated)
```bash
curl -i -H "Authorization: Bearer my-secure-gateway-token" http://localhost:8080/v1/models
```
#### 2. Chat completions proxying
```bash
curl -i -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer my-secure-gateway-token" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Explain gravity in one sentence"}]
}'
```
#### 3. Chat completions streaming
```bash
curl -i -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer my-secure-gateway-token" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"stream": true,
"messages": [{"role": "user", "content": "Count from 1 to 5"}]
}'
```
#### 4. OpenAI-compatible Responses API (`/v1/responses`)
```bash
curl -i -X POST http://localhost:8080/v1/responses \
-H "Authorization: Bearer my-secure-gateway-token" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"instructions": "Be concise",
"input": "Explain relativity in one sentence"
}'
```
#### 5. Anthropic-compatible Messages API (`/v1/messages`)
```bash
curl -i -X POST http://localhost:8080/v1/messages \
-H "Authorization: Bearer my-secure-gateway-token" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"system": "You are a helpful assistant",
"messages": [{"role": "user", "content": "Hello world"}]
}'
```
## Credits
Created by Luxferre in 2026, released into the public domain with no warranties.