Skip to content

cyeinfpro/Lumen

Repository files navigation

Lumen

Lumen 是一个自托管的多模态 AI 工作室:在同一个对话流里完成文本聊天、视觉问答、文生图、图生图、长会话压缩、图片分享和 Telegram 机器人生成。

后端采用 FastAPI + arq Worker,前端采用 Next.js,数据层使用 PostgreSQL 16 + Redis 7。图片二进制默认落本地文件系统,元数据与任务状态落 PostgreSQL,实时进度通过 Redis Pub/Sub + Redis Stream + SSE 推送。

核心能力

  • 统一会话流chatvision_qatext_to_imageimage_to_image 四种 intent 共用 conversations/messages 数据模型。
  • 后台任务可恢复:API 只负责写库和入队,Worker 独立执行;浏览器关窗、刷新、断线后可通过 SSE replay 和任务快照恢复状态。
  • 流式文本与工具进度:聊天走 Responses SSE,支持 reasoning delta、web search、file search、code interpreter、chat 内 image generation。
  • 高分辨率图像:支持 1K/2K/4K 预设;显式 fixed_size 校验最长边 <= 3840、16 对齐、总像素 655,360 到 8,294,400、长宽比 <= 3:1。
  • 多 Provider Pool:支持 priority、weight、启停、代理、provider 级图片并发、image-job 能力、探活、熔断、failover 和账号级配额。
  • 图片处理链路:生成图会保存原图、display2048、preview1024、thumb256;支持 blurhash、透明背景后处理、分享链接与签名图片代理。
  • 长上下文治理:200K 级输入预算、rolling summary、手动压缩、出窗图片 caption、压缩熔断和上下文健康统计。
  • 管理后台:用户白名单、邀请链接、请求事件、Provider、代理池、系统设置、Telegram、备份入口。
  • Telegram Bot:网页绑定码绑定账号,bot 内菜单式生图、prompt 优化、任务列表、重试、Redis Stream 断点续推图片。
  • 可选 image-job sidecar:把同步图片上游封装为异步任务,并提供临时图片 URL 和 reference URL。

架构总览

Browser / Telegram
   |
   | REST / upload / SSE
   v
Next.js Web ---------------> FastAPI API
   |                            |
   | same-origin /api rewrite   | PostgreSQL: users, sessions, conversations,
   |                            | messages, tasks, images, shares, settings
   |                            |
   |                            | Redis: arq queue, Pub/Sub, user event streams,
   |                            | rate limits, leases, health state
   |                            v
   |                       arq Worker
   |                            |
   |                            | Provider Pool + proxies + retries
   |                            v
   |                    OpenAI-compatible upstream
   |                    /v1/responses, /v1/images/*
   |
   +-- optional image-job sidecar for async image relay

技术栈

技术
Web Next.js 16, React 19, TypeScript, TanStack Query, Zustand, Framer Motion, lucide-react
API FastAPI, Uvicorn, Pydantic v2, SQLAlchemy async, Alembic
Worker arq, httpx, Pillow, blurhash, Prometheus metrics
Bot aiogram 3, httpx, Redis Stream listener
Core 共享 SQLAlchemy models、Pydantic schemas、尺寸校验、上下文窗口、Provider Pool 解析
Infra PostgreSQL 16, Redis 7, Docker Compose v2 全栈,Nginx;systemd 仅作兜底
Observability Prometheus metrics, Sentry, OpenTelemetry

仓库结构

.
├── apps/
│   ├── api/        # FastAPI 网关、REST/SSE 路由、Alembic 迁移、管理后台 API
│   ├── worker/     # arq Worker、上游调用、图片处理、任务重试、outbox publisher
│   ├── web/        # Next.js 前端工作台、管理后台、分享页、邀请页
│   └── tgbot/      # Telegram Bot:绑定、菜单生图、任务列表、结果推送
├── packages/
│   └── core/       # API/Worker 共享模型、schema、常量、尺寸、上下文和 provider helpers
├── image-job/      # 可选异步图片 sidecar
├── deploy/         # systemd、nginx、watchdog、image-job 部署模板
├── scripts/        # install/update/test/backup/restore/uninstall 运维脚本
├── docs/           # 架构设计、4K 支持、部署与集成说明
├── docker-compose.yml
└── pyproject.toml  # uv workspace

系统要求

生产部署:

  • macOS 或 Linux
  • Docker 24+
  • Docker Compose v2.17+(必须支持 docker compose up --wait
  • OpenSSL、curl
  • Linux 上自动准备 /opt/lumendata 目录需要 root 或 sudo 权限
  • Docker daemon 必须可启动;macOS 上首次启动 Docker Desktop 后重跑脚本

不再需要在宿主机安装 Python / uv / Node / npm —— API、Worker、Web、Bot 全部以 Docker 镜像运行。开发模式下才需要这些工具,详见下方 "开发模式" 一节。

Docker 一键安装

最简一行(GitHub raw):

curl -fsSL https://raw.githubusercontent.com/cyeinfpro/Lumen/main/scripts/install.sh | bash

这条命令会先把仓库拉到 ~/Lumen,再执行仓库内的 scripts/install.sh。如果要指定安装目录或分支:

curl -fsSL https://raw.githubusercontent.com/cyeinfpro/Lumen/main/scripts/install.sh \
  | LUMEN_INSTALL_DIR=/opt/Lumen LUMEN_BRANCH=main bash

如果已经在项目目录内,直接:

bash scripts/install.sh

或通过统一运维菜单:

bash scripts/lumenctl.sh

lumenctl.sh 会把 Lumen、image-job 和 nginx 相关操作放在同一个入口。运行后会出现交互菜单:

1) 安装 Lumen
2) 更新 Lumen
3) 卸载 Lumen
4) 安装 image-job
5) 卸载 image-job
6) 扫描 nginx 配置
7) nginx 反代优化向导
8) 查看运行状态(compose ps + 健康检查)
9) 跟随 API 日志(compose logs -f api)
10) 重启 api/worker/web(compose up -d --force-recreate)
11) 启动 api/worker/web(compose up -d --wait)
12) 停止 api/worker/web/tgbot(compose stop)
13) 执行 DB migrate(compose --profile migrate run --rm migrate)
0) 退出

也可以不进菜单,直接执行单个动作:

# Lumen 生命周期
bash scripts/lumenctl.sh install-lumen
bash scripts/lumenctl.sh update-lumen
bash scripts/lumenctl.sh uninstall-lumen
bash scripts/lumenctl.sh rollback              # 回滚到 previous release(pull 旧 tag)
bash scripts/lumenctl.sh version               # VERSION + 镜像 tag + git sha

# Compose 运维
bash scripts/lumenctl.sh status                # docker compose ps + 健康检查
bash scripts/lumenctl.sh logs api              # 跟随 api 日志
bash scripts/lumenctl.sh start                 # up -d --wait api worker web
bash scripts/lumenctl.sh stop                  # stop api worker web tgbot
bash scripts/lumenctl.sh restart               # up -d --force-recreate
bash scripts/lumenctl.sh migrate               # compose --profile migrate run --rm migrate
bash scripts/lumenctl.sh bootstrap             # 创建初始 admin(需 LUMEN_ADMIN_EMAIL/PASSWORD)
bash scripts/lumenctl.sh backup                # scripts/backup.sh
bash scripts/lumenctl.sh restore <ts>          # scripts/restore.sh <timestamp>

# image-job sidecar
bash scripts/lumenctl.sh install-image-job
bash scripts/lumenctl.sh uninstall-image-job

# nginx
bash scripts/lumenctl.sh nginx-scan
bash scripts/lumenctl.sh nginx-optimize
bash scripts/lumenctl.sh nginx-lumen
bash scripts/lumenctl.sh nginx-sub2api
bash scripts/lumenctl.sh nginx-sub2api-inner
bash scripts/lumenctl.sh nginx-sub2api-outer
bash scripts/lumenctl.sh nginx-image-job

安装脚本会执行:

  • 检查 Docker / Compose v2 / OpenSSL / curl
  • 准备 /opt/lumendata/{postgres,redis,storage,backup} 子目录并按服务分别 chown(参见 "数据目录与权限")
  • 准备 release 布局 ${LUMEN_DEPLOY_ROOT:-/opt/lumen}/{releases,shared,current}
  • 生成或合并 shared/.env,自动生成强随机密钥
  • 探测 GHCR 镜像(ghcr.io/cyeinfpro/lumen-{api,worker,web,tgbot}),未发布 latest 时回退到 main
  • docker compose pull -> 起 PostgreSQL + Redis -> migrate -> 可选 bootstrap admin -> api/worker/web (+tgbot)
  • 切换 current symlink
  • HTTP + compose 健康检查
  • 打印汇总(Web 地址、管理员账号、状态/日志命令)

安装完成后访问:

Web: http://<服务器IP>:3000
API health: http://127.0.0.1:8000/healthz

Web 默认绑定 0.0.0.0:3000。如果服务器本机 curl http://127.0.0.1:3000 正常,但外部浏览器打不开 http://<服务器IP>:3000,请检查云安全组或防火墙是否放行 TCP 3000。

更新

默认通过预构建 GHCR 镜像更新(docker compose pull -> migrate -> up):

bash scripts/lumenctl.sh update-lumen

如果服务器访问 GitHub/GHCR 需要代理,在 shared/.env 写入 LUMEN_HTTP_PROXY=http://127.0.0.1:7890LUMEN_UPDATE_PROXY_URL=...;命令行更新和管理面板「一键更新」都会自动读取。

如需追踪主分支构建,把 /opt/lumen/shared/.env 中两项改成:

LUMEN_UPDATE_CHANNEL=main
LUMEN_IMAGE_TAG=main

main 是 rolling tag;即使当前和目标 tag 名都叫 main,更新脚本也会重新 pull 最新 digest。

实际阶段:check -> backup_preflight -> fetch_release -> set_image_tag -> pull_images -> start_infra -> migrate_db -> switch -> restart_services -> health_check -> cleanup

如果需要在本机用 Dockerfile 重新构建(无 GHCR 访问,或本地有改动):

LUMEN_UPDATE_BUILD=1 bash scripts/lumenctl.sh update-lumen

回滚到上一个 release(自动还原 LUMEN_IMAGE_TAGpull 旧镜像):

bash scripts/lumenctl.sh rollback

推荐运维命令

参见 docs/docker-full-stack-cutover-plan.md §24。

docker compose ps                                          # 服务状态
docker compose logs -f api                                 # 跟随 api 日志
docker compose logs -f worker                              # 跟随 worker 日志
docker compose --profile migrate run --rm migrate          # 单独跑迁移
docker compose up -d --force-recreate api worker web       # 重启业务容器
docker compose down                                        # 停止全部容器(保留数据卷)
docker compose down -v                                     # 同时删除数据卷(仅卸载/重建时)

带项目名的完整形式(避免和其他 compose 项目混淆):

COMPOSE_PROJECT_NAME=lumen docker compose ps
COMPOSE_PROJECT_NAME=lumen docker compose logs -f api

数据目录与权限

默认所有持久化数据都落在 /opt/lumendataLUMEN_DATA_ROOT 可改)。如果 LUMEN_DATA_ROOT 是 CIFS/NAS,建议用 LUMEN_DB_ROOT 把 PostgreSQL / Redis 单独放到本机 Linux 文件系统:

${LUMEN_DB_ROOT:-/opt/lumendata}/postgres   # PostgreSQL PGDATA(容器 uid 999:999)
${LUMEN_DB_ROOT:-/opt/lumendata}/redis      # Redis dump/aof(容器 uid 999:999)
${LUMEN_DATA_ROOT:-/opt/lumendata}/storage  # 图片原图、display/preview/thumb(容器 uid 10001:10001)
${LUMEN_DATA_ROOT:-/opt/lumendata}/backup   # PG dump + Redis dump(容器 uid 10001:10001)

数据根顶层归 root,子目录按服务分别 chown,禁止整体 chown -R 10001:10001 —— 否则 PostgreSQL/Redis(uid 999)启动会失败。v1.0.48 起 PostgreSQL 镜像换到 pgvector/pgvector:pg16(Debian, uid=999),从 alpine 老镜像(uid=70)升级时 update.sh 会自动 chown 数据目录。安装脚本会自动按这张表设置;手动恢复时参考 docs/docker-full-stack-cutover-plan.md §15.2:

sudo mkdir -p /var/lib/lumen-data/{postgres,redis} /opt/lumendata/{storage,backup}
sudo chown -R 999:999 /var/lib/lumen-data/postgres
sudo chown -R 999:999 /var/lib/lumen-data/redis
sudo chown -R 10001:10001 /opt/lumendata/storage /opt/lumendata/backup
sudo chmod 700 /var/lib/lumen-data/postgres /var/lib/lumen-data/redis
sudo chmod 750 /opt/lumendata/storage /opt/lumendata/backup
sudo chown root:root /opt/lumendata
sudo chmod 755 /opt/lumendata

nginx 反代

生产推荐让 nginx 只反代 Web:3000,由 Next.js rewrites 转发 /api/*/events,避免 nginx 维护两条上游。Web 默认同时可通过宿主机公网 :3000 直连;如只允许 nginx 本机反代,可在 shared/.env 设置 WEB_BIND_HOST=127.0.0.1。nginx 反代仍可指向本机回环:

proxy_pass http://127.0.0.1:3000;

完整模板见 deploy/nginx.conf.example,关键约束:

  • client_max_body_size 60m(与前端上传上限对齐)
  • proxy_buffering off(SSE 必需)
  • proxy_request_buffering off(大图上传不缓存到磁盘)
  • proxy_read_timeout 600s 或更高(4K 图像 timeout 分层)
  • gzip off(SSE 帧不能压缩)

也可以使用统一向导:bash scripts/lumenctl.sh nginx-optimize,进入后按部署拓扑选择:

  • Lumen 反代:生成/更新 Lumen Web 反代,包含 /api//events SSE、上传大小和超时配置。
  • sub2api 单机公网反代:公网域名所在 nginx 直接代理到本机 sub2api,例如 http://127.0.0.1:8081
  • sub2api 内层反代:sub2api 所在机器也有 nginx,先把本机 127.0.0.1:8081 暴露成内网地址/端口。
  • sub2api 外层公网反代:公网域名在另一台机器上,该机器再代理到上面的内层 nginx。
  • image-job 路由注入:扫描已有 nginx 站点,备份后注入 /v1/image-jobs/v1/refs/images/temp//refs/

nginx 写入逻辑会先备份目标配置,写入后执行 nginx -t;如果校验失败会回滚。涉及 systemd、/etc/nginx/opt/image-job 的操作需要在 Linux 服务器上用有 sudo 权限的用户运行。

开发模式

开发与生产分流:开发不要起完整的生产镜像栈,建议宿主机运行 API/Worker/Web,仅 PostgreSQL + Redis 用 docker compose。

# 1. 基础设施(仅 PG + Redis)
COMPOSE_PROJECT_NAME=lumen docker compose up -d --wait postgres redis

# 2. 同步依赖
uv sync
cd apps/api && uv run alembic upgrade head
cd ../web && npm ci

# 3. API
cd apps/api
uv run uvicorn app.main:app --reload --port 8000

# 4. Worker
cd apps/worker
uv run python -m arq app.main.WorkerSettings

# 5. Web
cd apps/web
npm run dev

可选启动 Telegram Bot:

cd apps/tgbot
uv run python -m app.main

手动创建管理员(在宿主机):

cd apps/api
uv run python -m app.scripts.bootstrap admin@example.com --role admin --password 'change-me-strong'

或在容器栈里:

LUMEN_ADMIN_EMAIL=admin@example.com LUMEN_ADMIN_PASSWORD='change-me-strong' \
  bash scripts/lumenctl.sh bootstrap

开发模式需要的额外宿主机依赖:uv、Python 3.12(uv 管理)、Node.js >= 20、npm。生产部署不需要这些。

环境变量

根目录 .env 是 API、Worker、Bot 的主要配置来源;Next.js 也会读取其中的服务端变量,同时可使用 apps/web/.env.local 写前端公开变量。

数据库与缓存

变量 说明
DB_USER / DB_PASSWORD / DB_NAME Docker Compose PostgreSQL 初始化变量
DATABASE_URL async SQLAlchemy 连接串,例如 postgresql+asyncpg://...
REDIS_PASSWORD Docker Compose Redis 密码
REDIS_URL Redis DSN,生产不要写入日志

Provider Pool

PROVIDERS 是上游配置唯一入口。最小格式:

PROVIDERS='[{"name":"default","base_url":"https://api.example.com","api_key":"sk-xxx","priority":0,"weight":1,"enabled":true}]'

带代理的新格式:

{
  "proxies": [
    {
      "name": "s5-us",
      "type": "socks5",
      "host": "127.0.0.1",
      "port": 1080
    }
  ],
  "providers": [
    {
      "name": "default",
      "base_url": "https://api.example.com",
      "api_key": "sk-xxx",
      "proxy": "s5-us",
      "priority": 0,
      "weight": 1,
      "enabled": true,
      "image_concurrency": 1,
      "image_jobs_enabled": false
    }
  ]
}

Provider 关键字段:

  • priority:数值越高越优先。
  • weight:同优先级内加权轮询。
  • proxy:引用 proxies[] 中的名称。
  • image_rate_limit / image_daily_quota:账号级图片限流与日配额。
  • image_jobs_enabled:该 provider 是否可走 image-job sidecar。
  • image_jobs_endpointautogenerationsresponses
  • image_jobs_endpoint_lock:锁定 provider 只服务指定 endpoint family。
  • image_jobs_base_url:该 provider 独立 sidecar 地址,空则使用全局 image.job_base_url
  • image_concurrency:单 provider 图片并发上限。

应用与安全

变量 默认 说明
APP_ENV dev 非 dev 会启用更严格 secret/cookie 校验
APP_PORT 8000 API 端口
SESSION_SECRET 必填 生产必须显式设置,长度至少 32 字符
SESSION_TTL_MIN 10080 会话有效期,5 分钟到 30 天
IMAGE_PROXY_SECRET 分享页签名图片 URL 的 HMAC key,生产建议设置 >=32 字符
STORAGE_ROOT /opt/lumendata/storage 原图和变体文件根目录
BACKUP_ROOT /opt/lumendata/backup 备份根目录
PUBLIC_BASE_URL http://localhost:8000 API/分享链接生成 fallback
CORS_ALLOW_ORIGINS http://localhost:3000 允许浏览器访问 API 的前端 origin
TRUSTED_PROXIES 允许信任 X-Forwarded-For 的代理 CIDR

生产要求:

  • SESSION_SECRET 必须改成强随机值。
  • CORS_ALLOW_ORIGINS 应设置为真实 HTTPS 前端域名。
  • 非 dev 环境 cookie 会启用 Secure,请通过 HTTPS 访问。
  • 如果启用签名图片代理,IMAGE_PROXY_SECRET 轮换会立即作废未过期签名 URL。

上游与图片运行时

这些值既可来自 env,也可在管理后台写入 system_settings,DB 优先:

设置/变量 说明
UPSTREAM_DEFAULT_MODEL 默认聊天模型,代码默认 gpt-5.5
UPSTREAM_GLOBAL_CONCURRENCY 全局上游并发上限
UPSTREAM_PIXEL_BUDGET size_mode=auto 的默认像素预算
image.generation_concurrency / IMAGE_GENERATION_CONCURRENCY Worker 图片 FIFO 队列总并发,DB 设置优先
IMAGE_CHANNEL autostream_onlyimage_jobs_only
IMAGE_ENGINE responsesimage2dual_race
IMAGE_JOB_BASE_URL image-job sidecar 根地址
CHAT_FILE_SEARCH_VECTOR_STORE_IDS 默认 file_search vector store ids,逗号分隔

Web

变量 说明
LUMEN_BACKEND_URL Next.js 服务端 rewrite 到 FastAPI 的地址,默认 http://127.0.0.1:8000
NEXT_PUBLIC_API_BASE 浏览器侧 API 地址。留空时默认同源 /api;跨域部署才建议设绝对 URL
LUMEN_UPGRADE_INSECURE_REQUESTS 生产 CSP 是否启用 upgrade-insecure-requests
LUMEN_HSTS_INCLUDE_SUBDOMAINS 是否给 HSTS 加 includeSubDomains

生产推荐让浏览器访问同源 /api,由 Next.js rewrites 转发到 API;跨子域部署时再设置 NEXT_PUBLIC_API_BASE=https://api.example.com

Telegram

变量 说明
TELEGRAM_BOT_SHARED_SECRET Bot 调 API 的共享密钥;非 dev 必须 >=32 字符
TELEGRAM_BOT_TOKEN BotFather token
TELEGRAM_BOT_USERNAME 不带 @,用于 deep link
LUMEN_API_BASE Bot 调 API 的内网地址
TELEGRAM_PROXY_URL Bot 出站代理 fallback
TELEGRAM_ALLOWED_USER_IDS 可选 Telegram user id 白名单
BOT_MODE pollingwebhook;当前入口实现 polling

主要运行机制

消息与任务

POST /conversations/{id}/messages 会在一个事务内创建:

  1. user message
  2. assistant placeholder message
  3. completion 或 generation 任务
  4. outbox event

事务提交后 API 会尽力立即入队并发布 queued 事件;如果 Redis/ARQ 暂时失败,Worker 的 outbox publisher 每 2 秒扫描未发布事件并补偿入队。

SSE

前端订阅:

GET /events?channels=user:{uid},conv:{cid},task:{task_id}

特点:

  • API 会强制校验 channel owner。
  • 每个用户有 Redis Stream:events:user:{uid}
  • 浏览器重连时用 Last-Event-ID replay 最近事件。
  • Worker 同时向 Pub/Sub 和 user stream 写事件。
  • 前端还有 5 秒一次的 in-flight 自愈轮询,避免刷新瞬间错过终态事件。

图片生成

支持三类引擎策略:

  • responses/v1/responses + image_generation tool。
  • image2:直调 /v1/images/generations/v1/images/edits
  • dual_race:responses 和 image2 并发竞速,先完成者展示,后完成者可作为 bonus 图 attach。

支持三类通道策略:

  • auto:先选 provider,再按 provider 的 image_jobs_enabled 决定 sidecar 或流式路径。
  • stream_only:强制直接走上游流式/同步路径。
  • image_jobs_only:强制走 image-job,不支持的 provider 会返回 503。

Worker 侧图片任务有:

  • 全局 FIFO 队列
  • provider 级并发锁
  • lease 续租和 stuck reconciler
  • cancel flag
  • retry + backoff
  • provider avoid set
  • moderation 多 provider 尝试上限
  • 透明背景 prompt 增强、alpha refine 和 QC
  • 原图、display、preview、thumb 多文件写入

聊天与长上下文

聊天 completion 会打包当前会话上下文:

  • 已知模型 gpt-5.4 / gpt-5.5 输入预算为 200K token,未知模型保守 fallback。
  • 保留 response reserve,按消息 token 估算做窗口裁剪。
  • 支持 rolling summary、sticky original task、summary guardrail。
  • 可选 web_search、file_search、code_interpreter、image_generation tools。
  • 长对话可自动或手动压缩,压缩失败有 circuit breaker,失败时降级截断。

图片存储与分享

  • 上传限制:请求体总上限约 60MB,单图片上传 50MB,支持 PNG/JPEG/WebP。
  • 上传图片最长边超过 4096 会被缩小。
  • 存储 key 限制在 STORAGE_ROOT 下,读取时禁止路径逃逸和 symlink。
  • 私有图片接口需要登录 owner check。
  • 分享链接支持单图或多图,可选择是否展示 prompt,可设置默认过期天数。
  • 签名图片端点额外要求图片处于有效 share 中,防止签名 key 泄漏后任意读私图。

API 概览

路径 说明
GET /healthz 进程存活,不依赖 Redis/PostgreSQL
GET /readyz Redis PING + DB SELECT 1
/auth/* signup、login、logout、me、csrf、password reset
/conversations/* 会话列表、详情、更新、删除、上下文健康、手动压缩
POST /conversations/{id}/messages 核心消息入口
/generations/{id} / /completions/{id} 任务快照、取消、重试
/tasks / /tasks/mine/active 用户任务聚合
/images/* 上传、元数据、binary、variants、签名代理
/events SSE
/generations/feed 当前用户生成图灵感流
/share/* 创建/撤销/公开分享
/invite/* 邀请链接
/admin/* 用户、白名单、请求事件、provider、代理池、设置、备份、模型、Telegram
/telegram/* Bot service-to-service 端点

Telegram Bot

启用步骤:

  1. 设置 TELEGRAM_BOT_SHARED_SECRETTELEGRAM_BOT_TOKENTELEGRAM_BOT_USERNAME
  2. 启动 API、Worker、Bot。
  3. 登录 Web,进入设置或管理后台生成 Telegram 绑定码。
  4. 在 Telegram 发送 /start <code> 完成绑定。
  5. 使用 /new 配置参数并发送 prompt。

Bot 结果推送使用 Redis Stream events:user:{uid} 和 cursor tg:bot:cursor:{uid}。Bot 重启或网络中断后,只要事件还在 stream 保留窗口内,就能继续补推。发送图片统一走 sendDocument,避免 Telegram sendPhoto 压缩 4K 原图。

管理后台可以调整 telegram.* 设置并通过 /admin/telegram/restart 发布 Redis 控制消息,让 Bot clean exit 后由 Docker restart: unless-stopped 自动拉起。

image-job Sidecar

image-job/ 是独立 FastAPI 服务,用于把同步图片上游封装为异步任务:

POST /v1/image-jobs
GET  /v1/image-jobs/{job_id}
POST /v1/refs
GET  /images/temp/...
GET  /refs/...

它会:

  • 校验并保存 job 到 SQLite
  • 后台队列调用 IMAGE_JOB_UPSTREAM_BASE_URL + endpoint
  • 从 JSON/SSE/URL/data URL 中提取最终图片
  • 保存到临时目录并返回公网 URL
  • 定期清理过期图片和 job 行
  • requeue stuck queued/running job

image-job 必须绑定一个已经运行的 sub2api/OpenAI 兼容上游。统一安装菜单会让你填写实际的 IMAGE_JOB_UPSTREAM_BASE_URL,例如本机常见默认值 http://127.0.0.1:8081,也可以是其他端口、内网地址或公网反代地址。脚本会探测你填写的地址;如果当前机器连不上该上游,会中止安装。

最小启动:

cd image-job
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
IMAGE_JOB_UPSTREAM_BASE_URL=http://127.0.0.1:8081 \
IMAGE_JOB_PUBLIC_BASE_URL=https://img.example.com \
.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8091

部署模板见:

  • deploy/image-job/image-job.service
  • deploy/image-job/image-job.example.com.conf
  • deploy/image-job/nginx-image-job.locations.conf

也可以用统一菜单自动执行:

bash scripts/lumenctl.sh install-image-job
bash scripts/lumenctl.sh nginx-optimize

nginx-optimize 的各类反代拓扑见上文“快速安装”里的统一运维脚本说明。

测试

后端测试应分进程跑,避免 apps/apiapps/worker 都叫顶层包 app 时污染 Python module cache:

bash scripts/test.sh -q

单独执行:

uv run pytest apps/worker/tests -q
uv run pytest apps/api/tests -q
uv run pytest packages/core/tests -q

前端:

cd apps/web
npm run lint
npm run type-check
npm run build

常用完整验证:

bash scripts/test.sh -q
cd apps/web && npm run lint && npm run type-check && npm run build

生产部署

默认部署方式 = Docker Compose 全栈。代码、镜像 tag、数据目录约定:

/opt/lumen          # release 布局:releases/、shared/、current
/opt/lumen/shared/.env   # 统一环境变量;release/.env 为 -> shared/.env 的 symlink
/opt/lumendata     # postgres + redis + storage + backup

详细切换 SOP 与目录权限、镜像构建、回滚方案见 docs/docker-full-stack-cutover-plan.md

  • §1.2 安装 / 更新最佳体验
  • §3.1 / §3.2 命令行体验原则
  • §15.2 数据目录权限(按服务分别 chown)
  • §17 一次性切换实施步骤
  • §18 回滚方案(应用层 / systemd 兜底 / 数据恢复)
  • §22 风险清单 / §23 完成标准 / §24 推荐运维命令

systemd 不再是默认入口;deploy/systemd/lumen-{api,worker,web,tgbot}.service 仅作为 Docker 栈不可用时的兜底(详见 deploy/README.md)。deploy/systemd/lumen-update-runner.service(默认 LUMEN_UPDATE_BUILD=0)和 deploy/systemd/lumen-backup.{service,timer} 保留并继续生效。

典型发布流程:

cd /opt/lumen/current
bash scripts/lumenctl.sh update-lumen

或直接:

bash scripts/update.sh

scripts/update.sh 会按阶段执行 pull -> migrate -> switch -> restart,失败时输出回滚命令;不再 uv sync / npm ci / systemctl restart lumen-*

运维脚本

# 更新(pull 优先;LUMEN_UPDATE_BUILD=1 改为本地构建)
bash scripts/lumenctl.sh update-lumen

# 卸载向导:docker compose down --remove-orphans,可选 down -v;数据默认保留
bash scripts/uninstall.sh

# 备份 PostgreSQL + Redis(容器内 pg_dump / SAVE,落 /opt/lumendata/backup)
bash scripts/lumenctl.sh backup

# 恢复指定 timestamp 的备份
bash scripts/lumenctl.sh restore 20260424-123000

备份默认目录:

/opt/lumendata/backup/pg/<timestamp>.pg.dump.gz
/opt/lumendata/backup/redis/<timestamp>.redis.tgz

lumen-backup.timer 默认每 4 小时备份一次,保留最近 MAX_KEEP=40 份。

可观测性

  • API:/metrics 默认启用,可通过 METRICS_ENABLED 关闭。
  • Worker:独立 Prometheus metrics server,默认 WORKER_METRICS_PORT=9100
  • 支持 Sentry:SENTRY_DSNSENTRY_ENVIRONMENTSENTRY_TRACES_SAMPLE_RATE
  • 支持 OTEL HTTP exporter:OTEL_EXPORTER_OTLP_ENDPOINTOTEL_SERVICE_NAME
  • 上游指标包括请求数、耗时、token、x-codex-primary-used-percent
  • Provider stats 会汇总 total/success/fail/success_rate/traffic_pct。

故障排查

Docker 未运行

docker info

macOS 启动 Docker Desktop;Linux 启动 Docker 服务并确认当前用户可访问 docker socket。

5432 或 6379 端口冲突

lsof -iTCP:5432 -sTCP:LISTEN -nP
lsof -iTCP:6379 -sTCP:LISTEN -nP

Linux 可用:

ss -ltnp 'sport = :5432'
ss -ltnp 'sport = :6379'

API 启动时报 migration head mismatch

cd apps/api
uv run alembic upgrade head

生产环境 schema 不在 Alembic head 会拒绝启动;开发环境只 warn。测试或离线脚本可临时设置 LUMEN_SKIP_MIGRATION_CHECK=1

上传图片 413

同时检查三层上限:

  • Nginx client_max_body_size 60m
  • Next.js experimental.proxyClientMaxBodySize = "60mb"
  • API body middleware 约 60MB,图片上传 route 单文件 50MB

前端 network_error 或 mixed content

  • 同源部署建议不要设置绝对 NEXT_PUBLIC_API_BASE,让它默认 /api
  • 跨域部署时,NEXT_PUBLIC_API_BASE 必须是浏览器可访问的 HTTPS API 地址。
  • CORS_ALLOW_ORIGINS 必须包含前端 origin。

SSE 长时间无事件或一次性收到一堆事件

检查 Nginx:

  • /events 必须 proxy_buffering off
  • gzip off
  • proxy_read_timeout 足够长
  • X-Accel-Buffering no

Worker 任务卡住

查看 Worker 日志和 Redis lease:

docker compose logs --tail=200 worker
docker compose exec redis redis-cli --no-auth-warning keys 'task:*:lease'

reconciler 每分钟会扫描 stuck generation/completion,lease 过期后自动 requeue 或标 timeout。

Provider 全部不可用

在管理后台检查:

  • Provider 是否 enabled
  • base_url 是否合法
  • api_key 是否正确
  • 代理是否可用
  • image endpoint lock 是否把当前请求过滤掉
  • 账号级 rate limit / daily quota 是否耗尽

Telegram Bot 收不到图

  • 确认 Bot 已绑定账号:Web 生成绑定码后 /start <code>
  • 检查 TELEGRAM_BOT_SHARED_SECRET 与 API 一致。
  • 检查 bot 是否能访问 Telegram API,国内服务器通常需要代理。
  • 检查 Redis Stream 是否有 events:user:*
  • 查看日志:docker compose logs --tail=200 tgbot

安全说明

  • 所有写操作使用 session cookie + CSRF double-submit。
  • SESSION_SECRETREDIS_URLPROVIDERSTELEGRAM_BOT_TOKEN 不应出现在日志或前端环境变量中。
  • Provider/API key 只应存在后端 .env 或系统设置敏感字段里。
  • API 统一错误结构为 { "error": { "code", "message", "details?" } },避免泄漏内部异常。
  • 图片文件读取做 root 限制、路径逃逸检查、regular file 检查和 symlink 防护。
  • 公共分享接口有公开限流;用户级发送/上传限流可通过 USER_RATE_LIMIT_ENABLED 控制。

文档入口

  • docs/DESIGN.md:产品、架构、数据模型和任务流设计。
  • docs/docker-full-stack-cutover-plan.md:全栈 Docker 化切换方案(镜像、Compose、迁移、回滚、运维命令)。
  • docs/4k-support-upgrade-plan.md:4K 尺寸策略和约束。
  • docs/responses-image-integration-guide.md:Responses 图片链路集成说明。
  • docs/image-gateway-test-summary.md:图片网关行为验证摘要。
  • deploy/README.md:Docker 部署、nginx 反代、systemd 兜底说明。
  • apps/api/README.mdapps/worker/README.mdimage-job/README.md:子模块说明。

License

Lumen is licensed under the PolyForm Noncommercial License 1.0.0 + Commercial License upon request. See LICENSE.

Noncommercial use is permitted under PolyForm Noncommercial License 1.0.0. Commercial use, including selling hosted Lumen services, paid integrations, resale, or using Lumen primarily for commercial advantage, requires a separate commercial license from the copyright holders.

About

Self-hosted multimodal AI workspace — chat, vision QA, text-to-image, image-to-image in one conversation

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors