A lightweight LLM protocol aggregation wrapper, similar to litellm but more minimal.
- Multi-upstream Aggregation: Configure multiple upstream LLM APIs
- Multi-protocol Support: Chat Completions, Responses, Anthropic Messages with automatic conversion
- Model Aliases: Define local aliases for upstream models
- Parameter Settings: Supports
override(force) anddefault(fallback) modes - CLIProxyAPI Auth: Built-in OAuth management for Claude/Codex upstreams via CLIProxyAPI sidecar
- Hot Config Reload: WebUI config changes take effect immediately without restart
- YAML Configuration: Persistent config file support
- Single-file WebUI: Management interface built with pure HTML + JS
- API Key Masking: Auto-masks API keys in management endpoints
- Auto Alias: One-click passthrough alias creation by clicking upstream model tags
CLIProxyAPI is included as a git submodule. Clone with submodule:
git clone --recursive <repo-url>
# Or if already cloned:
git submodule update --initcargo build --release./target/release/llm-wrapperllm-wrapper -c config.yaml -a 0.0.0.0:3000
-c, --config <PATH> Config file path (default: config.yaml)
-a, --addr <ADDR> Bind address (default: 0.0.0.0:3000)
-v, --version Print version
-h, --help Print helpCLI args take precedence over environment variables.
Run with Docker (Recommended):
docker run -d \
--name llm-wrapper \
-p 3000:3000 \
-p 8317:8317 \
-v $(pwd)/config:/app/config \
-v llm-wrapper-data:/app/.llm-wrapper \
-e BIND_ADDR=0.0.0.0:3000 \
-e CONFIG_PATH=/app/config/config.yaml \
sczhengyabin/llm-wrapper:latestPorts:
3000- Main API and WebUI8317- CLIProxyAPI (OAuth management for Claude/Codex)
Volumes:
/app/config- Configuration directory/app/.llm-wrapper- Token cache and CLIProxyAPI data
With docker-compose:
# Start
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose downBuild image locally (optional):
git submodule update --init
docker build -t llm-wrapper:latest .CONFIG_PATH- Config file path (default: config.yaml)BIND_ADDR- Bind address (default: 0.0.0.0:3000)
# CLIProxyAPI endpoint (for OAuth upstreams, default: http://127.0.0.1:8317)
cli_proxy_api_endpoint: http://127.0.0.1:8317
# Upstream config (name as unique identifier)
upstreams:
- name: qwen-test
base_url: http://192.168.100.7:30002
auth:
type: api_key
key: null # or "your-api-key"
enabled: true
support_chat_completions: true # Supports OpenAI chat/completions
support_responses: false # Supports OpenAI responses
support_anthropic_messages: false # Supports Anthropic messages
- name: claude
base_url: https://api.anthropic.com
auth:
type: anthropic_oauth # OAuth managed by CLIProxyAPI
enabled: true
support_chat_completions: false
support_responses: false
support_anthropic_messages: true
# Model alias config
aliases:
- alias: qwen
target_model: Qwen/Qwen3.5-122B-A10B-GPTQ-Int4
upstream: qwen-test
param_overrides:
- key: temperature
value: 0.7
mode: default # or override
# extra_body configured separately
- key: extra_body
value:
chat_template_kwargs:
enable_thinking: false
mode: default
source: manual # manually created alias| Type | Description |
|---|---|
api_key |
Static API key (default) |
anthropic_oauth |
OAuth for Anthropic, managed by CLIProxyAPI |
codex_oauth |
OAuth for Codex, managed by CLIProxyAPI |
For anthropic_oauth / codex_oauth upstreams, login via WebUI or API:
# Start login
curl -X POST http://localhost:3000/api/cli-proxy-api/login/claude
# The response contains an auth URL, open it in browser to authenticate
# Token is automatically cached and refreshed- Alias Matching: The
modelparameter in requests only matches thealiasfield - Target Model is Not Routed:
target_modelis only used to replace the model name when forwarding, not for routing - Direct Upstream Call: If no alias match is found and
modelmatches an enabled upstreamname, use that upstream directly
This means:
- With
alias: my-model -> target_model: gpt-4, you must call withmodel: "my-model" - To support
model: "gpt-4", create analias: gpt-4 -> target_model: gpt-4auto alias
GET /api/config- Get current configPUT /api/config- Update config (saves to YAML file)
GET /api/upstream-models- Get model list from all upstreamsPOST /api/upstream-models/alias- Create auto alias for upstream model
POST /api/auth/login/{upstream_name}- OAuth login for upstreamDELETE /api/auth/token/{upstream_name}- Clear OAuth token
POST /api/cli-proxy-api/login/{upstream_name}- Start CLIProxyAPI loginPOST /api/cli-proxy-api/complete-login/{upstream_name}- Complete login callbackGET /api/cli-proxy-api/login-stream/{upstream_name}- SSE login progressGET /api/cli-proxy-api/status- Get CLIProxyAPI account status
POST /v1/chat/completions- Chat completionsPOST /v1/responses- Responses API (upstream support required)POST /v1/messages- Anthropic Messages API (upstream support required)GET /v1/models- Model list (returns all aliases)
GET /api/debug- Get latest debug infoDELETE /api/debug- Clear debug infoGET /api/debug/stream- SSE streaming debug info
GET /- WebUI management interface
Displays all model aliases accessible via /v1/models at the top of the page, grouped by upstream.
- Blue dashed border: Available model, click to create auto alias
- Green solid border: Enabled auto alias, click to delete
- Red background: Alias name conflict, cannot create
Auto alias is a passthrough alias: alias = target_model = upstream model name, with no parameter overrides.
Create:
- WebUI: Click model tags in upstream config cards
- API:
POST /api/upstream-models/alias
Delete:
- WebUI: Click enabled green model tags
- Manually delete alias
curl -X POST http://localhost:3000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen",
"messages": [
{"role": "user", "content": "Hello"}
]
}'curl http://localhost:3000/v1/modelscurl -X POST http://localhost:3000/api/upstream-models/alias \
-H "Content-Type: application/json" \
-d '{
"upstream": "qwen-test",
"model": "Qwen/Qwen3.5-122B-A10B-GPTQ-Int4"
}'curl -X POST http://localhost:3000/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "qwen",
"input": "Hello"
}'Note: Responses API requires upstream support. If the upstream only supports Chat Completions, the response format may not comply with the Responses API spec.
curl -X POST http://localhost:3000/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: your-anthropic-api-key" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello"}
]
}'Note: Messages API requires upstream support for the Anthropic protocol (e.g., Anthropic API). If unsupported, it will return 404/405 errors.
Enable debug mode with the X-Debug-Mode: true header to get full request/response debug info:
curl -X POST http://localhost:3000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Debug-Mode: true" \
-d '{
"model": "qwen",
"messages": [
{"role": "user", "content": "Hello"}
]
}'Response includes:
client_request: Original request sent to the Wrapperclient_ip: Client source IPclient_url: Client request URLendpoint: Called endpointupstream_url: Upstream request URLupstream_request: Request sent to upstream (with param overrides applied)upstream_response: Response from upstream