OpenAI- and Anthropic-compatible proxy for UMANS AI, written in Go. Zero external dependencies.
This is a Go port and opinionated customization of the original Node.js project at https://github.com/notBlubbll/umans-dash.
- OpenAI-Compatible API — Drop-in for
/v1/chat/completionsand/v1/models - Anthropic Messages API — Pass-through for
/v1/messages(Anthropic-compatible clients) - Multi-Key Pool — Round-robin key pool with unhealthy marking, cooldowns, and persistence across restarts
- Automatic Upstream Retry — Retries failed requests on HTTP 500/503 and network failures up to a configurable number of attempts (default 2, range 0–10) with configurable backoff presets (aggressive or conservative), rotating to a fresh key on each attempt. Applies to both OpenAI and Anthropic paths
- Concurrency Queue — Bounded request queue gated by Concurrency Limit mode (Soft Cap, Hard Cap, or Manual with a custom slider), with soft-limit (limit) and hard-cap (burst) tracking, throttled-503 counter, and rejection when full. A background queue worker with
sync.Condsignaling dispatches queued requests when slots free up. A configurable Slot Free Delay (seconds) can pause before freeing a slot after request completion to avoid upstream rate limits - Dashboard — Clean UI with usage cards, concurrency monitoring, usage history chart with sortable tables, model management, key management, and configuration
- API Key Mode — Smart (default), Managed, or Pass-Through mode controlling which API key is used for upstream requests (client key vs. proxy key pool)
- Empty Stream Detection — Detects when upstream returns HTTP 200 +
text/event-streambut sends zero data bytes, and treats it as a retryable error (with configurable retry attempts) instead of forwarding an empty stream to the client - Non-Blocking Config Updates — Proxy request handlers snapshot config fields and release the config lock immediately, so settings changes complete instantly even during active LLM completions
- Bing Wallpaper — Daily rotating UHD (3840px) backgrounds from Bing
- Wallhaven Wallpaper — Optional high-resolution (≥2560×1440) backgrounds from Wallhaven with JPEG, PNG, and WebP support
- Automatic Image-Attachment Limit — Caps vision/images per request; excess images are pruned oldest-first before forwarding to UMANS
- Vision Handoff — Models that can't see images natively (e.g. GLM 5.2) automatically delegate image analysis to a vision-capable model (default
umans-coder) and inject the text description into the session context - Handoff Image Cache — When vision handoff is enabled, caches image descriptions for 24 hours so repeated images skip re-analysis
- Thinking Payload Normalization — Rewrites camelCase
budgetTokensback to snake_casebudget_tokensfor UMANS Pydantic compatibility - Tool Schema Normalization — Simplifies
anyOf/nullable combinators and resolves$refreferences before forwarding
umans-dash-go/
├── main.go # Entry point (config load, HTTP server, graceful shutdown)
├── go.mod # Go module definition (stdlib only)
├── src/ # Proxy package (package proxy)
│ ├── embed.go # go:embed wrapper for dashboard assets
│ ├── interface/
│ │ ├── dashboard.html # Dashboard UI
│ │ └── dashboard.js # Dashboard JavaScript (template-rendered)
│ ├── config.go # Constants, config loading/saving, env overrides
│ ├── upstream.go # UpstreamClient, HTTP calls to upstream API
│ ├── keypool.go # API key pool with cooldown/health tracking
│ ├── cache.go # ImageHandoffCache LRU, hash helpers
│ ├── normalize.go # Reasoning, tool schema, payload normalization
│ ├── conversation.go # Conversation tracking, fingerprinting
│ ├── errors.go # Header/body redaction
│ ├── errors_log.go # Error log file, HTTP error records
│ ├── retry.go # Retry loop, backoff presets
│ ├── http.go # HTTP helpers, SSE, error writers, auth
│ ├── proxy_chat.go # OpenAI chat completions proxy handler
│ ├── proxy_anthropic.go # Anthropic messages proxy handler
│ ├── queue.go # Queue dispatch, concurrency limit mode
│ ├── usage.go # Usage/concurrency fetch, caching
│ ├── handlers.go # All HTTP handlers
│ ├── wallpaper.go # Wallpaper download/cache/serve
│ ├── vision.go # Vision handoff
│ ├── dashboard.go # Dashboard rendering
│ ├── proxy.go # NewProxy, ServeHTTP, Shutdown, lifecycle
│ └── types.go # Type definitions (Config, KeyPool, Proxy, etc.)
├── screenshots/ # Dashboard screenshots
├── .cache/ # Cached wallpaper images (auto-created)
├── .logs/ # HTTP error logs (auto-created)
├── .config/ # config.json (auto-created, gitignored)
└── AGENTS.md # Developer guide
- Single Go binary — no Node.js runtime, no
npm install, nonode_modules - Zero external dependencies — uses only the Go standard library
- Excluded features (present in the upstream original, removed in this port):
- FreeGen AI wallpaper generator (
/api/bg-freegen,FREEGEN_PROMPT, WebSocket integration with freegen.app) - Sleev context-compression gateway (
SLEEV_ENABLED,SLEEV_GATEWAY_*, binary discovery, OAuth sign-in, lifecycle management) /api/sleevendpoint (dashboard control to enable/disable and check Sleev gateway status)- Shell Guard (git-command blocking in tool-call responses, streaming and non-streaming)
- i18n autotranslation system (
/api/i18n,I18N_STRINGScatalog,LOCALEconfig, one-click autotranslate viaumans-flash) - Response cache for non-streaming chat (
ResponseCache,cacheKey(),/api/cacheGET/DELETE,CACHE_TTL/CACHE_MAX_SIZE/CACHE_ENABLED) - UMANS app login (
EMAIL/PASSWORD/APP_SESSION,loginToApp(),/api/umans/login,/api/umans/logout) - Per-model rate limit map (
RATE_LIMIT_MAP,enforceRateLimit()) - Opencode config auto-setup (
setupOpencodeConfig(),discoverOpencodeConfigs(): discovers and writes aumansprovider entry intoopencode.jsonwith reasoning variants, context limits, tool/vision flags, and AGENTS.md/skills.md instructions; backs up existing config on first run) - models.dev catalog integration (
getModelsDevCatalog(),findModelsDevEntry(): fetches models.dev to derive reasoning mode and metadata for the opencode provider entry) - Automatic dashboard browser launch on first run (
xdg-open/open/startwhen opencode config is first created) - Test Chat panel in the dashboard
- SS Mode (screenshot-safe blur/masking)
- SVG-filter glassmorphism (
feDisplacementMap/feColorMatrix; replaced with CSSbackdrop-filter) - SQLite usage-history cache (
.cache/usage.db,node:sqlite/bun:sqlite)
- FreeGen AI wallpaper generator (
- Default value differences (both ports have the feature, but defaults differ):
- Vision handoff default model:
umans-kimi-k2.7upstream,umans-coderhere - Vision handoff default state: enabled (
true) upstream, disabled (false) here - Wallpaper source default:
freegenupstream,binghere
- Vision handoff default model:
- Added features (not in the upstream original):
- API Key Mode (Smart / Managed / Pass-Through) controlling which key is sent upstream
- Concurrency Limit mode (Soft Cap / Hard Cap / Manual with slider), persisted to config
- Slot Free Delay (configurable delay before freeing a concurrency slot)
- Configurable Retry Attempts (slider, 0-10, default 2) with Backoff Strategy presets (aggressive/conservative) replacing hardcoded 10-retry count and flat 3s backoff
- Configurable Request Timeout (slider, 30s-30m, default 15m, applied live without restart)
- Anthropic path retry support (upstream
/v1/messagesis single-attempt only; this port retries on HTTP 500/503 and network failures with key rotation, matching the OpenAI path) - Priority status card replacing Throttled card in the dashboard (always visible, red when Low priority)
- Usage Limits card consolidating concurrency usage progress bar and priority budget inline bars with service mode banner
- Service Status card with per-model health breakdown (3 view variants: Table/Bars/Heatmap, color thresholds for uptime/TTFT/TPS)
- Empty SSE stream detection and retry (detects HTTP 200 +
text/event-streamwith zero data bytes; prevents "empty stream" client errors) - Non-blocking config updates (request handlers snapshot config fields and release the lock immediately)
- Vision Handoff Image Cache (SHA-256 keyed LRU, 24h TTL, configurable)
/v1/models/infoendpoint exposing the raw upstream model catalog- UHD Bing wallpaper (3840px peapix resolution upgrade)
- Wallhaven resolution filter (
atleast=2560x1440) with JPEG/PNG/WebP content-type detection - Queue-full responses return HTTP 429 Too Many Requests with
Retry-Afterheader to discourage immediate retries - Response-writer tracker with abort/hijack guards prevents superfluous
WriteHeaderpanics and protects streaming responses from timeout middleware races - Unified dispatch path with
sync.Cond-based queue worker:ProcessQueuereserves slots underqueueMubefore launchingrunDispatchgoroutines, eliminating gate overshoot;dispatchItemused only for the no-limit fast path OnRequestCompleteuses aqueueDone-aware goroutine for slot-free delay instead of blockingtime.Sleep, allowing shutdown to cancel pending delays without timer/lock deadlocks- Idempotent
Shutdownviasync.OnceonqueueDoneclose; worker drains before HTTP server shutdown GetEffectiveConcurrencyacceptsoverride intparameter to avoid recursiveconfigMu.RLock()when called fromgateLimit()orHandleConfigPostHandleConfigPostreleasesconfigMubefore callingsignalQueue()to prevent lock-order inversion- Timeout middleware uses compare-and-set on
tw.writtento claim the response exclusively before writing the timeout error, preventing the "superfluous response.WriteHeader" log warning WriteJSONchecksIsCommitted()before writing in the non-SSE branch to skip writes on already-committed trackers- Request-context propagation through upstream calls so client disconnects cancel in-flight work
- Configurable vision-handoff concurrency cap (default 4) to prevent goroutine explosion on multi-image requests
- Shared
catalogClientfor upstream catalog/models/user-info fetches with connection reuse - Catalog singleflight with stale fallback to prevent thundering-herd refreshes
- Effective-concurrency cache and gate-limit cache to reduce lock churn under load
- Shutdown queue draining: rejects queued requests with 503 before draining active requests
- Redesigned dashboard UI (unified refresh cycle, concurrency card with burst-zone visualization, sortable usage history with per-model drill-down)
- Service status proxy endpoints (
/api/umans/statuswith 1-min cache,/api/umans/status-historywith 5-min cache) forwarding raw JSON from upstream/v1/statusand/v1/status/history - Usage Limits dashboard card consolidating concurrency usage and priority budget with service mode banner
- Service Status dashboard card with Table/Bars/Heatmap view variants and color-coded health thresholds (uptime, TTFT, TPS)
- Shared features (present in both, no behavioral difference documented):
DISABLED_MODELSconfig field, tool schema normalization, thinking payload normalization, reasoning-level adaptive thinking injection
- Wallpaper sources:
none,bing, orwallhavenonly (nofreegen) - Default listen address:
127.0.0.1:8084
- Go 1.22 or later
- Go to app.umans.ai and sign up or log in.
- Navigate to the API Keys section.
- Click Create API Key — you'll get a key starting with
sk-.... - Copy this key — it will be used by the proxy.
Edit .config/config.json and set your API key:
{
"API_KEY": "sk-..."
}Or via environment variable:
export UMANS_API_KEY=sk-...go build -o umans-dash-go .
./umans-dash-goFor development you can also run directly:
go run .Edit .config/config.json and add the model IDs you want to expose to the ENABLED_MODELS array:
{
"ENABLED_MODELS": ["qwen3-coder", "deepseek-v4-pro"]
}Restart the proxy for the change to take effect.
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'ignored',
baseURL: 'http://127.0.0.1:8084/v1'
});
const response = await client.chat.completions.create({
model: 'qwen3-coder', // must be in your enabled models list
messages: [{ role: 'user', content: 'Hello!' }]
});The proxy also exposes /v1/messages for Anthropic-format clients:
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
apiKey: 'ignored',
baseURL: 'http://127.0.0.1:8084/v1'
});
const response = await client.messages.create({
model: 'umans-kimi-k2.7',
max_tokens: 4096,
messages: [{ role: 'user', content: 'Hello!' }]
});Open http://127.0.0.1:8084 in your browser.
Unified refresh cycle: Status, usage, concurrency, history, and service status all refresh together on the user-selected Automatic Refresh interval. The interval is persisted in
localStorageand restored on page load (default 30s).
- Consolidates concurrency usage progress bar and priority budget inline bars
- Service mode banner shown when upstream is in low-interactivity mode
- Collapsible (expanded by default); remains expanded on mobile
- 4 stat cards: Active (green), Queued (blue), Limit (soft, yellow), Burst (hard cap, orange)
- Progress bar with solid fill (proxy active) and dotted overlay (upstream concurrent)
- Bottom-border zone markers: yellow for soft-cap region, orange for burst region
- The progress bar scales to the effective gate (
barMax): Soft Cap →limit, Hard Cap →hard_cap, Manual →manualLimit. Zones (green→orange gradient) are shown whenbarMax > limit - Percentage shows value relative to the effective gate
- Collapsible
- 6 static stat cards: Requests, Throttled, Cached %, Start Time, Tokens In, Tokens Out
- Collapsible
- Bar chart with Y-axis labels, dashed grid lines, and X-axis labels
- Click a bar to filter the table to that date
- Table shows consolidated per-date rows (Date, Requests, Tokens In, Tokens Out, Cache %, Peak) with sortable headers. Peak shows
peak → weighted peakwith an arrow (same as Requests) - Click a row to expand a per-model detail table (Model, Requests, Tokens In, Tokens Out, Cache %, Peak) with its own sortable headers
- Metric toggle (Tokens/Requests) controls chart scale and default sort
- Status legend shown only in Requests mode; hidden in Tokens mode
- Client-side cache — 30d of data is fetched once per granularity and cached for 5 minutes. Switching between 24h, 7d, and 30d ranges is instant (client-side date filtering, no upstream call)
- Overall health stats and per-model breakdown
- 3 view variants: Table (default), Bars, Heatmap — all fully dynamic from the upstream response
- Color thresholds: uptime ≥99.5% green / ≥99% yellow / below red; TTFT ≤1.5s green / ≤3s yellow / above red; TPS ≥100 green / ≥50 yellow / below red
- Collapsible
- Automatic Refresh — 30s / 1m / 2m / 5m (=298s) interval selector; persisted to
localStorage - Wallpaper — None / Bing / Wallhaven
- Concurrency Limit — Soft Cap / Hard Cap / Manual selector. Manual reveals a slider (1 to hard cap) for a custom concurrency limit. Persisted in config (
CONCURRENCY_LIMIT_MODE,MANUAL_CONCURRENCY_LIMIT) - Slot Free Delay — Slider (0–60s, default 2) that delays freeing a concurrency slot after a request completes. Persisted in config (
SLOT_FREE_DELAY) - Request Timeout — Slider (30s–30m, default 15m) for upstream request timeout, applied live without restart. Positioned above Retry Attempts
- API Key — Smart / Managed / Pass-Through mode selector
- Manage API Keys — Button to open the key management modal (visible when mode is Smart or Managed; hidden in Pass-Through)
- Vision Handoff — Toggle image handoff for vision-incapable models
- Handoff Cache — Toggle caching of vision handoff image analyses (shown only when Vision Handoff is enabled)
- Sections collapse with a chevron icon that swaps between
bi-chevron-down(expanded) andbi-chevron-right(collapsed)
- Health check, connection test, manual refresh, restart proxy
- View and toggle models from the catalog
- Runtime, Port, Started At
- Desktop:
background-size: cover(fills the viewport) - Mobile (≤575px):
background-size: contain(shows the full image centered) - Server-side CSS injection includes full background properties (
no-repeat,center,fixed, base color)
- User ID (click-to-reveal, masked by default) and online status indicator
- All sections except Usage Limits collapse by default on screens ≤575px
- Right-column sections (Quick Settings, Quick Actions, Models, Environment) remain accessible via collapse toggles
The proxy supports three modes for selecting which API key is sent to the upstream UMANS API on each request. The mode is configurable from the dashboard's Quick Settings panel and persisted in config.json via the API_KEY_MODE field.
| Mode | Behavior | Authorization |
|---|---|---|
| Smart (default) | Uses the client's API key if one is provided in the request (Authorization: Bearer <key> or X-Api-Key); falls back to the proxy's own key pool if no client key is present |
Accepts all requests |
| Managed | Always uses the proxy's own key pool, ignoring any client-supplied key | Validates client requests against the API_KEYS allow-list (if configured); open if no allow-list is set |
| Pass-Through | Always uses the client's API key, disabling the proxy's key management entirely | Accepts any request that includes an API key |
Dashboard upstream calls (usage, concurrency, history) adapt to the active mode:
- Pass-Through: uses the last-known-good client key
- Smart: prefers the last-known-good client key, falls back to the pool key
- Managed: uses the pool key
The Manage API Keys button in Quick Settings is hidden in Pass-Through mode.
Some UMANS models (e.g. umans-glm-5.2, umans-glm-5.1) are advertised with supports_vision: "via-handoff" — they cannot process images natively. The proxy transparently bridges this gap:
- When a request to a
via-handoffmodel contains images, the proxy extracts them (OpenAIimage_urland Anthropicimageparts, including nested tool-result blocks). - Each image is sent to the handoff model (default
umans-coder) with an analysis prompt. - If the handoff cache is enabled, the proxy checks a SHA-256 hash of the image data URI against the
ImageHandoffCachefirst. On cache hit, the cached description is reused without calling upstream. On miss, the upstream is called and the result is cached for 24 hours. - Images are analyzed in parallel and replaced in-place with
{type: "text", text: "[Image content — analyzed by vision module, shown as text because the active model cannot see images:]\n<description>"}blocks. - The modified payload (now text-only) is forwarded to the originally requested model.
The handoff applies to both the OpenAI (/v1/chat/completions) and Anthropic (/v1/messages) paths, running after model resolution and before the upstream call.
Config keys:
| Field | Description | Default |
|---|---|---|
VISION_HANDOFF_ENABLED |
Toggle the handoff for all via-handoff models |
false |
VISION_HANDOFF_MODEL |
Vision-capable model used to analyze images | umans-coder |
VISION_HANDOFF_PROMPT |
Custom analysis system prompt (empty = built-in) | — |
VISION_HANDOFF_CACHE_ENABLED |
Enable caching of vision handoff image analyses | false |
VISION_HANDOFF_CACHE_TTL |
TTL for cached vision handoff analyses | 24h |
The built-in prompt instructs the handoff model to produce a factual, third-person description of the image as a single coherent paragraph — covering image type, visible elements and their spatial arrangement, exact transcription of text/code, and salient technical details.
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
Proxy health check (vision handoff status + cache stats) |
GET |
/v1/models |
OpenAI-format model list with pricing and context length |
GET |
/v1/models/info |
Raw model catalog |
POST |
/v1/chat/completions |
OpenAI-format chat completions |
POST |
/v1/messages |
Anthropic Messages API (pass-through to upstream) |
GET |
/api/config |
Get proxy configuration |
POST |
/api/config |
Update proxy configuration |
GET |
/api/validate |
Validate API key |
GET |
/api/models |
List enabled models |
GET |
/api/umans/usage |
UMANS usage data (supports ?fresh=1 to bypass cache) |
GET |
/api/umans/usage-history |
UMANS usage history (supports from, to, granularity, scope; ?fresh=1 bypasses cache) |
GET |
/api/umans/concurrency |
Concurrency sessions, limit, hard_cap, active count & queue depth (supports ?fresh=1) |
GET |
/api/umans/user |
UMANS user info |
GET |
/api/umans/status |
UMANS service status (supports ?fresh=1 to bypass cache) |
GET |
/api/umans/status-history |
UMANS status history (supports from, to, granularity, metric, model; ?fresh=1 bypasses cache) |
GET |
/api/keys |
List API keys |
POST |
/api/keys |
Add/update/delete API keys |
GET |
/api/bg |
Daily Bing wallpaper |
GET |
/api/bg-wallhaven |
Random Wallhaven wallpaper |
POST |
/api/restart |
Restart the proxy |
.config/config.json supports:
| Field | Description | Default |
|---|---|---|
LISTEN_ADDR |
Proxy listen address | 127.0.0.1:8084 |
UPSTREAM_BASE_URL |
UMANS API URL | https://api.code.umans.ai/v1 |
API_KEY |
UMANS API key (sk-*) |
— |
KEYS |
Array of additional API keys for the key pool (name + key) | [] |
REQUEST_TIMEOUT |
Upstream request timeout | 15m |
ENABLED_MODELS |
Array of model IDs to expose | [] |
DISABLED_MODELS |
Array of model IDs to hide from the catalog | [] |
MODEL_DISPLAY_NAMES |
Custom display names per model | {} |
API_KEYS |
Array of allowed proxy API keys (auth) | [] |
OVERRIDE_CONCURRENCY |
Override concurrent sessions value (0 = auto-detect from API; capped to min of override and upstream hard_cap) | 0 |
MAX_IMAGES |
Maximum image attachments per forwarded request; oldest images are dropped first | 9 |
VISION_HANDOFF_ENABLED |
Enable image handoff for vision-incapable models | false |
VISION_HANDOFF_MODEL |
Vision-capable model used to analyze images during handoff | umans-coder |
VISION_HANDOFF_PROMPT |
Custom analysis prompt for the handoff model (empty = built-in) | — |
VISION_HANDOFF_CACHE_ENABLED |
Enable caching of vision handoff image analyses | false |
VISION_HANDOFF_CACHE_TTL |
TTL for cached vision handoff analyses | 24h |
wallpaperSource |
Dashboard wallpaper source: none, bing, or wallhaven |
bing |
API_KEY_MODE |
API key selection mode: smart, managed, or passthrough |
smart |
CONCURRENCY_LIMIT_MODE |
Concurrency gating mode: soft (soft cap), hard (hard cap), or manual (custom via slider) |
soft |
MANUAL_CONCURRENCY_LIMIT |
Custom concurrency limit when mode is manual (0 = auto-initialize to soft cap on first use) |
0 |
SLOT_FREE_DELAY |
Delay in seconds before freeing a concurrency slot after a request completes (0 = immediate) | 2 |
BURST_MODE |
Deprecated. Kept for config migration + rollback safety. true when mode is hard, false otherwise |
false |
# Compile check
go build ./...
# Static analysis
go vet ./...
# Run directly
go run .
# Build binary
go build -o umans-dash-go .This project is licensed under the MIT License — see LICENSE for details.