{"id":49580303,"url":"https://github.com/cyeinfpro/lumen","last_synced_at":"2026-06-08T02:05:13.516Z","repository":{"id":355065280,"uuid":"1226622350","full_name":"cyeinfpro/Lumen","owner":"cyeinfpro","description":"Self-hosted multimodal AI workspace — chat, vision QA, text-to-image, image-to-image in one conversation","archived":false,"fork":false,"pushed_at":"2026-05-21T10:54:57.000Z","size":55735,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T20:25:16.816Z","etag":null,"topics":["ai","chatgpt","fastapi","image-generation","multimodal","nextjs","openai","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyeinfpro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-01T16:24:55.000Z","updated_at":"2026-05-21T10:54:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cyeinfpro/Lumen","commit_stats":null,"previous_names":["cyeinfpro/lumen"],"tags_count":94,"template":false,"template_full_name":null,"purl":"pkg:github/cyeinfpro/Lumen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyeinfpro%2FLumen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyeinfpro%2FLumen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyeinfpro%2FLumen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyeinfpro%2FLumen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyeinfpro","download_url":"https://codeload.github.com/cyeinfpro/Lumen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyeinfpro%2FLumen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33457339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-24T19:21:36.376Z","status":"online","status_checked_at":"2026-05-25T02:00:05.812Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","chatgpt","fastapi","image-generation","multimodal","nextjs","openai","self-hosted"],"created_at":"2026-05-03T19:04:15.000Z","updated_at":"2026-05-28T06:09:27.081Z","avatar_url":"https://github.com/cyeinfpro.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lumen\n\nLumen 是一个自托管的多模态 AI 工作室：在同一个对话流里完成文本聊天、视觉问答、文生图、图生图、长会话压缩、图片分享和 Telegram 机器人生成。\n\n后端采用 FastAPI + arq Worker，前端采用 Next.js，数据层使用 PostgreSQL 16 + Redis 7。图片二进制默认落本地文件系统，元数据与任务状态落 PostgreSQL，实时进度通过 Redis Pub/Sub + Redis Stream + SSE 推送。\n\n## 核心能力\n\n- **统一会话流**：`chat`、`vision_qa`、`text_to_image`、`image_to_image` 四种 intent 共用 `conversations/messages` 数据模型。\n- **后台任务可恢复**：API 只负责写库和入队，Worker 独立执行；浏览器关窗、刷新、断线后可通过 SSE replay 和任务快照恢复状态。\n- **流式文本与工具进度**：聊天走 Responses SSE，支持 reasoning delta、web search、file search、code interpreter、chat 内 image generation。\n- **高分辨率图像**：支持 1K/2K/4K 预设；显式 `fixed_size` 校验最长边 \u003c= 3840、16 对齐、总像素 655,360 到 8,294,400、长宽比 \u003c= 3:1。\n- **多 Provider Pool**：支持 priority、weight、启停、代理、provider 级图片并发、image-job 能力、探活、熔断、failover 和账号级配额。\n- **图片处理链路**：生成图会保存原图、display2048、preview1024、thumb256；支持 blurhash、透明背景后处理、分享链接与签名图片代理。\n- **长上下文治理**：200K 级输入预算、rolling summary、手动压缩、出窗图片 caption、压缩熔断和上下文健康统计。\n- **管理后台**：用户白名单、邀请链接、请求事件、Provider、代理池、系统设置、Telegram、备份入口。\n- **Telegram Bot**：网页绑定码绑定账号，bot 内菜单式生图、prompt 优化、任务列表、重试、Redis Stream 断点续推图片。\n- **可选 image-job sidecar**：把同步图片上游封装为异步任务，并提供临时图片 URL 和 reference URL。\n\n## 架构总览\n\n```text\nBrowser / Telegram\n   |\n   | REST / upload / SSE\n   v\nNext.js Web ---------------\u003e FastAPI API\n   |                            |\n   | same-origin /api rewrite   | PostgreSQL: users, sessions, conversations,\n   |                            | messages, tasks, images, shares, settings\n   |                            |\n   |                            | Redis: arq queue, Pub/Sub, user event streams,\n   |                            | rate limits, leases, health state\n   |                            v\n   |                       arq Worker\n   |                            |\n   |                            | Provider Pool + proxies + retries\n   |                            v\n   |                    OpenAI-compatible upstream\n   |                    /v1/responses, /v1/images/*\n   |\n   +-- optional image-job sidecar for async image relay\n```\n\n## 技术栈\n\n| 层 | 技术 |\n| --- | --- |\n| Web | Next.js 16, React 19, TypeScript, TanStack Query, Zustand, Framer Motion, lucide-react |\n| API | FastAPI, Uvicorn, Pydantic v2, SQLAlchemy async, Alembic |\n| Worker | arq, httpx, Pillow, blurhash, Prometheus metrics |\n| Bot | aiogram 3, httpx, Redis Stream listener |\n| Core | 共享 SQLAlchemy models、Pydantic schemas、尺寸校验、上下文窗口、Provider Pool 解析 |\n| Infra | PostgreSQL 16, Redis 7, Docker Compose v2 全栈，Nginx；systemd 仅作兜底 |\n| Observability | Prometheus metrics, Sentry, OpenTelemetry |\n\n## 仓库结构\n\n```text\n.\n├── apps/\n│   ├── api/        # FastAPI 网关、REST/SSE 路由、Alembic 迁移、管理后台 API\n│   ├── worker/     # arq Worker、上游调用、图片处理、任务重试、outbox publisher\n│   ├── web/        # Next.js 前端工作台、管理后台、分享页、邀请页\n│   └── tgbot/      # Telegram Bot：绑定、菜单生图、任务列表、结果推送\n├── packages/\n│   └── core/       # API/Worker 共享模型、schema、常量、尺寸、上下文和 provider helpers\n├── image-job/      # 可选异步图片 sidecar\n├── deploy/         # systemd、nginx、watchdog、image-job 部署模板\n├── scripts/        # install/update/test/backup/restore/uninstall 运维脚本\n├── docs/           # 架构设计、4K 支持、部署与集成说明\n├── docker-compose.yml\n└── pyproject.toml  # uv workspace\n```\n\n## 系统要求\n\n生产部署：\n\n- macOS 或 Linux\n- Docker 24+\n- Docker Compose v2.17+（必须支持 `docker compose up --wait`）\n- OpenSSL、curl\n- Linux 上自动准备 `/opt/lumendata` 目录需要 root 或 sudo 权限\n- Docker daemon 必须可启动；macOS 上首次启动 Docker Desktop 后重跑脚本\n\n不再需要在宿主机安装 Python / uv / Node / npm —— API、Worker、Web、Bot 全部以 Docker 镜像运行。开发模式下才需要这些工具，详见下方 \"开发模式\" 一节。\n\n桌面版打包说明：\n\n- Windows 安装器默认使用 NSIS `currentUser` 安装模式，不要求管理员权限；企业内统一分发如需 per-machine 安装，需要先调整 `apps/desktop/tauri.conf.json` 的 `windows.nsis.installMode` 并重新签名发布。\n- 自动更新包必须通过 `TAURI_UPDATER_PUBKEY` 与 `TAURI_SIGNING_PRIVATE_KEY` 生成签名工件；tag 发布缺少这些密钥时 CI 会直接失败。\n\n## Docker 一键安装\n\n最简一行（GitHub raw）：\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/cyeinfpro/Lumen/main/scripts/install.sh | bash\n```\n\n这条命令会先把仓库拉到 `~/Lumen`，再执行仓库内的 `scripts/install.sh`。如果要指定安装目录或分支：\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/cyeinfpro/Lumen/main/scripts/install.sh \\\n  | LUMEN_INSTALL_DIR=/opt/Lumen LUMEN_BRANCH=main bash\n```\n\n如果已经在项目目录内，直接：\n\n```bash\nbash scripts/install.sh\n```\n\n或通过统一运维菜单：\n\n```bash\nbash scripts/lumenctl.sh\n```\n\n`lumenctl.sh` 会把 Lumen、image-job 和 nginx 相关操作放在同一个入口。运行后会出现交互菜单：\n\n```text\n1) 安装 Lumen\n2) 更新 Lumen\n3) 卸载 Lumen\n4) 安装 image-job\n5) 卸载 image-job\n6) 扫描 nginx 配置\n7) nginx 反代优化向导\n8) 查看运行状态（compose ps + 健康检查）\n9) 跟随 API 日志（compose logs -f api）\n10) 重启 api/worker/web（compose up -d --force-recreate）\n11) 启动 api/worker/web（compose up -d --wait）\n12) 停止 api/worker/web/tgbot（compose stop）\n13) 执行 DB migrate（compose --profile migrate run --rm migrate）\n0) 退出\n```\n\n也可以不进菜单，直接执行单个动作：\n\n```bash\n# Lumen 生命周期\nbash scripts/lumenctl.sh install-lumen\nbash scripts/lumenctl.sh update-lumen\nbash scripts/lumenctl.sh uninstall-lumen\nbash scripts/lumenctl.sh rollback              # 回滚到 previous release（pull 旧 tag）\nbash scripts/lumenctl.sh version               # VERSION + 镜像 tag + git sha\n\n# Compose 运维\nbash scripts/lumenctl.sh status                # docker compose ps + 健康检查\nbash scripts/lumenctl.sh logs api              # 跟随 api 日志\nbash scripts/lumenctl.sh start                 # up -d --wait api worker web\nbash scripts/lumenctl.sh stop                  # stop api worker web tgbot\nbash scripts/lumenctl.sh restart               # up -d --force-recreate\nbash scripts/lumenctl.sh migrate               # compose --profile migrate run --rm migrate\nbash scripts/lumenctl.sh bootstrap             # 创建初始 admin（需 LUMEN_ADMIN_EMAIL/PASSWORD）\nbash scripts/lumenctl.sh backup                # scripts/backup.sh\nbash scripts/lumenctl.sh restore \u003cts\u003e          # scripts/restore.sh \u003ctimestamp\u003e\n\n# image-job sidecar\nbash scripts/lumenctl.sh install-image-job\nbash scripts/lumenctl.sh uninstall-image-job\n\n# nginx\nbash scripts/lumenctl.sh nginx-scan\nbash scripts/lumenctl.sh nginx-optimize\nbash scripts/lumenctl.sh nginx-lumen\nbash scripts/lumenctl.sh nginx-sub2api\nbash scripts/lumenctl.sh nginx-sub2api-inner\nbash scripts/lumenctl.sh nginx-sub2api-outer\nbash scripts/lumenctl.sh nginx-image-job\n```\n\n安装脚本会执行：\n\n- 检查 Docker / Compose v2 / OpenSSL / curl\n- 准备 `/opt/lumendata/{postgres,redis,storage,backup}` 子目录并按服务分别 chown（参见 \"数据目录与权限\"）\n- 准备 release 布局 `${LUMEN_DEPLOY_ROOT:-/opt/lumen}/{releases,shared,current}`\n- 生成或合并 `shared/.env`，自动生成强随机密钥\n- 探测 GHCR 镜像（`ghcr.io/cyeinfpro/lumen-{api,worker,web,tgbot}`），未发布 latest 时回退到 main\n- `docker compose pull` -\u003e 起 PostgreSQL + Redis -\u003e migrate -\u003e 可选 bootstrap admin -\u003e api/worker/web (+tgbot)\n- 切换 `current` symlink\n- HTTP + compose 健康检查\n- 打印汇总（Web 地址、管理员账号、状态/日志命令）\n\n安装完成后访问：\n\n```text\nWeb: http://\u003c服务器IP\u003e:3000\nAPI health: http://127.0.0.1:8000/healthz\n```\n\nWeb 默认绑定 `0.0.0.0:3000`。如果服务器本机 `curl http://127.0.0.1:3000` 正常，但外部浏览器打不开 `http://\u003c服务器IP\u003e:3000`，请检查云安全组或防火墙是否放行 TCP 3000。\n\n## 更新\n\n默认通过预构建 GHCR 镜像更新（`docker compose pull` -\u003e migrate -\u003e up）：\n\n```bash\nbash scripts/lumenctl.sh update-lumen\n```\n\n如果服务器访问 GitHub/GHCR 需要代理，在 `shared/.env` 写入 `LUMEN_HTTP_PROXY=http://127.0.0.1:7890` 或 `LUMEN_UPDATE_PROXY_URL=...`；命令行更新和管理面板「一键更新」都会自动读取。\n\n如需追踪主分支构建，把 `/opt/lumen/shared/.env` 中两项改成：\n\n```env\nLUMEN_UPDATE_CHANNEL=main\nLUMEN_IMAGE_TAG=main\n```\n\n`main` 是 rolling tag；即使当前和目标 tag 名都叫 `main`，更新脚本也会重新 `pull` 最新 digest。\n\n实际阶段：`check` -\u003e `backup_preflight` -\u003e `fetch_release` -\u003e `set_image_tag` -\u003e `pull_images` -\u003e `start_infra` -\u003e `migrate_db` -\u003e `switch` -\u003e `restart_services` -\u003e `health_check` -\u003e `cleanup`。\n\n如果需要在本机用 Dockerfile 重新构建（无 GHCR 访问，或本地有改动）：\n\n```bash\nLUMEN_UPDATE_BUILD=1 bash scripts/lumenctl.sh update-lumen\n```\n\n回滚到上一个 release（自动还原 `LUMEN_IMAGE_TAG` 并 `pull` 旧镜像）：\n\n```bash\nbash scripts/lumenctl.sh rollback\n```\n\n## 推荐运维命令\n\n参见 `docs/docker-full-stack-cutover-plan.md` §24。\n\n```bash\ndocker compose ps                                          # 服务状态\ndocker compose logs -f api                                 # 跟随 api 日志\ndocker compose logs -f worker                              # 跟随 worker 日志\ndocker compose --profile migrate run --rm migrate          # 单独跑迁移\ndocker compose up -d --force-recreate api worker web       # 重启业务容器\ndocker compose down                                        # 停止全部容器（保留数据卷）\ndocker compose down -v                                     # 同时删除数据卷（仅卸载/重建时）\n```\n\n带项目名的完整形式（避免和其他 compose 项目混淆）：\n\n```bash\nCOMPOSE_PROJECT_NAME=lumen docker compose ps\nCOMPOSE_PROJECT_NAME=lumen docker compose logs -f api\n```\n\n## 数据目录与权限\n\n默认所有持久化数据都落在 `/opt/lumendata`（`LUMEN_DATA_ROOT` 可改）。如果\n`LUMEN_DATA_ROOT` 是 CIFS/NAS，建议用 `LUMEN_DB_ROOT` 把 PostgreSQL / Redis\n单独放到本机 Linux 文件系统：\n\n```text\n${LUMEN_DB_ROOT:-/opt/lumendata}/postgres   # PostgreSQL PGDATA（容器 uid 999:999）\n${LUMEN_DB_ROOT:-/opt/lumendata}/redis      # Redis dump/aof（容器 uid 999:999）\n${LUMEN_DATA_ROOT:-/opt/lumendata}/storage  # 图片原图、display/preview/thumb（容器 uid 10001:10001）\n${LUMEN_DATA_ROOT:-/opt/lumendata}/backup   # PG dump + Redis dump（容器 uid 10001:10001）\n```\n\n数据根顶层归 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：\n\n```bash\nsudo mkdir -p /var/lib/lumen-data/{postgres,redis} /opt/lumendata/{storage,backup}\nsudo chown -R 999:999 /var/lib/lumen-data/postgres\nsudo chown -R 999:999 /var/lib/lumen-data/redis\nsudo chown -R 10001:10001 /opt/lumendata/storage /opt/lumendata/backup\nsudo chmod 700 /var/lib/lumen-data/postgres /var/lib/lumen-data/redis\nsudo chmod 750 /opt/lumendata/storage /opt/lumendata/backup\nsudo chown root:root /opt/lumendata\nsudo chmod 755 /opt/lumendata\n```\n\n## nginx 反代\n\n生产推荐让 nginx 只反代 `Web:3000`，由 Next.js rewrites 转发 `/api/*` 与 `/events`，避免 nginx 维护两条上游。Web 默认同时可通过宿主机公网 `:3000` 直连；如只允许 nginx 本机反代，可在 `shared/.env` 设置 `WEB_BIND_HOST=127.0.0.1`。nginx 反代仍可指向本机回环：\n\n```nginx\nproxy_pass http://127.0.0.1:3000;\n```\n\n完整模板见 `deploy/nginx.conf.example`，关键约束：\n\n- `client_max_body_size 60m`（与前端上传上限对齐）\n- `proxy_buffering off`（SSE 必需）\n- `proxy_request_buffering off`（大图上传不缓存到磁盘）\n- `proxy_read_timeout 600s` 或更高（4K 图像 timeout 分层）\n- `gzip off`（SSE 帧不能压缩）\n\n也可以使用统一向导：`bash scripts/lumenctl.sh nginx-optimize`，进入后按部署拓扑选择：\n\n- `Lumen 反代`：生成/更新 Lumen Web 反代，包含 `/api/`、`/events` SSE、上传大小和超时配置。\n- `sub2api 单机公网反代`：公网域名所在 nginx 直接代理到本机 sub2api，例如 `http://127.0.0.1:8081`。\n- `sub2api 内层反代`：sub2api 所在机器也有 nginx，先把本机 `127.0.0.1:8081` 暴露成内网地址/端口。\n- `sub2api 外层公网反代`：公网域名在另一台机器上，该机器再代理到上面的内层 nginx。\n- `image-job 路由注入`：扫描已有 nginx 站点，备份后注入 `/v1/image-jobs`、`/v1/refs`、`/images/temp/`、`/refs/`。\n\nnginx 写入逻辑会先备份目标配置，写入后执行 `nginx -t`；如果校验失败会回滚。涉及 systemd、`/etc/nginx`、`/opt/image-job` 的操作需要在 Linux 服务器上用有 sudo 权限的用户运行。\n\n## 开发模式\n\n开发与生产分流：开发不要起完整的生产镜像栈，建议宿主机运行 API/Worker/Web，仅 PostgreSQL + Redis 用 docker compose。\n\n```bash\n# 1. 基础设施（仅 PG + Redis）\nCOMPOSE_PROJECT_NAME=lumen docker compose up -d --wait postgres redis\n\n# 2. 同步依赖\nuv sync\ncd apps/api \u0026\u0026 uv run alembic upgrade head\ncd ../web \u0026\u0026 npm ci\n\n# 3. API\ncd apps/api\nuv run uvicorn app.main:app --reload --port 8000\n\n# 4. Worker\ncd apps/worker\nuv run python -m arq app.main.WorkerSettings\n\n# 5. Web\ncd apps/web\nnpm run dev\n```\n\n可选启动 Telegram Bot：\n\n```bash\ncd apps/tgbot\nuv run python -m app.main\n```\n\n手动创建管理员（在宿主机）：\n\n```bash\ncd apps/api\nuv run python -m app.scripts.bootstrap admin@example.com --role admin --password 'change-me-strong'\n```\n\n或在容器栈里：\n\n```bash\nLUMEN_ADMIN_EMAIL=admin@example.com LUMEN_ADMIN_PASSWORD='change-me-strong' \\\n  bash scripts/lumenctl.sh bootstrap\n```\n\n开发模式需要的额外宿主机依赖：[`uv`](https://docs.astral.sh/uv/)、Python 3.12（uv 管理）、Node.js \u003e= 20、npm。生产部署不需要这些。\n\n## 环境变量\n\n根目录 `.env` 是 API、Worker、Bot 的主要配置来源；Next.js 也会读取其中的服务端变量，同时可使用 `apps/web/.env.local` 写前端公开变量。\n\n### 数据库与缓存\n\n| 变量 | 说明 |\n| --- | --- |\n| `DB_USER` / `DB_PASSWORD` / `DB_NAME` | Docker Compose PostgreSQL 初始化变量 |\n| `DATABASE_URL` | async SQLAlchemy 连接串，例如 `postgresql+asyncpg://...` |\n| `REDIS_PASSWORD` | Docker Compose Redis 密码 |\n| `REDIS_URL` | Redis DSN，生产不要写入日志 |\n\n### Provider Pool\n\n`PROVIDERS` 是上游配置唯一入口。最小格式：\n\n```env\nPROVIDERS='[{\"name\":\"default\",\"base_url\":\"https://api.example.com\",\"api_key\":\"sk-xxx\",\"priority\":0,\"weight\":1,\"enabled\":true}]'\n```\n\n带代理的新格式：\n\n```json\n{\n  \"proxies\": [\n    {\n      \"name\": \"s5-us\",\n      \"type\": \"socks5\",\n      \"host\": \"127.0.0.1\",\n      \"port\": 1080\n    }\n  ],\n  \"providers\": [\n    {\n      \"name\": \"default\",\n      \"base_url\": \"https://api.example.com\",\n      \"api_key\": \"sk-xxx\",\n      \"proxy\": \"s5-us\",\n      \"priority\": 0,\n      \"weight\": 1,\n      \"enabled\": true,\n      \"image_concurrency\": 1,\n      \"image_jobs_enabled\": false\n    }\n  ]\n}\n```\n\nProvider 关键字段：\n\n- `priority`：数值越高越优先。\n- `weight`：同优先级内加权轮询。\n- `proxy`：引用 `proxies[]` 中的名称。\n- `image_rate_limit` / `image_daily_quota`：账号级图片限流与日配额。\n- `image_jobs_enabled`：该 provider 是否可走 image-job sidecar。\n- `image_jobs_endpoint`：`auto`、`generations` 或 `responses`。\n- `image_jobs_endpoint_lock`：锁定 provider 只服务指定 endpoint family。\n- `image_jobs_base_url`：该 provider 独立 sidecar 地址，空则使用全局 `image.job_base_url`。\n- `image_concurrency`：单 provider 图片并发上限。\n\n### 应用与安全\n\n| 变量 | 默认 | 说明 |\n| --- | --- | --- |\n| `APP_ENV` | `dev` | 非 dev 会启用更严格 secret/cookie 校验 |\n| `APP_PORT` | `8000` | API 端口 |\n| `SESSION_SECRET` | 必填 | 生产必须显式设置，长度至少 32 字符 |\n| `SESSION_TTL_MIN` | `10080` | 会话有效期，5 分钟到 30 天 |\n| `IMAGE_PROXY_SECRET` | 空 | 分享页签名图片 URL 的 HMAC key，生产建议设置 \u003e=32 字符 |\n| `STORAGE_ROOT` | `/opt/lumendata/storage` | 原图和变体文件根目录 |\n| `BACKUP_ROOT` | `/opt/lumendata/backup` | 备份根目录 |\n| `PUBLIC_BASE_URL` | `http://localhost:8000` | API/分享链接生成 fallback |\n| `CORS_ALLOW_ORIGINS` | `http://localhost:3000` | 允许浏览器访问 API 的前端 origin |\n| `TRUSTED_PROXIES` | 空 | 允许信任 `X-Forwarded-For` 的代理 CIDR |\n\n生产要求：\n\n- `SESSION_SECRET` 必须改成强随机值。\n- `CORS_ALLOW_ORIGINS` 应设置为真实 HTTPS 前端域名。\n- 非 dev 环境 cookie 会启用 `Secure`，请通过 HTTPS 访问。\n- 如果启用签名图片代理，`IMAGE_PROXY_SECRET` 轮换会立即作废未过期签名 URL。\n\n### 上游与图片运行时\n\n这些值既可来自 env，也可在管理后台写入 `system_settings`，DB 优先：\n\n| 设置/变量 | 说明 |\n| --- | --- |\n| `UPSTREAM_DEFAULT_MODEL` | 默认聊天模型，代码默认 `gpt-5.5` |\n| `UPSTREAM_GLOBAL_CONCURRENCY` | 全局上游并发上限 |\n| `UPSTREAM_PIXEL_BUDGET` | `size_mode=auto` 的默认像素预算 |\n| `image.generation_concurrency` / `IMAGE_GENERATION_CONCURRENCY` | Worker 图片 FIFO 队列总并发，DB 设置优先 |\n| `IMAGE_CHANNEL` | `auto`、`stream_only`、`image_jobs_only` |\n| `IMAGE_ENGINE` | `responses`、`image2`、`dual_race` |\n| `IMAGE_JOB_BASE_URL` | image-job sidecar 根地址 |\n| `CHAT_FILE_SEARCH_VECTOR_STORE_IDS` | 默认 file_search vector store ids，逗号分隔 |\n\n### Web\n\n| 变量 | 说明 |\n| --- | --- |\n| `LUMEN_BACKEND_URL` | Next.js 服务端 rewrite 到 FastAPI 的地址，默认 `http://127.0.0.1:8000` |\n| `NEXT_PUBLIC_API_BASE` | 浏览器侧 API 地址。留空时默认同源 `/api`；跨域部署才建议设绝对 URL |\n| `LUMEN_UPGRADE_INSECURE_REQUESTS` | 生产 CSP 是否启用 `upgrade-insecure-requests` |\n| `LUMEN_HSTS_INCLUDE_SUBDOMAINS` | 是否给 HSTS 加 `includeSubDomains` |\n\n生产推荐让浏览器访问同源 `/api`，由 Next.js rewrites 转发到 API；跨子域部署时再设置 `NEXT_PUBLIC_API_BASE=https://api.example.com`。\n\n### Telegram\n\n| 变量 | 说明 |\n| --- | --- |\n| `TELEGRAM_BOT_SHARED_SECRET` | Bot 调 API 的共享密钥；非 dev 必须 \u003e=32 字符 |\n| `TELEGRAM_BOT_TOKEN` | BotFather token |\n| `TELEGRAM_BOT_USERNAME` | 不带 `@`，用于 deep link |\n| `LUMEN_API_BASE` | Bot 调 API 的内网地址 |\n| `TELEGRAM_PROXY_URL` | Bot 出站代理 fallback |\n| `TELEGRAM_ALLOWED_USER_IDS` | 可选 Telegram user id 白名单 |\n| `BOT_MODE` | `polling` 或 `webhook`；当前入口实现 polling |\n\n## 主要运行机制\n\n### 消息与任务\n\n`POST /conversations/{id}/messages` 会在一个事务内创建：\n\n1. user message\n2. assistant placeholder message\n3. completion 或 generation 任务\n4. outbox event\n\n事务提交后 API 会尽力立即入队并发布 queued 事件；如果 Redis/ARQ 暂时失败，Worker 的 outbox publisher 每 2 秒扫描未发布事件并补偿入队。\n\n### SSE\n\n前端订阅：\n\n```text\nGET /events?channels=user:{uid},conv:{cid},task:{task_id}\n```\n\n特点：\n\n- API 会强制校验 channel owner。\n- 每个用户有 Redis Stream：`events:user:{uid}`。\n- 浏览器重连时用 `Last-Event-ID` replay 最近事件。\n- Worker 同时向 Pub/Sub 和 user stream 写事件。\n- 前端还有 5 秒一次的 in-flight 自愈轮询，避免刷新瞬间错过终态事件。\n\n### 图片生成\n\n支持三类引擎策略：\n\n- `responses`：`/v1/responses` + `image_generation` tool。\n- `image2`：直调 `/v1/images/generations` 或 `/v1/images/edits`。\n- `dual_race`：responses 和 image2 并发竞速，先完成者展示，后完成者可作为 bonus 图 attach。\n\n支持三类通道策略：\n\n- `auto`：先选 provider，再按 provider 的 `image_jobs_enabled` 决定 sidecar 或流式路径。\n- `stream_only`：强制直接走上游流式/同步路径。\n- `image_jobs_only`：强制走 image-job，不支持的 provider 会返回 503。\n\nWorker 侧图片任务有：\n\n- 全局 FIFO 队列\n- provider 级并发锁\n- lease 续租和 stuck reconciler\n- cancel flag\n- retry + backoff\n- provider avoid set\n- moderation 多 provider 尝试上限\n- 透明背景 prompt 增强、alpha refine 和 QC\n- 原图、display、preview、thumb 多文件写入\n\n### 聊天与长上下文\n\n聊天 completion 会打包当前会话上下文：\n\n- 已知模型 `gpt-5.4` / `gpt-5.5` 输入预算为 200K token，未知模型保守 fallback。\n- 保留 response reserve，按消息 token 估算做窗口裁剪。\n- 支持 rolling summary、sticky original task、summary guardrail。\n- 可选 web_search、file_search、code_interpreter、image_generation tools。\n- 长对话可自动或手动压缩，压缩失败有 circuit breaker，失败时降级截断。\n\n### 图片存储与分享\n\n- 上传限制：请求体总上限约 60MB，单图片上传 50MB，支持 PNG/JPEG/WebP。\n- 上传图片最长边超过 4096 会被缩小。\n- 存储 key 限制在 `STORAGE_ROOT` 下，读取时禁止路径逃逸和 symlink。\n- 私有图片接口需要登录 owner check。\n- 分享链接支持单图或多图，可选择是否展示 prompt，可设置默认过期天数。\n- 签名图片端点额外要求图片处于有效 share 中，防止签名 key 泄漏后任意读私图。\n\n## API 概览\n\n| 路径 | 说明 |\n| --- | --- |\n| `GET /healthz` | 进程存活，不依赖 Redis/PostgreSQL |\n| `GET /readyz` | Redis `PING` + DB `SELECT 1` |\n| `/auth/*` | signup、login、logout、me、csrf、password reset |\n| `/conversations/*` | 会话列表、详情、更新、删除、上下文健康、手动压缩 |\n| `POST /conversations/{id}/messages` | 核心消息入口 |\n| `/generations/{id}` / `/completions/{id}` | 任务快照、取消、重试 |\n| `/tasks` / `/tasks/mine/active` | 用户任务聚合 |\n| `/images/*` | 上传、元数据、binary、variants、签名代理 |\n| `/events` | SSE |\n| `/generations/feed` | 当前用户生成图灵感流 |\n| `/share/*` | 创建/撤销/公开分享 |\n| `/invite/*` | 邀请链接 |\n| `/admin/*` | 用户、白名单、请求事件、provider、代理池、设置、备份、模型、Telegram |\n| `/telegram/*` | Bot service-to-service 端点 |\n\n## Telegram Bot\n\n启用步骤：\n\n1. 设置 `TELEGRAM_BOT_SHARED_SECRET`、`TELEGRAM_BOT_TOKEN`、`TELEGRAM_BOT_USERNAME`。\n2. 启动 API、Worker、Bot。\n3. 登录 Web，进入设置或管理后台生成 Telegram 绑定码。\n4. 在 Telegram 发送 `/start \u003ccode\u003e` 完成绑定。\n5. 使用 `/new` 配置参数并发送 prompt。\n\nBot 结果推送使用 Redis Stream `events:user:{uid}` 和 cursor `tg:bot:cursor:{uid}`。Bot 重启或网络中断后，只要事件还在 stream 保留窗口内，就能继续补推。发送图片统一走 `sendDocument`，避免 Telegram `sendPhoto` 压缩 4K 原图。\n\n管理后台可以调整 `telegram.*` 设置并通过 `/admin/telegram/restart` 发布 Redis 控制消息，让 Bot clean exit 后由 Docker `restart: unless-stopped` 自动拉起。\n\n## image-job Sidecar\n\n`image-job/` 是独立 FastAPI 服务，用于把同步图片上游封装为异步任务：\n\n```text\nPOST /v1/image-jobs\nGET  /v1/image-jobs/{job_id}\nPOST /v1/refs\nGET  /images/temp/...\nGET  /refs/...\n```\n\n它会：\n\n- 校验并保存 job 到 SQLite\n- 后台队列调用 `IMAGE_JOB_UPSTREAM_BASE_URL + endpoint`\n- 从 JSON/SSE/URL/data URL 中提取最终图片\n- 保存到临时目录并返回公网 URL\n- 定期清理过期图片和 job 行\n- requeue stuck queued/running job\n\n`image-job` 必须绑定一个已经运行的 sub2api/OpenAI 兼容上游。统一安装菜单会让你填写实际的 `IMAGE_JOB_UPSTREAM_BASE_URL`，例如本机常见默认值 `http://127.0.0.1:8081`，也可以是其他端口、内网地址或公网反代地址。脚本会探测你填写的地址；如果当前机器连不上该上游，会中止安装。\n\n最小启动：\n\n```bash\ncd image-job\npython3 -m venv .venv\n.venv/bin/pip install -r requirements.txt\nIMAGE_JOB_UPSTREAM_BASE_URL=http://127.0.0.1:8081 \\\nIMAGE_JOB_PUBLIC_BASE_URL=https://img.example.com \\\n.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8091\n```\n\n部署模板见：\n\n- `deploy/image-job/image-job.service`\n- `deploy/image-job/image-job.example.com.conf`\n- `deploy/image-job/nginx-image-job.locations.conf`\n\n也可以用统一菜单自动执行：\n\n```bash\nbash scripts/lumenctl.sh install-image-job\nbash scripts/lumenctl.sh nginx-optimize\n```\n\n`nginx-optimize` 的各类反代拓扑见上文“快速安装”里的统一运维脚本说明。\n\n## 测试\n\n后端测试应分进程跑，避免 `apps/api` 和 `apps/worker` 都叫顶层包 `app` 时污染 Python module cache：\n\n```bash\nbash scripts/test.sh -q\n```\n\n单独执行：\n\n```bash\nuv run pytest apps/worker/tests -q\nuv run pytest apps/api/tests -q\nuv run pytest packages/core/tests -q\n```\n\n前端：\n\n```bash\ncd apps/web\nnpm run lint\nnpm run type-check\nnpm run build\n```\n\n常用完整验证：\n\n```bash\nbash scripts/test.sh -q\ncd apps/web \u0026\u0026 npm run lint \u0026\u0026 npm run type-check \u0026\u0026 npm run build\n```\n\n## 生产部署\n\n默认部署方式 = Docker Compose 全栈。代码、镜像 tag、数据目录约定：\n\n```text\n/opt/lumen          # release 布局：releases/、shared/、current\n/opt/lumen/shared/.env   # 统一环境变量；release/.env 为 -\u003e shared/.env 的 symlink\n/opt/lumendata     # postgres + redis + storage + backup\n```\n\n详细切换 SOP 与目录权限、镜像构建、回滚方案见 [`docs/docker-full-stack-cutover-plan.md`](docs/docker-full-stack-cutover-plan.md)：\n\n- §1.2 安装 / 更新最佳体验\n- §3.1 / §3.2 命令行体验原则\n- §15.2 数据目录权限（按服务分别 chown）\n- §17 一次性切换实施步骤\n- §18 回滚方案（应用层 / systemd 兜底 / 数据恢复）\n- §22 风险清单 / §23 完成标准 / §24 推荐运维命令\n\nsystemd 不再是默认入口；`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}` 保留并继续生效。\n\n典型发布流程：\n\n```bash\ncd /opt/lumen/current\nbash scripts/lumenctl.sh update-lumen\n```\n\n或直接：\n\n```bash\nbash scripts/update.sh\n```\n\n`scripts/update.sh` 会按阶段执行 pull -\u003e migrate -\u003e switch -\u003e restart，失败时输出回滚命令；不再 `uv sync / npm ci / systemctl restart lumen-*`。\n\n## 运维脚本\n\n```bash\n# 更新（pull 优先；LUMEN_UPDATE_BUILD=1 改为本地构建）\nbash scripts/lumenctl.sh update-lumen\n\n# 卸载向导：docker compose down --remove-orphans，可选 down -v；数据默认保留\nbash scripts/uninstall.sh\n\n# 备份 PostgreSQL + Redis（容器内 pg_dump / SAVE，落 /opt/lumendata/backup）\nbash scripts/lumenctl.sh backup\n\n# 恢复指定 timestamp 的备份\nbash scripts/lumenctl.sh restore 20260424-123000\n```\n\n备份默认目录：\n\n```text\n/opt/lumendata/backup/pg/\u003ctimestamp\u003e.pg.dump.gz\n/opt/lumendata/backup/redis/\u003ctimestamp\u003e.redis.tgz\n```\n\n`lumen-backup.timer` 默认每 4 小时备份一次，保留最近 `MAX_KEEP=40` 份。\n\n## 可观测性\n\n- API：`/metrics` 默认启用，可通过 `METRICS_ENABLED` 关闭。\n- Worker：独立 Prometheus metrics server，默认 `WORKER_METRICS_PORT=9100`。\n- 支持 Sentry：`SENTRY_DSN`、`SENTRY_ENVIRONMENT`、`SENTRY_TRACES_SAMPLE_RATE`。\n- 支持 OTEL HTTP exporter：`OTEL_EXPORTER_OTLP_ENDPOINT`、`OTEL_SERVICE_NAME`。\n- 上游指标包括请求数、耗时、token、`x-codex-primary-used-percent`。\n- Provider stats 会汇总 total/success/fail/success_rate/traffic_pct。\n\n## 故障排查\n\n**Docker 未运行**\n\n```bash\ndocker info\n```\n\nmacOS 启动 Docker Desktop；Linux 启动 Docker 服务并确认当前用户可访问 docker socket。\n\n**5432 或 6379 端口冲突**\n\n```bash\nlsof -iTCP:5432 -sTCP:LISTEN -nP\nlsof -iTCP:6379 -sTCP:LISTEN -nP\n```\n\nLinux 可用：\n\n```bash\nss -ltnp 'sport = :5432'\nss -ltnp 'sport = :6379'\n```\n\n**API 启动时报 migration head mismatch**\n\n```bash\ncd apps/api\nuv run alembic upgrade head\n```\n\n生产环境 schema 不在 Alembic head 会拒绝启动；开发环境只 warn。测试或离线脚本可临时设置 `LUMEN_SKIP_MIGRATION_CHECK=1`。\n\n**上传图片 413**\n\n同时检查三层上限：\n\n- Nginx `client_max_body_size 60m`\n- Next.js `experimental.proxyClientMaxBodySize = \"60mb\"`\n- API body middleware 约 60MB，图片上传 route 单文件 50MB\n\n**前端 network_error 或 mixed content**\n\n- 同源部署建议不要设置绝对 `NEXT_PUBLIC_API_BASE`，让它默认 `/api`。\n- 跨域部署时，`NEXT_PUBLIC_API_BASE` 必须是浏览器可访问的 HTTPS API 地址。\n- `CORS_ALLOW_ORIGINS` 必须包含前端 origin。\n\n**SSE 长时间无事件或一次性收到一堆事件**\n\n检查 Nginx：\n\n- `/events` 必须 `proxy_buffering off`\n- `gzip off`\n- `proxy_read_timeout` 足够长\n- `X-Accel-Buffering no`\n\n**Worker 任务卡住**\n\n查看 Worker 日志和 Redis lease：\n\n```bash\ndocker compose logs --tail=200 worker\ndocker compose exec redis redis-cli --no-auth-warning keys 'task:*:lease'\n```\n\nreconciler 每分钟会扫描 stuck generation/completion，lease 过期后自动 requeue 或标 timeout。\n\n**Provider 全部不可用**\n\n在管理后台检查：\n\n- Provider 是否 enabled\n- base_url 是否合法\n- api_key 是否正确\n- 代理是否可用\n- image endpoint lock 是否把当前请求过滤掉\n- 账号级 rate limit / daily quota 是否耗尽\n\n**Telegram Bot 收不到图**\n\n- 确认 Bot 已绑定账号：Web 生成绑定码后 `/start \u003ccode\u003e`。\n- 检查 `TELEGRAM_BOT_SHARED_SECRET` 与 API 一致。\n- 检查 bot 是否能访问 Telegram API，国内服务器通常需要代理。\n- 检查 Redis Stream 是否有 `events:user:*`。\n- 查看日志：`docker compose logs --tail=200 tgbot`。\n\n## 安全说明\n\n- 所有写操作使用 session cookie + CSRF double-submit。\n- `SESSION_SECRET`、`REDIS_URL`、`PROVIDERS`、`TELEGRAM_BOT_TOKEN` 不应出现在日志或前端环境变量中。\n- Provider/API key 只应存在后端 `.env` 或系统设置敏感字段里。\n- API 统一错误结构为 `{ \"error\": { \"code\", \"message\", \"details?\" } }`，避免泄漏内部异常。\n- 图片文件读取做 root 限制、路径逃逸检查、regular file 检查和 symlink 防护。\n- 公共分享接口有公开限流；用户级发送/上传限流可通过 `USER_RATE_LIMIT_ENABLED` 控制。\n\n## 文档入口\n\n- `docs/DESIGN.md`：产品、架构、数据模型和任务流设计。\n- `docs/docker-full-stack-cutover-plan.md`：全栈 Docker 化切换方案（镜像、Compose、迁移、回滚、运维命令）。\n- `docs/4k-support-upgrade-plan.md`：4K 尺寸策略和约束。\n- `docs/responses-image-integration-guide.md`：Responses 图片链路集成说明。\n- `docs/image-gateway-test-summary.md`：图片网关行为验证摘要。\n- `deploy/README.md`：Docker 部署、nginx 反代、systemd 兜底说明。\n- `apps/api/README.md`、`apps/worker/README.md`、`image-job/README.md`：子模块说明。\n\n## License\n\nLumen is licensed under the PolyForm Noncommercial License 1.0.0 + Commercial License upon request. See `LICENSE`.\n\nNoncommercial 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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyeinfpro%2Flumen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyeinfpro%2Flumen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyeinfpro%2Flumen/lists"}