Skip to content

a1exus/sozo

Repository files navigation

Sozo

OpenAI-compatible proxy to OpenRouter, targeting Qwen 3.6 Plus.

Any client that speaks the OpenAI API (SDKs, curl, etc.) can point at Sozo and get responses from Qwen 3.6 Plus via OpenRouter — no client-side configuration changes needed. If the upstream is rate-limited, Sozo automatically retries with exponential backoff.

Quick Start

# Set your OpenRouter API key
export OPENROUTER_API_KEY=sk-or-...

# Run with Docker Compose
docker compose up -d

# Or build and run locally
make build
./bin/sozo

Usage

Sozo exposes a standard OpenAI-compatible endpoint:

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": false
  }'

Streaming works too:

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": true
  }'

Or use any OpenAI SDK by pointing it at Sozo:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="unused")
response = client.chat.completions.create(
    model="anything",  # ignored — Sozo rewrites to Qwen 3.6 Plus
    messages=[{"role": "user", "content": "Hello!"}],
)

Configuration

Variable Required Default Description
OPENROUTER_API_KEY yes Your OpenRouter API key
LISTEN_ADDR no :8080 Listen address
OPENROUTER_BASE_URL no https://openrouter.ai/api/v1 Upstream URL
DEFAULT_MODEL no qwen/qwen3.6-plus:free Model ID injected into requests

Endpoints

Method Path Description
GET / Redirects to /docs
POST /v1/chat/completions Chat completions (streaming and non-streaming)
GET /health Health check
GET /swagger.json OpenAPI 3.0 spec
GET /docs Swagger UI

Deployment

Sozo is designed to sit behind Traefik. The included docker-compose.yml has Traefik labels pre-configured for production. For local development, docker-compose.override.yml routes via sozo.localhost on the https entrypoint.

To deploy to production, update the host rule in docker-compose.yml:

labels:
  - "traefik.http.routers.sozo.rule=Host(`your-domain.com`)"

Both compose files join the external traefik_traefik network. The Docker image is ~14MB (multi-stage build to scratch).

Development

make build       # Build binary
make test        # Run tests with race detector
make docker      # Build Docker image
make up          # docker compose up -d
make down        # docker compose down
make smoke-test  # Smoke test against running instance

License

TBD

About

OpenAI-compatible proxy to OpenRouter — forwards /v1/chat/completions to Qwen 3.6 Plus with SSE streaming support

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors