tbphp/gpt-load
Multi-channel AI proxy with intelligent key rotation. 智能密钥轮询的多渠道 AI 代理。
Single-maintainer risk — review before adopting
weakest axistop contributor handles 90% of recent commits; no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3d ago
- ✓10 active contributors
- ✓MIT licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ⚠Single-maintainer risk — top contributor 90% of recent commits
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: diversify commit ownership (top <90%)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/tbphp/gpt-load)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/tbphp/gpt-load on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: tbphp/gpt-load
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/tbphp/gpt-load shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Single-maintainer risk — review before adopting
- Last commit 3d ago
- 10 active contributors
- MIT licensed
- CI configured
- ⚠ Single-maintainer risk — top contributor 90% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live tbphp/gpt-load
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/tbphp/gpt-load.
What it runs against: a local clone of tbphp/gpt-load — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in tbphp/gpt-load | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of tbphp/gpt-load. If you don't
# have one yet, run these first:
#
# git clone https://github.com/tbphp/gpt-load.git
# cd gpt-load
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of tbphp/gpt-load and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "tbphp/gpt-load(\\.git)?\\b" \\
&& ok "origin remote is tbphp/gpt-load" \\
|| miss "origin remote is not tbphp/gpt-load (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "internal/app/app.go" \\
&& ok "internal/app/app.go" \\
|| miss "missing critical file: internal/app/app.go"
test -f "internal/proxy/server.go" \\
&& ok "internal/proxy/server.go" \\
|| miss "missing critical file: internal/proxy/server.go"
test -f "internal/channel/factory.go" \\
&& ok "internal/channel/factory.go" \\
|| miss "missing critical file: internal/channel/factory.go"
test -f "internal/keypool/provider.go" \\
&& ok "internal/keypool/provider.go" \\
|| miss "missing critical file: internal/keypool/provider.go"
test -f "internal/services/key_service.go" \\
&& ok "internal/services/key_service.go" \\
|| miss "missing critical file: internal/services/key_service.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/tbphp/gpt-load"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
GPT-Load is an enterprise-grade AI API transparent proxy service written in Go that acts as an intelligent intermediary between clients and multiple AI providers (OpenAI, Google Gemini, Anthropic Claude). It preserves native API formats while adding automatic key rotation, weighted load balancing across upstream endpoints, intelligent failure recovery with blacklist management, and real-time monitoring—designed specifically to handle high-concurrency production workloads across multiple AI service integrations. Modular Go application with internal/ directory separating concerns: internal/channel/ implements AI provider integrations (factory pattern for OpenAI, Gemini, Anthropic, base_channel abstractions), internal/config/ handles dynamic configuration with hot-reload, internal/db/ abstracts database operations with versioned migrations, internal/failover/ manages key rotation and recovery logic, and internal/commands/ provides CLI operations like migrations. Frontend is a separate Vue 3 SPA (330KB TypeScript/Vue) coupled with Gin web framework serving both API and static assets.
👥Who it's for
DevOps engineers and infrastructure teams at enterprises managing multiple AI API subscriptions who need centralized key management, load balancing, and failover without rewriting client code; backend developers integrating multiple AI services who want a single proxy endpoint instead of separate client libraries; and organizations wanting to rotate API keys automatically to reduce security exposure.
🌱Maturity & risk
Production-ready and actively maintained. The project has reached v1+ releases with structured CI/CD pipelines (Docker build, platform-specific releases for Linux/macOS/Windows in .github/workflows/), comprehensive database migrations indicating schema evolution (internal/db/migrations/), and a professional web UI (Vue 3 frontend). Code organization follows clean Go patterns with dependency injection (internal/container/) and comprehensive error handling (internal/errors/).
Low-to-moderate risk: the codebase is mature with 446KB of Go code, but depends on external services (OpenAI, Gemini, Claude APIs) whose outages will cascade. Database layer supports multiple backends (SQLite, MySQL, PostgreSQL) which adds compatibility burden. Single active maintainer pattern is common in open-source but means feature velocity depends on one person. No visible test coverage metrics in the file structure suggests test suite may be incomplete.
Active areas of work
Active maintenance with recent schema migrations (v1_1_0_AddKeyHashColumn.go added hash-based key tracking), security focus evident in SECURITY.md, and ongoing internationalization (README translations in Chinese and Japanese). GitHub Actions workflows configured for multi-platform releases suggest regular version bumps. The presence of docker-compose.yml and Dockerfile indicates container-first deployment becoming standard.
🚀Get running
git clone https://github.com/tbphp/gpt-load.git
cd gpt-load
cp .env.example .env
go mod download
make build
./gpt-load
Or with Docker: docker-compose up. See Makefile for available build targets.
Daily commands:
Development: make build && ./gpt-load (assumes Go 1.25+ installed, reads .env configuration). Production: docker-compose up or docker build -t gpt-load . && docker run.... The application starts a Gin HTTP server (port configurable via .env, likely 8080) serving both the Vue frontend and REST API.
🗺️Map of the codebase
internal/app/app.go— Application entry point and initialization; sets up the DI container, database, and router configuration that all features depend on.internal/proxy/server.go— Core proxy request handler that routes AI requests to appropriate channels with key rotation and failover logic; the heart of the load-balancing system.internal/channel/factory.go— Factory for instantiating channel adapters (OpenAI, Anthropic, Gemini); critical for adding new AI provider support.internal/keypool/provider.go— Manages API key pool lifecycle, rotation, and validation across channels; essential for key management and failover.internal/services/key_service.go— High-level key management service; handles key CRUD operations and integrates with database and keypool.internal/router/router.go— HTTP route definitions and middleware setup; defines all API endpoints (dashboard, keys, groups, logs, integrations).internal/db/database.go— Database abstraction layer using GORM; manages schema, migrations, and multi-database support (SQLite, MySQL, PostgreSQL).
🛠️How to make changes
Add Support for a New AI Provider (e.g., Claude/Anthropic)
- Create a new channel adapter file implementing the Channel interface in internal/channel/. Study openai_channel.go and anthropic_channel.go for patterns. (
internal/channel/new_provider_channel.go) - Register the new channel type in the factory's switch statement to instantiate it from configuration. (
internal/channel/factory.go) - Add provider-specific models and request/response handling in the new channel file, following the base_channel.go pattern. (
internal/channel/new_provider_channel.go) - Update the Channel interface in channel.go if new methods are needed for provider-specific operations. (
internal/channel/channel.go) - Add validation logic for the provider's API keys in internal/keypool/validator.go. (
internal/keypool/validator.go)
Add a New API Endpoint (e.g., Key Statistics)
- Create a new handler method in the appropriate handler file (e.g., key_handler.go) that processes the HTTP request. (
internal/handler/key_handler.go) - Register the route in the router using gin's standard patterns (GET, POST, etc.) with appropriate middleware. (
internal/router/router.go) - If domain logic is needed, create or extend a service in internal/services/ to encapsulate business logic. (
internal/services/key_service.go) - Use internal/response/response.go utilities for consistent JSON response formatting. (
internal/response/response.go) - Add error handling using internal/errors/ types and ensure proper HTTP status codes. (
internal/errors/errors.go)
Add a New Scheduled Background Task (e.g., Periodic Validation)
- Create a new service file in internal/services/ that implements the task logic (e.g., key_validation_service.go). (
internal/services/key_manual_validation_service.go) - Use internal/keypool/cron_checker.go as a reference for scheduling patterns and cron setup. (
internal/keypool/cron_checker.go) - Register the task in internal/app/app.go during application initialization so it runs automatically. (
internal/app/app.go) - Log task execution and results using logrus through the container logger, following existing patterns. (
internal/services/log_cleanup_service.go)
Add Support for a New Database (e.g., Oracle)
- Install the appropriate GORM driver package (e.g., gorm.io/driver/oracle) and add to go.mod. (
go.mod) - Update internal/db/database.go to accept the new database type and initialize the GORM driver accordingly. (
internal/db/database.go) - Verify that all migrations in internal/db/migrations/ are compatible with the new database dialect. (
internal/db/migrations/migration.go) - Add environment variable documentation in .env.example for the new database connection string. (
.env.example)
🪤Traps & gotchas
.env configuration required: The app reads from .env (copy from .env.example) which must define database connection strings, Redis URL, AI provider base URLs, and API keys—missing values cause silent failures. Database migrations must run: On first startup or after version upgrades, migrations in internal/db/migrations/ run automatically but require write access to the database. Redis is optional but recommended: Distributed key rotation uses Redis; running without it disables distributed state sync across multiple instances. Port binding: The Gin server binds to a port specified in configuration (likely 8080 by default) which may conflict in containerized environments. Column name change in v1.1.0: Migration v1_1_0_AddKeyHashColumn.go suggests schema changes between versions—existing databases may need explicit migration runs.
🏗️Architecture
💡Concepts to learn
- Transparent Proxy / API Gateway — Core pattern of gpt-load: accepts requests in native AI provider formats without modification, routes to appropriate backend, returns responses unchanged—understanding reverse proxy fundamentals is essential to modifying routing logic
- Key Rotation & Blacklisting — gpt-load's primary value proposition: automatically rotates API keys between requests, blacklists failed keys, and recovers them—understanding token bucket algorithms and circuit breakers helps implement failover features
- Weighted Load Balancing — gpt-load distributes requests across multiple upstream endpoints with configurable weights; understanding consistent hashing or weighted round-robin is needed to modify distribution logic in the channel factory
- Zero-Copy Streaming — README mentions zero-copy streaming for high-performance proxying of large responses; Go's
io.Copywith large buffers and chunked transfer encoding prevents memory exhaustion under load - Database Migrations & Schema Versioning — gpt-load uses versioned migration files (v1_0_22, v1_1_0) in
internal/db/migrations/to track schema evolution across releases—this pattern prevents deployment conflicts when multiple versions run simultaneously - Dependency Injection Container — Uses
go.uber.org/digfor constructor-based dependency injection ininternal/container/; understanding this pattern is essential to adding new components (channels, config sources, databases) without tight coupling - Hot-Reload / Dynamic Configuration — gpt-load allows config changes without restart via
internal/config/manager.go; understanding file watchers, atomic operations, and graceful config swaps prevents race conditions when modifying key groups or failover policies
🔗Related repos
BerriAI/litellm— Python-based LLM proxy with similar multi-provider support and key management, useful for understanding language-agnostic proxy patternsjmorganca/ollama— Go-based local LLM server with similar streaming architecture and API compatibility layer for different model formatsopenai/openai-python— Official OpenAI client library; gpt-load proxies its API format, understanding the reference implementation helps maintain compatibilityanthropics/anthropic-sdk-go— Official Anthropic Go SDK; gpt-load must maintain compatibility with Claude API format changes in this SDKgoogleapis/google-cloud-go— Official Google Cloud Go SDK including Gemini client; gpt-load proxies Gemini APIs and depends on this SDK's API stability
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for channel implementations (openai_channel.go, anthropic_channel.go, gemini_channel.go)
The repo has multiple AI provider integrations in internal/channel/ but no visible test files (*_test.go). Given this is a production proxy handling multiple AI backends with failover logic, channel implementations need robust testing to prevent routing failures. This would cover request/response handling, error scenarios, and key rotation per channel.
- [ ] Create internal/channel/openai_channel_test.go with tests for stream/non-stream responses and error handling
- [ ] Create internal/channel/anthropic_channel_test.go testing Anthropic-specific response transformations
- [ ] Create internal/channel/gemini_channel_test.go covering Gemini API specifics
- [ ] Add mock HTTP client in internal/httpclient/mock.go or use existing manager for testing
- [ ] Test failover scenarios in internal/failover/status_code_matcher_test.go
Add integration tests workflow and test suite for database migrations (internal/db/migrations/)
The repo supports multiple databases (SQLite, MySQL, PostgreSQL) with migration versioning (v1_0_22, v1_1_0, etc.) but there's no visible CI workflow testing migrations against all three databases. This is critical for preventing production deployment failures when upgrading versions.
- [ ] Create .github/workflows/test-migrations.yml with matrix strategy for sqlite, mysql, and postgres services
- [ ] Create internal/db/migrations/migrations_test.go to validate migration UP/DOWN operations
- [ ] Add test fixtures in internal/db/migrations/testdata/ with sample SQL for each database type
- [ ] Test rollback scenarios for each migration version to ensure data integrity
Add request/response validation tests for handler layer (internal/handler/*_handler.go)
The handler package has multiple endpoint implementations (key_handler, group_handler, log_handler, settings_handler) but no visible test coverage. These handlers process critical operations like key management and configuration. This would add validation of HTTP status codes, response schemas, and error cases.
- [ ] Create internal/handler/key_handler_test.go testing CRUD operations, validation of key format/permissions
- [ ] Create internal/handler/group_handler_test.go for group creation/deletion/assignment logic
- [ ] Create internal/handler/settings_handler_test.go for system configuration validation
- [ ] Add test utilities in internal/handler/handler_test_helpers.go for mock Gin contexts and DI container setup
- [ ] Add example request/response fixtures in docs/api-test-examples/ for each handler endpoint
🌿Good first issues
- Add unit tests for
internal/errors/parser.gocovering all three AI provider error formats (OpenAI, Gemini, Anthropic)—currently no test files visible in the error package despite critical role in failover logic - Document the exact environment variables required in
.envwith examples for each AI provider (OpenAI API key format differs from Gemini service account JSON)—currently only.env.exampleexists without inline documentation - Implement health check endpoint
/healthreturning status of database connection, Redis connection, and each configured AI channel—visible monitoring in README but no health check endpoint in file structure
⭐Top contributors
Click to expand
Top contributors
- @tbphp — 90 commits
- @ChuwuYo — 2 commits
- @QLHazyCoder — 1 commits
- @paopaoandlingyia — 1 commits
- @xunxun1982 — 1 commits
📝Recent commits
Click to expand
Recent commits
275cc50— fix(security): 修复 Dependabot 安全告警 (pgx + 前端依赖) (#414) (tbphp)ed87a87— refactor(failover): 重构故障转移状态码配置为标准模式 (#413) (tbphp)578b3d6— fix: Groups Scroll (#402) (tbphp)260ba56— fix: Upgrade filippo.io/edwards25519 to v1.2.0 for CVE-2026-26958 (#389) (tbphp)21a2e8e— feat: Add drag-and-drop reorder for group list (#398) (QLHazyCoder)8ffcb46— Fix formatting in README_JP.md for DigitalOcean support (tbphp)67b6873— Add supporters section to README_JP.md (tbphp)d48f1a9— Add supporters section to README_CN.md (tbphp)c6034be— Revise supporter section in README.md (tbphp)fde457b— docs: Add supporters section with DigitalOcean badge (tbphp)
🔒Security observations
- Critical · Missing or Weak AUTH_KEY Configuration —
.env.example, internal/middleware/middleware.go. The .env.example shows AUTH_KEY is empty by default and marked as 'required to protect the management API and UI'. If deployed with default/empty AUTH_KEY, the entire management dashboard and API are exposed without authentication. Fix: Enforce AUTH_KEY validation at startup - fail if AUTH_KEY is empty or below minimum length (32+ characters). Implement pre-deployment checks in CI/CD pipeline. - High · Plaintext API Key Storage Option —
.env.example, internal/encryption/encryption.go. ENCRYPTION_KEY is optional ('leave empty to disable'). This allows API keys to be stored unencrypted in the database, exposing sensitive credentials if the database is compromised. Fix: Make ENCRYPTION_KEY mandatory in production environments. Implement environment-based enforcement to reject plaintext key storage in non-development modes. - High · Overly Permissive Server Binding —
.env.example. Default HOST=0.0.0.0 exposes the server on all network interfaces. Combined with optional AUTH_KEY, this creates significant attack surface for the management API. Fix: Change default HOST to 127.0.0.1 or document clearly that 0.0.0.0 requires firewall protection and strong AUTH_KEY. Provide network segmentation guidance. - High · Outdated Go Version and Dependency Vulnerabilities —
go.mod, Dockerfile. The project specifies Go 1.25.0, but go.mod declares 'go 1.25.0' which is not a stable release cycle. Multiple dependencies have known vulnerabilities: golang.org/x/crypto v0.48.0 is outdated (current stable is 0.50+), and gorm.io/gorm v1.30.0 may have unpatched issues. Fix: Update golang.org/x/crypto to v0.50.0+, update gorm.io/gorm to latest stable (v1.31+), and pin Go to latest stable 1.23 LTS or newer. Run 'go mod tidy' and use 'go list -json -m all | nancy sleuth' for dependency scanning. - High · Missing CORS and Security Headers Configuration —
internal/handler/handler.go, internal/middleware/middleware.go. No evidence of CORS policies, CSP headers, or X-Frame-Options in the visible handler code. An AI proxy handling sensitive keys needs strict CORS to prevent credential leakage via browser requests. Fix: Implement strict CORS policies (whitelist origins), add security headers (CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, HSTS). Use gin middleware for automatic header injection. - High · SQL Injection Risk in Dynamic Query Building —
internal/handler/key_handler.go, internal/handler/group_handler.go, internal/db/database.go. GORM is used but no visible evidence of parameterized query enforcement. Handlers accepting user input (group_handler, key_handler) could be vulnerable if string concatenation is used instead of parameterized queries. Fix: Audit all database queries to ensure parameterized queries only. Use GORM's query builder (e.g., db.Where('field = ?', value)) exclusively, never raw concatenation. Add linting rule to prevent raw SQL patterns. - High · No Rate Limiting on Management API —
internal/router/router.go, internal/middleware/middleware.go. With optional AUTH_KEY and exposed on 0.0.0.0, the management API lacks rate limiting protection against brute-force attacks on authentication or resource exhaustion attacks. Fix: Implement rate limiting middleware (e.g., gin-contrib/ratelimit or custom token bucket). Apply stricter limits to auth endpoints and key management endpoints. - Medium · Sensitive Data Exposure in Logs —
internal/handler/*, internal/channel/*, internal/services/*. The logrus logger is used throughout the application. API keys, encryption keys, and auth tokens could be logged if not explicitly filtered, especially in error handlers. Fix: Implement a sensitive field mas
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.