tbphp bd539713da perf: 优化测试性能与 CI/发布流水线耗时 (#465)
* test(control): 消除测试 fixture 中的重复密钥派生

- internal/control:fixture 构造中 388 次重复执行 PBKDF2(10 万轮
  SHA256)密钥派生,改为共享单例,仅派生一次
- 新增 internal/testutil/encryptiontest,提供按 key material 值
  memoize 的共享 encryption.Service,横向覆盖 gateway、subscription、
  execution/cpa 中同样存在的重复派生(PBKDF2 占比最高达 79%)
- aesService 构造后只读,跨 fixture(含并行测试)共享安全;container
  包同类调用经排查确认属于被测装配流程本身,不适用本次改造

* test(state): 迁移测试改用共享 schema 模板

state/loader 中 openMigratedDatabase 被 21 次调用,每次都完整走一遍
迁移注册表;改为复用 sqlitetest.OpenMigrated 的模板缓存,与 control、
requestlog 部分测试已采用的方式一致。app、container、requestlog 中同
类调用点经逐点排查,均属被测对象本身或依赖真实文件/账本状态,不适用。

* ci(release): 消除 CI 与发布流水线中的耗时浪费

- race-tests 独立 setup-go 缓存命名空间,避免与 test/database-contract
  共用 key 导致 race-instrumented GOCACHE 永远无法写回
- 移除 publish-images 中零命中的 GHA buildx image cache,同时消除其
  失败会拖累发布状态判定为 partial/blocked 的关联风险
- 三个只读 job(仅用 buildx imagetools 查询)移除多余的 buildx 初始化
- native-artifact-smoke 改为按需下载单个 binary + 独立 checksums,
  不再下载完整 release-assets

* test(control): 第一批测试标记 t.Parallel(35 个文件)

internal/control 651 个顶层测试里只有 5.8% 标了 t.Parallel(),其余
在包内是排队串行执行的;race-tests 总耗时现在完全由该包的最长执行时
间决定(213s),是唯一剩余的瓶颈。

本批覆盖 35 个文件、299 个函数。排除 18 个不适用的函数:
- 16 个直接操作全局 logrus 状态的测试(catalog_sync_test.go 等 8 个
  文件),继续保持串行;Go 的 t.Parallel() 调度语义保证未标记的测试
  会在所有并行测试开始前完整跑完,两者不会交叉执行
- external_database_integration_test.go 的 2 个测试依赖
  GPT_LOAD_DATABASE_TEST_DSN:race-tests 中因未设置而被 t.Skip,标记
  并行无收益;database-contract job 中会共享同一个外部数据库实例真
  实执行,标记并行有正确性风险(唯一索引冲突/断言假设独占)

已排查确认安全:t.Setenv/os.Setenv 均为 0 处,无硬编码端口占用,每个
fixture 走独立 :memory: 数据库;本地 -count=3 重复验证无 flaky。

剩余约 297 个函数(36 个文件)作为第二批,待本批 CI 实测确认收益后
再决定是否推进。

* test(control): 第二批测试标记 t.Parallel,补齐排除说明(36 个文件)

覆盖剩余 297 个函数、36 个文件,加上第一批的 299 个,internal/control
除 18 个明确排除的函数外全部标记 t.Parallel()。

为 18 个排除函数逐一补充一行注释说明原因,避免日后被误认为遗漏而被
顺手加上 t.Parallel():
- 16 个劫持全局 logrus 状态的测试
- 2 个依赖 GPT_LOAD_DATABASE_TEST_DSN 共享外部数据库的测试

本地 -count=3 重复验证、全量 go test -count=1 . ./internal/...(58
包 ok)均通过。

* fix(release): 采纳 Review 反馈,修正文档措辞与产物保留期

- encryptiontest.Service 的文档注释修正为明确说明缓存仅在单个包的
  测试二进制内有效,不跨包共享(每个包的 go test 是独立进程)
- native-artifact-smoke 依赖的 binary-*、release-checksums 保留期
  从 1 天提高到与 release-assets 一致的 14 天:失败的 release run
  若延迟超过 1 天才重试,此前较短的保留期会导致产物过期、下载失败,
  阻塞发布流程

以下反馈经核实判定为不成立,未采纳:
- validation_test.go 中两个测试被指"竞争全局并发槽位"——经查
  newValidationWorkerForTest 每次调用构造全新独立实例(worker、
  channel、mutation coordinator 等),validationConcurrency 只是
  编译期常量,两个测试之间不存在任何运行时共享状态
2026-08-24 20:07:11 +08:00
2025-07-02 17:15:10 +08:00
2025-06-09 22:27:30 +08:00
2026-08-07 19:00:58 +08:00

GPT-Load

GPT-Load

A self-hosted AI gateway for multi-channel, multi-credential setups

API keys, subscription accounts, traffic scheduling, failure handling, request logs, and usage accounting — behind a single entry point.

English · 中文 · 日本語

Release Docker Go License

tbphp/gpt-load | Trendshift Featured|HelloGitHub


Your application only needs one base URL and one AccessKey. Providers, accounts, credentials, models, and routing policy are all configured in the management UI.

flowchart LR
    C["Application / AI client"]
    G["GPT-Load<br/>————————<br/>Native protocol entry<br/>Scheduling · Retry · Health isolation<br/>Logs · Usage · Cost estimates"]
    U1["Official APIs"]
    U2["Cloud platforms"]
    U3["Compatible relays"]
    U4["Subscription accounts"]

    C -->|"One base URL<br/>One AccessKey"| G
    G --> U1
    G --> U2
    G --> U3
    G --> U4

Why GPT-Load

  • One gateway for many upstreams — Official APIs, cloud platforms, popular model services, and OpenAI-compatible relays are all managed in one place.
  • API keys and subscription accounts share one mechanism — Subscription channels like Codex, Claude, Antigravity, and Grok use the same credential management, scheduling, and health system as API key channels.
  • Get the most out of a credential pool — Multi-credential scheduling, automatic weighting, retries, cooldown, blacklisting, and session affinity reduce the impact of a single overloaded or failing credential.
  • Clients keep their native protocol — Applications keep using OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, or the Gemini native API without code changes.
  • Every call is visible — Health state, route inspection, request logs, usage rollups, and per-model cost estimates make problems and consumption easy to trace.
  • Simple to deploy, your data stays yours — The management UI is embedded in a single Go binary; SQLite by default, MySQL or PostgreSQL optional; channel credentials are encrypted at rest locally.

Screenshots

Home — Groups and credentials at a glance, one-click client setup, 30-day cost estimate

GPT-Load home

Monitoring — Request volume, cache rate, token breakdown, cost estimates, usage quality

GPT-Load monitoring

Scope

Client protocols

Protocol Main entry
OpenAI Chat Completions POST /v1/chat/completions
OpenAI Responses /v1/responses and its resource paths
Anthropic Messages POST /v1/messages
Gemini /v1beta/models/...

Each channel declares exactly which protocols and capabilities it can execute. GPT-Load converts between supported capabilities, but it is not a general-purpose any-protocol, any-JSON translator.

Built-in channels

Show all 20 built-in channels
  • Official and cloud — OpenAI, Anthropic, Gemini, xAI, Azure OpenAI, AWS Bedrock, Google Vertex AI
  • Model services — DeepSeek, Moonshot AI, SiliconFlow, Zhipu AI, Alibaba, Volcengine, OpenRouter, Groq
  • Subscription — Codex, Claude, Antigravity, Grok
  • Custom — OpenAI Compatible (any compatible relay)

Quick start

1. Start the service

Requires Docker and Docker Compose.

git clone --depth 1 --branch v2 https://github.com/tbphp/gpt-load.git
cd gpt-load

cp .env.example .env
docker compose up -d

Confirm the service is up:

curl --fail http://127.0.0.1:3001/health

The first start generates a management key. Read it and store it safely:

docker compose exec gpt-load sh -c 'cat /app/data/auth.key'

Open http://127.0.0.1:3001 and sign in to the console with that key.

You can also set AUTH_KEY explicitly in .env before starting. By default the service listens on the loopback address only and is not exposed to the internet.

2. Initial configuration

The console has three configuration layers:

flowchart LR
    A["① Channel<br/>Add API keys<br/>or complete OAuth"] --> B["② Group<br/>Pick a channel<br/>Set models and policy"] --> C["③ AccessKey<br/>Pick groups and protocols<br/>Hand it to your app"]
  1. Add a channel — Choose an upstream service and add one or more API keys. For subscription channels, complete the OAuth flow or import credentials as prompted.
  2. Create a group — Pick a channel, then configure available models and runtime policy.
  3. Create an AccessKey — Set the groups and client protocols it may use, then give the generated AccessKey to your application.
OAuth callback ports for subscription channels

The Codex, Claude, and Antigravity OAuth clients use fixed callback ports. Compose publishes them on the address configured by HOST, which defaults to 127.0.0.1; setting HOST=0.0.0.0 also publishes these callback ports on all host interfaces. Because the ports are fixed by the upstream clients, only one default Compose instance can run on a host at a time.

When working over SSH or from a remote browser, the browser's localhost may not reach GPT-Load — paste the full callback URL into the authorization dialog to finish the flow.

3. Send your first request

Replace the AccessKey and model ID with the real values from your console:

export GPT_LOAD_ACCESS_KEY="your-access-key"

curl http://127.0.0.1:3001/v1/chat/completions \
  -H "Authorization: Bearer ${GPT_LOAD_ACCESS_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model-id",
    "messages": [{ "role": "user", "content": "Hello, introduce yourself." }]
  }'

Using existing clients

For the OpenAI SDK or any OpenAI-compatible client, two settings usually change:

Base URL: http://127.0.0.1:3001/v1
API Key:  the AccessKey created in GPT-Load
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:3001/v1",
    api_key="your-access-key",
)

response = client.responses.create(
    model="your-model-id",
    input="Hello",
    store=False,
)

print(response.output_text)

Anthropic clients use /v1/messages and Gemini clients use /v1beta/models/.... Authenticate the way each client normally does: Authorization: Bearer, x-api-key, x-goog-api-key, or Gemini's key query parameter.

Deployment and data

Docker Compose uses application-managed SQLite by default. Data lives in the gpt-load-data named volume and includes the database, auth.key, and encryption.key.

Important

encryption.key decrypts channel credentials. When backing up or migrating, the database and the key must be kept together. Once the key is lost or replaced, existing encrypted credentials cannot be recovered, and this version does not support master key rotation.

For an external database, use the unified DATABASE_DSN to connect SQLite, MySQL, or PostgreSQL:

mysql://user:password@db.example:3306/gpt_load?charset=utf8mb4&collation=utf8mb4_bin
postgres://user:password@db.example:5432/gpt_load?sslmode=require

See .env.example for the full configuration reference.

Common operations:

docker compose logs -f      # view logs
docker compose pull && docker compose up -d   # update to the latest 2.x image
docker compose stop         # stop the service

The official 2.x Compose file uses ghcr.io/tbphp/gpt-load:v2beta and does not rely on the latest tag.

Using a native binary

Download the build for your platform from GitHub Releases, and verify it against the bundled SHA256SUMS first:

chmod +x ./gpt-load-linux-amd64
mkdir -p ./data

HOST=127.0.0.1 DATA_DIR=./data ./gpt-load-linux-amd64

Then open http://127.0.0.1:3001. Builds are provided for five targets across Linux, macOS (amd64 / arm64), and Windows.

Before you go to production

  • The service listens on 127.0.0.1 only by default. For remote access, expose it through a controlled network or a TLS reverse proxy, and configure ACLs and firewall rules.
  • Manage AUTH_KEY and ENCRYPTION_KEY carefully. Never commit real keys to a repository, log, screenshot, or public issue.
  • 2.0 is designed for a single application instance. Instances do not share state, so horizontal scaling is not supported.
  • Usage and cost are estimates derived from upstream responses. They support operational analysis and capacity planning, and do not equal a provider invoice or a financial reconciliation.
  • Subscription channels depend on upstream OAuth and compatibility protocols and may change as upstreams change. Only connect accounts you are entitled to use, and follow each provider's terms.
  • In OpenAI Responses, stateful requests relying on previous_response_id, conversation, or an existing resource ID are only reliable with a single credential, or with an upstream that shares resources across credentials.

Moving from 1.x

Warning

GPT-Load 2.0 is a complete rewrite. It cannot open, import, or migrate 1.x data in place.

Deploy 2.0 with its own database, DATA_DIR, port, and Docker volume. Cut traffic over only after verification, and keep the original 1.x deployment until the rollback window closes. Documentation for the 1.4.x maintenance line is at the official docs.

Open-source dependencies

Some of GPT-Load's capabilities build on these projects, with thanks:

Project Role License
Bifrost Core Provider authentication, request/response conversion, streaming, usage normalization Apache-2.0
CLIProxyAPI OAuth and execution adapter for subscription channels MIT
Lobe Icons Channel brand icons in the management UI MIT

GPT-Load owns credential storage, account selection, scheduling, retry, health, affinity, logging, and usage policy. Third-party notices are in THIRD_PARTY_NOTICES.md, full license texts in LICENSES/, and each release ships a CycloneDX SBOM covering the Go dependency graph.

Channel icons identify their respective upstream providers. All trademarks belong to their owners; this project is not affiliated with or endorsed by them.

Feedback and contributing

For problems or feature ideas, open a GitHub Issue. Report security vulnerabilities through the process in SECURITY.md.

For community chat and usage discussion, join the Telegram group.

If GPT-Load is useful to you, a star is appreciated.

Sponsors and support

APIMart Thanks to APIMart for sponsoring this project! APIMart is a low-cost API platform for AI image & video generation — GPT-Image-2 from $0.006/image, 160+ images per dollar. One async API covers both image and video: submit a task, get an ID, fetch results via polling or callback. Batch tens of thousands of images without timeouts, switch models without changing code. Pay-as-you-go with no monthly fee — sign up here to get started.
OpenAI Thanks to OpenAI for sponsoring this project.
LINUX DO Thanks to the LINUX DO community for their support.
DigitalOcean This project is supported by DigitalOcean.

License

GPT-Load is released under the MIT License.

S
Description
Self-hosted AI gateway for multi-channel, multi-credential setups — API keys and subscription accounts, scheduling, failover, request logs and usage. 自托管 AI 网关:多渠道多凭据统一接入,含密钥与订阅账号、调度容错、日志与用量。
https://www.gpt-load.com
Readme MPL-2.0 Apache-2.0 Inno-Setup MIT
27 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 65.9%
Vue 20.8%
TypeScript 12.1%
CSS 0.3%
Shell 0.3%
Other 0.5%