安装
如何把一个 HybridInference 网关跑起来——可以用 Docker 服务栈,也可以用一份能直接改的源码检出。
如果你更想先看到网关真的应答一次请求、再去配置别的,那就从快速开始开始。它跑的是一个确定性的假 provider,不需要 provider 账号、不需要 API key,连 .env 都不需要。本页是下一步:用你自己的 provider,搭你自己的部署。
需要准备什么
用途 |
要求 |
|---|---|
Docker 服务栈 |
Docker Engine 24+ 与 Docker Compose v2+ |
源码检出 |
Python 3.10–3.13(按 |
在 Docker 之外运行控制台 |
Node.js 22(CI 安装的版本) |
Linux 或 macOS。所有依赖都来自 PyPI,所以一次普通的 uv sync 除了能访问索引之外别无要求。
用 Docker 快速开始
git clone <repository-url> hybridinference
cd hybridinference
cp .env.example .env
接下来编辑 .env。这三个变量没有值时,make up 会在启动任何东西之前中止——deploy/docker/docker-compose.yml 用 Compose 的 :? 必填写法声明了它们,而 Compose 是在变量插值阶段失败,不是在运行时:
变量 |
|
|---|---|
|
已设为 |
|
为空;必须自己填 |
|
为空;必须自己填 |
还有两个 Compose 不强制、但在有人注册之前就该设好:JWT_SECRET_KEY 和 API_KEY_SECRET。两者出厂都是空的;如果一直空着,apps/backend/serving/servers/app.py 会打一条 CRITICAL 日志,然后带着不安全的 token 和不安全的 API key 哈希继续运行。各生成一个:
python3 -c "import secrets; print(secrets.token_urlsafe(48))"
然后启动这套服务栈:
make up # creates the external volume, then brings up Compose
make ps # show the services and their health
curl -s http://localhost:8080/health
make up 会先执行 Makefile 的 docker-volumes 目标,在 Docker 卷 hybridinference_postgres_data 不存在时创建它。这个卷在 Compose 文件里声明为 external: true,所以 Compose 自身从不创建、也不删除它——想清空数据库之前,先看部署。
会启动三个容器:
服务 |
发布地址 |
说明 |
|---|---|---|
|
|
FastAPI 网关;用 |
|
|
Next.js 控制台;用 |
|
|
用 |
pgAdmin 和 Codex on-call relay 也在 Compose 文件里,但被 profile 挡着,不主动要求就不会有东西启动它们(见部署指南)。
全新的服务栈会提供哪些模型
一份没有自带模型注册表的克隆并不是空的。当 MODELS_CONFIG_PATH 和处于 active 的 distribution manifest 都没有点名某个文件时,后端会回退到内置的参考注册表 config/examples/models.openrouter.yaml,并搭配 config/examples/routing.minimal.yaml(见 apps/backend/serving/config/distribution.py 的 _LEGACY_DEFAULTS)。这份注册表通过 OpenRouter 路由,只需要一个凭据:
OPENROUTER_API_KEY=... # in .env
改完之后要再跑一次 make up,而不是 make restart:容器只在创建时读取自己的 env_file,所以 docker compose restart 会原样保留旧的环境变量,而 up 会把配置有变动的服务重建。要让网关改用你自己的注册表,见下面的配置和添加新模型。
开发用检出(不用 Docker)
git clone <repository-url> hybridinference
cd hybridinference
make setup-dev
make setup-dev 会用 Python 3.12 创建 .venv(如果已有的 .venv 是别的次版本,它会拒绝继续),以可编辑方式安装本项目,同步 dev 依赖组,并装好 pre-commit hooks。手动来做的话:
uv venv -p 3.12
source .venv/bin/activate
uv sync --group dev
在仓库根目录运行网关——后端的包在 apps/backend/ 下面,所以才要设置 PYTHONPATH:
cp .env.example .env # edit as above; a process started here reads it
PYTHONPATH=apps/backend uv run uvicorn serving.servers.app:app \
--host 127.0.0.1 --port 8080
在另一个终端里运行控制台:
cd apps/frontend
npm ci
npm run dev # listens on :3001
想在不启动整套服务栈的情况下对着 Postgres 开发,就只起数据库容器:
docker compose -f deploy/docker/docker-compose.yml --env-file .env up -d postgres
或者在 .env 里设 DB_ENABLED=false,完全不带数据库运行:网关照样路由请求,/health 会报告 "database_configured": false。账户、API key 和请求历史则需要数据库。
配置
环境变量
仓库根目录的 .env 就是那唯一的一个文件。有两处会读它:
backend 进程,当你从仓库根目录启动它时(
apps/backend/serving/config/settings.py里的Settings.Config.env_file = ".env");各个容器,因为 Compose 以
--env-file .env调用,而 backend 服务也把它列为env_file。
除了快速开始里那五个必填变量(DB_NAME、DB_USER、DB_PASSWORD、JWT_SECRET_KEY、API_KEY_SECRET),以及默认注册表需要的 OPENROUTER_API_KEY,.env.example 中的一切都是可选的。最可能用得上的是这些:
变量 |
作用 |
|---|---|
|
|
|
|
|
|
|
|
|
用户在验证邮件和重置邮件里点击的绝对 URL |
|
本网关自己的公网 origin;留空则从请求中推导 |
|
日志详细程度,以及 |
|
进程内告警,默认关闭 |
|
是否信任 |
Provider 凭据
代码里并没有一份固定的 provider 变量清单。模型注册表在加载时会对 ${VAR} 和 ${VAR:-default} 做插值(apps/backend/routing/config.py 里的 _expand_env_value),所以一个部署需要哪些 provider 凭据,正好就是它自己的注册表点名的那些变量:
route:
- kind: openrouter
base_url: https://openrouter.ai/api/v1
api_keys:
- ${OPENROUTER_API_KEY}
.env.example 为本项目有 adapter 或示例的那些 provider 提供了空白占位;你也可以随意添加自己的变量名。内置的默认注册表只需要 OPENROUTER_API_KEY。
配置文件在哪里
本仓库里没有 config/models.yaml,也没有 config/routing.yaml。apps/backend/serving/config/distribution.py 里的 resolve_config_path 按以下顺序解析每个配置文件:
显式的环境变量——
MODELS_CONFIG_PATH、ROUTING_CONFIG_PATH、ALERTS_CONFIG_PATH(旧的MODELS_CONFIG和ROUTING_CONFIG写法仍然接受;两者都设置时以*_CONFIG_PATH为准);distribution manifest——
DISTRIBUTION_CONFIG_PATH指向某个distributions/<name>/distribution.yaml,其中的paths:一节点名了各个文件。该 manifest 只在DISTRIBUTION_CONFIG_MODE=active时才生效;默认的dark只加载并校验它、记录它本来会改动什么,而解析结果保持原样;内置默认值——
config/examples/models.openrouter.yaml和config/examples/routing.minimal.yaml。没有默认的告警文件,它的缺席意味着内置阈值生效。
路径相对于工作目录(容器里是 /app)。Compose 文件把 config/ 和 distributions/ 都以只读方式挂进 backend,所以在宿主机上改动其中任意一个再重启 backend 就够了——不必重新构建镜像。
这些文件里面该写什么,见配置;一个完整走通的 overlay 示例见快速开始。
备注
直接跑在宿主机上的 backend 可以通过 localhost 访问宿主机上的推理服务器。而 Docker 里的 backend 必须用 Docker 能访问到的地址,例如 host.docker.internal,并在模型注册表里显式写出来;HybridInference 不会改写 provider 的 URL。
验证安装
make lint # ruff format --check, ruff check, pydocstyle
make test # pytest, excluding the external and dbtest tiers
make check # lint + test
make format # ruff format, then ruff check --fix --unsafe-fixes
对着运行中的网关做一次端到端验证:
curl -s http://localhost:8080/v1/models
curl -s http://localhost:8080/v1/chat/completions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${HYBRIDINFERENCE_API_KEY}" \
-d '{"model":"<model-id>","messages":[{"role":"user","content":"Say hello."}]}'
如果设了 USER_AUTH_ENABLED=0,就去掉 Authorization 头。模型 id 要用 /v1/models 真的列出来的那些。
文档
这份开发者文档是 MyST Markdown,由 Sphinx 从 docs/developer/ 构建。本地构建方式以及为其把关的检查,见贡献指南。
故障排查
required variable DB_USER is missing a value: DB_USER must be set in .env file——Compose 在变量插值阶段就停了。在 .env 里填上 DB_USER 和 DB_PASSWORD,见上面的表格。
env file ... .env not found——backend 服务读的是相对 deploy/docker/ 的 ../../.env,也就是仓库根目录的 .env。在 make up 之前先 cp .env.example .env。
从源码运行时报导入错误——请在仓库根目录、带上 PYTHONPATH=apps/backend 运行,并确认虚拟环境已激活(或改用 uv run)。
所有请求都 404,且 /v1/models 为空——说明注册表什么都没加载进来。启动时 backend 要么打印 Registered N routes from <path>,要么打印一条指出它找不到的注册表路径的错误;把那个路径和上面的优先级列表对一下。
端口已被占用——在 .env 里覆盖 BACKEND_PORT、FRONTEND_PORT 或 DB_PORT。