docs: merge dev tasks and doc requirements into unified agent brief
Single English document containing: - Full 5-phase task breakdown (60+ tasks) - Provider interface design and priority matrix - API endpoint specifications - Documentation requirements per task - Testing requirements with examples - Agent prompt instructions and commit rules - Build and verification checklist Signed-off-by: Boos4721 <boos4721@icloud.com>
This commit is contained in:
@@ -1,232 +0,0 @@
|
|||||||
# Go 后端重构 — 开发任务书
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
将 BoosAPI 后端从 Node.js (Fastify/TypeScript) 重构为 Go。前端 (React SPA) 保持不变。
|
|
||||||
|
|
||||||
## 规模
|
|
||||||
|
|
||||||
- 后端源码文件:约 350+ .ts 文件(不含测试)
|
|
||||||
- API 端点:约 80+
|
|
||||||
- Provider 适配器:15+ 个平台
|
|
||||||
- Transformer 转换器:4 个协议(OpenAI、Anthropic、Gemini、Canonical)
|
|
||||||
|
|
||||||
## 开发原则
|
|
||||||
|
|
||||||
1. **API 兼容** — 所有端点 path/request/response 一致,前端零改动
|
|
||||||
2. **按协议推进** — 先 OpenAI(最常用)→ Anthropic → Gemini → Canonical
|
|
||||||
3. **测试先行** — 每个 handler 有 httptest
|
|
||||||
4. **增量替换** — Go binary 与 Node.js 可并存,逐步切流
|
|
||||||
5. **DB schema 兼容** — 复用现有 SQLite/MySQL/Postgres 表结构
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 1:脚手架 ✅ 已完成
|
|
||||||
|
|
||||||
| 任务 | 状态 | 文件 |
|
|
||||||
|------|------|------|
|
|
||||||
| go mod init | ✅ | `go.mod` |
|
|
||||||
| 目录结构 | ✅ | `cmd/`, `internal/`, `pkg/` |
|
|
||||||
| 配置加载 | ✅ | `internal/config/config.go` — 全部环境变量 |
|
|
||||||
| 数据库连接 | ✅ | `internal/db/db.go` — SQLite/MySQL/Postgres |
|
|
||||||
| 迁移框架 | ✅ | `internal/db/migrate/000001_initial.up.sql` |
|
|
||||||
| Health Check | ✅ | `GET /healthz` — json + DB ping |
|
|
||||||
| 中间件 | ✅ | Logger、Auth(CORS)、Panic Recovery |
|
|
||||||
| chi 路由分组 | ✅ | `/admin`, `/v1`, `/api` |
|
|
||||||
| 优雅关闭 | ✅ | SIGINT/SIGTERM → graceful shutdown |
|
|
||||||
| Dockerfile | ✅ | 多阶段构建 ~20MB |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 2:Provider 核心代理(当前阶段)
|
|
||||||
|
|
||||||
### P2-A:HTTP 工具层
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 1 | SSE writer — `text/event-stream` 格式写入 | `pkg/httputil/sse.go` | — |
|
|
||||||
| 2 | HTTP 流式转发 — 将上游 stream 中继到客户端 | `pkg/httputil/stream.go` | 1 |
|
|
||||||
| 3 | HTTP 反向代理工具 — header 传递、URL 重写 | `pkg/httputil/proxy.go` | — |
|
|
||||||
| 4 | 共享 API 类型定义 | `pkg/types/api.go` | — |
|
|
||||||
|
|
||||||
### P2-B:Proxy Core 骨架
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 5 | Provider 接口定义:`Provider` interface | `internal/proxycore/provider.go` | 4 |
|
|
||||||
| 6 | 失败重试/降级策略 | `internal/proxycore/failover.go` | 5 |
|
|
||||||
| 7 | 通道选择与负载均衡 | `internal/proxycore/channel.go` | 5 |
|
|
||||||
| 8 | 核心编排器 `Orchestrator` | `internal/proxycore/orchestrator.go` | 5-7 |
|
|
||||||
| 9 | 请求构建:header 注入、URL 构建 | `internal/proxycore/request.go` | — |
|
|
||||||
|
|
||||||
### P2-C:OpenAI Provider
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 10 | Chat Completion 非流式 | `internal/provider/openai/chat.go` | 5, 9 |
|
|
||||||
| 11 | Chat Completion 流式 (SSE) | `internal/provider/openai/stream.go` | 1, 2, 10 |
|
|
||||||
| 12 | Models 列表 | `internal/provider/openai/models.go` | 5 |
|
|
||||||
| 13 | Images 生成/编辑 | `internal/provider/openai/images.go` | 5 |
|
|
||||||
| 14 | Embeddings | `internal/provider/openai/embeddings.go` | 5 |
|
|
||||||
| 15 | Audio (TTS/STT) | `internal/provider/openai/audio.go` | 5 |
|
|
||||||
| 16 | Files (上传/列表/内容) | `internal/provider/openai/files.go` | 5 |
|
|
||||||
| 17 | Responses API | `internal/provider/openai/responses.go` | 5, 9 |
|
|
||||||
|
|
||||||
### P2-D:Anthropic Provider
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 18 | Messages 非流式 | `internal/provider/anthropic/messages.go` | 5, 9 |
|
|
||||||
| 19 | Messages 流式 | `internal/provider/anthropic/stream.go` | 1, 2, 18 |
|
|
||||||
| 20 | 请求转换(OpenAI → Anthropic 格式) | `internal/transformer/anthropic/inbound.go` | — |
|
|
||||||
| 21 | 响应转换(Anthropic → OpenAI 格式) | `internal/transformer/anthropic/outbound.go` | — |
|
|
||||||
|
|
||||||
### P2-E:Gemini Provider
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 22 | Generate Content 非流式 | `internal/provider/gemini/content.go` | 5, 9 |
|
|
||||||
| 23 | Generate Content 流式 | `internal/provider/gemini/stream.go` | 1, 2, 22 |
|
|
||||||
| 24 | 请求转换(OpenAI → Gemini) | `internal/transformer/gemini/inbound.go` | — |
|
|
||||||
| 25 | 响应转换(Gemini → OpenAI) | `internal/transformer/gemini/outbound.go` | — |
|
|
||||||
|
|
||||||
### P2-F:Canonical 协议(用于多 provider 统一处理)
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 26 | Canonical Request 类型定义 | `internal/transformer/canonical/request.go` | — |
|
|
||||||
| 27 | Canonical Response 类型定义 | `internal/transformer/canonical/response.go` | — |
|
|
||||||
| 28 | Canonical → Provider 转换 | `internal/transformer/canonical/conversion.go` | 26-27 |
|
|
||||||
| 29 | Provider → Canonical 转换 | `internal/transformer/canonical/normalization.go` | 26-27 |
|
|
||||||
|
|
||||||
### P2-G:API 路由注册
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 30 | `/v1/chat/completions` | `internal/server/handler/chat.go` | 10, 11, 18, 19, 22, 23 |
|
|
||||||
| 31 | `/v1/models` | `internal/server/handler/models.go` | 12 |
|
|
||||||
| 32 | `/v1/images/*` | `internal/server/handler/images.go` | 13 |
|
|
||||||
| 33 | `/v1/embeddings` | `internal/server/handler/embeddings.go` | 14 |
|
|
||||||
| 34 | `/v1/audio/*` | `internal/server/handler/audio.go` | 15 |
|
|
||||||
| 35 | `/v1/files/*` | `internal/server/handler/files.go` | 16 |
|
|
||||||
| 36 | `/v1/responses` | `internal/server/handler/responses.go` | 17 |
|
|
||||||
| 37 | 路由统一注册 | `internal/server/router.go` | 30-36 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 3:管理 API
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 38 | 管理员认证 (JWT + Bearer) | `internal/auth/jwt.go` | — |
|
|
||||||
| 39 | 用户 CRUD | `internal/server/handler/admin/users.go` | 38 |
|
|
||||||
| 40 | API Key 管理 | `internal/server/handler/admin/apikeys.go` | 38 |
|
|
||||||
| 41 | 站点管理 (上游 Provider 配置) | `internal/server/handler/admin/sites.go` | 38 |
|
|
||||||
| 42 | 账单/用量统计 | `internal/server/handler/admin/usage.go` | 38 |
|
|
||||||
| 43 | Token 路由配置 | `internal/server/handler/admin/routing.go` | 38 |
|
|
||||||
| 44 | 组织/团队管理 | `internal/server/handler/admin/orgs.go` | 38 |
|
|
||||||
| 45 | 设置页面 (数据库迁移、备份等) | `internal/server/handler/admin/settings.go` | 38 |
|
|
||||||
| 46 | 更新中心 (K3s/Helm) | `internal/server/handler/admin/update.go` | 38 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 4:增值功能
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 47 | ComfyUI 反向代理 | `internal/server/handler/comfyui.go` | — |
|
|
||||||
| 48 | Video Agent (脚本→视频 pipeline) | `internal/server/handler/video_agent.go` | — |
|
|
||||||
| 49 | ComfyUI Agent SSE 对话 | `internal/server/handler/comfyui_agent.go` | 1 |
|
|
||||||
| 50 | 文件存储 (proxyFileStore) | `internal/services/filestore.go` | — |
|
|
||||||
| 51 | 搜索服务 | `internal/server/handler/search.go` | — |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 5:通知 + 运维
|
|
||||||
|
|
||||||
| # | 任务 | 文件 | 依赖 |
|
|
||||||
|---|------|------|------|
|
|
||||||
| 52 | 通知服务 (Telegram) | `internal/notify/telegram.go` | — |
|
|
||||||
| 53 | 通知服务 (企业微信/飞书/通用 Webhook) | `internal/notify/webhook.go` | — |
|
|
||||||
| 54 | 通知服务 (Bark/Server酱/SMTP) | `internal/notify/others.go` | — |
|
|
||||||
| 55 | 通知节流 (冷静期合并) | `internal/notify/throttle.go` | 52-54 |
|
|
||||||
| 56 | 桌面版后端适配 | `internal/desktop/adapter.go` | — |
|
|
||||||
| 57 | 桌面版 runtime 状态 API | `internal/server/handler/desktop.go` | 56 |
|
|
||||||
| 58 | OAuth Provider (Claude/Codex/Gemini) | `internal/oauth/provider.go` | — |
|
|
||||||
| 59 | OAuth 会话存储 | `internal/oauth/session.go` | 38 |
|
|
||||||
| 60 | 后台定时任务 (签到、模型探测、日志清理) | `internal/cron/scheduler.go` | — |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## API 兼容性要求
|
|
||||||
|
|
||||||
所有 Go handler 必须保持与现有 Node.js 相同的:
|
|
||||||
|
|
||||||
### 请求
|
|
||||||
- URL 路径
|
|
||||||
- HTTP Method
|
|
||||||
- Query 参数
|
|
||||||
- Request Body JSON 结构
|
|
||||||
- Header 名称
|
|
||||||
|
|
||||||
### 响应
|
|
||||||
- HTTP Status Code
|
|
||||||
- Response Body JSON 结构
|
|
||||||
- SSE 事件格式 (`data: {...}\n\n`)
|
|
||||||
- Error 格式 (`{"error": {...}}`)
|
|
||||||
- 速率限制 Header (`X-RateLimit-*`)
|
|
||||||
|
|
||||||
### 验证方法
|
|
||||||
```
|
|
||||||
curl -s http://localhost:4000/v1/models \
|
|
||||||
-H "Authorization: Bearer $PROXY_TOKEN" | jq .
|
|
||||||
# 返回格式必须与 Node.js 版一致
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Provider 优先级矩阵
|
|
||||||
|
|
||||||
| Provider | 优先级 | 使用量 | 复杂度 | 阶段 |
|
|
||||||
|----------|--------|--------|--------|------|
|
|
||||||
| OpenAI | P0 | 🔴🔴🔴 | 中 | Phase 2 |
|
|
||||||
| Anthropic | P0 | 🔴🔴🔴 | 高 (转换器) | Phase 2 |
|
|
||||||
| Google Gemini | P1 | 🟡🟡 | 高 (转换器) | Phase 2 |
|
|
||||||
| OneAPI/NewAPI | P1 | 🟡🟡 | 低 | Phase 3 |
|
|
||||||
| Claude (自有平台) | P1 | 🟡🟡 | 中 | Phase 3 |
|
|
||||||
| Codex | P2 | 🟢 | 中 | Phase 4 |
|
|
||||||
| VoAPI/RIX/Super | P2 | 🟢 | 低 | Phase 4 |
|
|
||||||
| Veloera/Sub2/DoneHub | P2 | 🟢 | 低 | Phase 4 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 测试策略
|
|
||||||
|
|
||||||
| 层级 | 工具 | 覆盖 |
|
|
||||||
|------|------|------|
|
|
||||||
| 单元测试 | `testing` | Provider 转换逻辑、工具函数 |
|
|
||||||
| Handler 测试 | `httptest` | 每个 API 端点 |
|
|
||||||
| 集成测试 | 自建 test suite | 完整请求→响应链路 |
|
|
||||||
| Provider 测试 | mock HTTP server | 模拟上游返回,验证转发正确性 |
|
|
||||||
| 兼容性测试 | 对照 Node.js 版输出 | 相同请求 → 相同响应 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 构建与发布
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 开发构建
|
|
||||||
CGO_ENABLED=0 go build -o boosapi ./cmd/boosapi/
|
|
||||||
|
|
||||||
# 生产构建 (带版本号)
|
|
||||||
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(git describe --tags --always)" \
|
|
||||||
-o boosapi ./cmd/boosapi/
|
|
||||||
|
|
||||||
# Docker 构建
|
|
||||||
docker build -f docker/Dockerfile -t boosapi:latest .
|
|
||||||
|
|
||||||
# 运行
|
|
||||||
./boosapi
|
|
||||||
# 或
|
|
||||||
docker run -p 4000:4000 boosapi:latest
|
|
||||||
```
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
# Go 后端重构 — 文档需求
|
|
||||||
|
|
||||||
## 文档目标
|
|
||||||
|
|
||||||
1. **开发者上手** — 5 分钟内能本地跑起来改代码
|
|
||||||
2. **架构可理解** — 新团队能看懂模块划分和请求链路
|
|
||||||
3. **API 兼容参考** — 前端/客户端开发者知道怎么调用
|
|
||||||
4. **运维可操作** — 部署、日志、监控、问题排查
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 一、开发者文档
|
|
||||||
|
|
||||||
### 1.1 README.md 更新
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| 简介 | BoosAPI 是什么,Go 重构版 |
|
|
||||||
| 快速开始 | `go build ./cmd/boosapi` → `./boosapi` |
|
|
||||||
| 前置依赖 | Go 1.26+,SQLite 无需安装 |
|
|
||||||
| 环境变量 | 简表(AUTH_TOKEN、PROXY_TOKEN、PORT、DB_TYPE、DB_URL) |
|
|
||||||
| 示例命令 | 用 `curl` 验证 `/healthz` 和 `/v1/models` |
|
|
||||||
|
|
||||||
### 1.2 CONTRIBUTING.md
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| 分支策略 | `refactor/go-backend` → `main` |
|
|
||||||
| 编码规范 | Go 标准 `gofmt`、`go vet`、无 lint 警告 |
|
|
||||||
| 提交规范 | Conventional Commits:`feat:` `fix:` `refactor:` `docs:` `test:` |
|
|
||||||
| PR 流程 | 每个 Phase 先开 PR review |
|
|
||||||
| 测试要求 | 新 handler 必须有 httptest;改逻辑不能降低覆盖率 |
|
|
||||||
|
|
||||||
### 1.3 GoDoc 注释
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| 每个 export 函数 | 一行注释说明作用 |
|
|
||||||
| 每个 export 类型 | 说明用途和字段含义 |
|
|
||||||
| Package 注释 | `package db` — 简短说明包职责 |
|
|
||||||
| Handler 注释 | 说明对应的 API 端点和行为 |
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```go
|
|
||||||
// Package provider defines the Provider interface for upstream AI API backends.
|
|
||||||
//
|
|
||||||
// Each provider (OpenAI, Anthropic, Gemini) implements this interface to
|
|
||||||
// support chat completions, streaming, and other capabilities through
|
|
||||||
// a unified abstraction.
|
|
||||||
package provider
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、架构文档
|
|
||||||
|
|
||||||
### 2.1 架构概览
|
|
||||||
|
|
||||||
**位置:** `docs/architecture.md`
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| 整体架构图 | ASCII 或 mermaid 图:前端 → 路由 → 代理核心 → Provider |
|
|
||||||
| 请求生命周期 | 从 HTTP 入站到上游响应回来的完整路径 |
|
|
||||||
| 模块边界 | `proxycore`、`provider`、`transformer`、`handler` 的职责 |
|
|
||||||
| 关键数据流 | SSE 流式请求/响应、WebSocket 传输路径 |
|
|
||||||
|
|
||||||
### 2.2 Provider 适配器设计
|
|
||||||
|
|
||||||
**位置:** `docs/provider-architecture.md`
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| Provider 接口定义 | Go interface + 方法签名 |
|
|
||||||
| Provider 注册机制 | 如何添加新的 Provider |
|
|
||||||
| 转换器模式 | Inbound/Outbound converter 模式说明 |
|
|
||||||
| 支持列表 | 当前已支持的 Provider 和对应基准 URL |
|
|
||||||
|
|
||||||
### 2.3 数据库 Schema
|
|
||||||
|
|
||||||
**位置:** `docs/database.md`
|
|
||||||
|
|
||||||
| 需求 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| ER 图 | mermaid `erDiagram` |
|
|
||||||
| 表结构 | 每张表的字段、类型、索引、外键 |
|
|
||||||
| 迁移流程 | 新增字段/表的操作步骤 |
|
|
||||||
| 兼容性 | SQLite / MySQL / PostgreSQL 差异 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、API 文档
|
|
||||||
|
|
||||||
### 3.1 管理 API
|
|
||||||
|
|
||||||
**位置:** `docs/management-api.md`(已有,需更新)
|
|
||||||
|
|
||||||
| 端点 | 方法 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/admin/users` | GET/POST | 用户列表/创建 |
|
|
||||||
| `/admin/users/:id` | GET/PUT/DELETE | 用户详情/更新/删除 |
|
|
||||||
| `/admin/api-keys` | GET/POST | API Key 管理 |
|
|
||||||
| `/admin/sites` | GET/POST | 上游站点管理 |
|
|
||||||
| `/admin/usage` | GET | 用量统计 |
|
|
||||||
| `/admin/settings` | GET/PUT | 系统设置 |
|
|
||||||
| `/admin/update-center` | GET/POST | K3s 更新中心 |
|
|
||||||
| `/admin/backup` | POST | 手动备份 |
|
|
||||||
|
|
||||||
### 3.2 代理 API(OpenAI 兼容)
|
|
||||||
|
|
||||||
| 端点 | 方法 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/v1/chat/completions` | POST | Chat 补全(流式+非流式) |
|
|
||||||
| `/v1/models` | GET | 模型列表 |
|
|
||||||
| `/v1/images/generations` | POST | 图片生成 |
|
|
||||||
| `/v1/images/edits` | POST | 图片编辑 |
|
|
||||||
| `/v1/embeddings` | POST | 向量嵌入 |
|
|
||||||
| `/v1/audio/transcriptions` | POST | 语音转文字 |
|
|
||||||
| `/v1/audio/translations` | POST | 语音翻译 |
|
|
||||||
| `/v1/audio/speech` | POST | 文字转语音 |
|
|
||||||
| `/v1/files` | GET/POST | 文件管理 |
|
|
||||||
| `/v1/files/:id` | GET/DELETE | 文件详情 |
|
|
||||||
| `/v1/files/:id/content` | GET | 文件内容 |
|
|
||||||
| `/v1/responses` | POST | Responses API |
|
|
||||||
|
|
||||||
### 3.3 自定义 API
|
|
||||||
|
|
||||||
| 端点 | 方法 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/api/comfyui/prompt` | POST | ComfyUI 工作流执行 |
|
|
||||||
| `/api/comfyui/history` | GET | ComfyUI 历史 |
|
|
||||||
| `/api/comfyui/view` | GET | ComfyUI 图片查看 |
|
|
||||||
| `/api/comfyui/proxy/ui*` | GET | ComfyUI UI 代理 |
|
|
||||||
| `/api/video-agent/*` | POST | 视频生成 Agent |
|
|
||||||
| `/api/comfyui-agent/chat` | POST | ComfyUI 对话助手 (SSE) |
|
|
||||||
| `/api/search` | POST | 搜索 |
|
|
||||||
| `/api/events` | GET | SSE 事件流 |
|
|
||||||
| `/api/auth/*` | POST | 登录/认证 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、运维文档
|
|
||||||
|
|
||||||
### 4.1 部署文档(已有,需更新)
|
|
||||||
|
|
||||||
**位置:** `docs/deployment.md`
|
|
||||||
|
|
||||||
| 需求 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| Docker 镜像 | 从 Go 构建改为多阶段,体积变化 |
|
|
||||||
| 环境变量 | 所有 BoosAPI 环境变量(更新 BOOSAPI_*) |
|
|
||||||
| Docker Compose | 示例更新为 Go 版本镜像 |
|
|
||||||
| 健康检查 | `/healthz` 端点使用说明 |
|
|
||||||
| 资源要求 | Go binary 内存/CPU 需求 |
|
|
||||||
|
|
||||||
### 4.2 运维手册(已有,需更新)
|
|
||||||
|
|
||||||
**位置:** `docs/operations.md`
|
|
||||||
|
|
||||||
| 需求 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| 日志 | Go slog 输出格式与日志级别 |
|
|
||||||
| 监控 | 健康检查、prometheus/metrics |
|
|
||||||
| 故障排查 | 常见错误和排查步骤 |
|
|
||||||
| 备份恢复 | SQLite/MySQL/Postgres 备份命令 |
|
|
||||||
|
|
||||||
### 4.3 CLI 参数
|
|
||||||
|
|
||||||
| 参数 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `-h` / `--help` | 帮助 |
|
|
||||||
| `-version` | 版本号 |
|
|
||||||
| `-config` | 配置文件路径(可选) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、GoDoc 发布(可选)
|
|
||||||
|
|
||||||
| 需求 | 工具 |
|
|
||||||
|------|------|
|
|
||||||
| 在线 GoDoc | `pkg.go.dev/github.com/cita-777/boosapi` |
|
|
||||||
| 本地生成 | `go doc ./...` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文档模板
|
|
||||||
|
|
||||||
每个 Go package 的 README 模板:
|
|
||||||
|
|
||||||
`````markdown
|
|
||||||
# package `<name>`
|
|
||||||
|
|
||||||
`package <name>` 提供了...
|
|
||||||
|
|
||||||
## 用法
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/cita-777/boosapi/internal/<path>"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 类型
|
|
||||||
|
|
||||||
### `<TypeName>`
|
|
||||||
|
|
||||||
```go
|
|
||||||
type TypeName struct {
|
|
||||||
Field1 Type `json:"field1"`
|
|
||||||
Field2 Type `json:"field2"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 函数
|
|
||||||
|
|
||||||
### `<FuncName>`
|
|
||||||
|
|
||||||
```go
|
|
||||||
func FuncName(param Type) (ResultType, error)
|
|
||||||
```
|
|
||||||
`````
|
|
||||||
@@ -0,0 +1,746 @@
|
|||||||
|
# BoosAPI Go Backend Rewrite — Agent Development Brief
|
||||||
|
|
||||||
|
You are a senior Go developer tasked with rewriting the BoosAPI backend from Node.js/Fastify/TypeScript to Go. The frontend (React SPA) and database schema remain unchanged.
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
- **Branch:** `refactor/go-backend`
|
||||||
|
- **Remote:** `gitea` (`https://git.boos.lat/boos/BoosAPI.git`)
|
||||||
|
- **Go version:** 1.26
|
||||||
|
- **Module:** `github.com/cita-777/boosapi`
|
||||||
|
|
||||||
|
## Golden Rules
|
||||||
|
|
||||||
|
1. **API compatibility first.** Every endpoint path, HTTP method, request body, response JSON, SSE event format, and error shape must match the existing Node.js implementation exactly. Frontend must work with zero changes.
|
||||||
|
2. **Test before claim.** Every handler must have an `httptest` test. Every provider must have a mock HTTP server test. Run `go test ./...` and `go vet ./...` before committing.
|
||||||
|
3. **One phase at a time.** Complete all tasks in the current phase before moving to the next.
|
||||||
|
4. **Phase 1 is done.** Do not touch Phase 1 files unless fixing a bug.
|
||||||
|
5. **Commit convention:** `feat:` for new functionality, `fix:` for bugs, `refactor:` for restructuring, `docs:` for documentation, `test:` for tests. Always sign off: `Signed-off-by: Boos4721 <boos4721@icloud.com>`
|
||||||
|
6. **Go standard:** `gofmt`, `go vet`, no lint warnings. Use stdlib where possible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ React SPA │
|
||||||
|
│ (unchanged) │
|
||||||
|
└────────┬─────────┘
|
||||||
|
│ HTTP / SSE / WebSocket
|
||||||
|
┌────────▼─────────┐
|
||||||
|
│ Go Backend │
|
||||||
|
│ (net/http + chi) │
|
||||||
|
│ │
|
||||||
|
│ ┌───────────┐ │
|
||||||
|
│ │ chi Router │ │
|
||||||
|
│ └─────┬─────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────▼─────┐ │
|
||||||
|
│ │ Proxy │ │
|
||||||
|
│ │ Core │ │
|
||||||
|
│ └─────┬─────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────▼─────┐ │
|
||||||
|
│ │ Providers │ │
|
||||||
|
│ │ (interfce)│ │
|
||||||
|
│ └─────┬─────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────▼─────┐ │
|
||||||
|
│ │ DB Layer │ │
|
||||||
|
│ │ (sql.DB) │ │
|
||||||
|
│ └───────────┘ │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
cmd/boosapi/main.go # Entry point (Phase 1 done, do not touch)
|
||||||
|
internal/
|
||||||
|
config/config.go # Env var loader (Phase 1 done)
|
||||||
|
db/db.go # DB connection + migration runner (Phase 1 done)
|
||||||
|
db/migrate/ # SQL migration files (Phase 1 done)
|
||||||
|
server/
|
||||||
|
router.go # Route registration (extend as you add handlers)
|
||||||
|
middleware/middleware.go # Auth, CORS, Logger, Recover (Phase 1 done)
|
||||||
|
handler/ # HTTP handlers
|
||||||
|
health.go # GET /healthz (Phase 1 done)
|
||||||
|
chat.go # POST /v1/chat/completions
|
||||||
|
models.go # GET /v1/models
|
||||||
|
images.go # POST /v1/images/*
|
||||||
|
embeddings.go # POST /v1/embeddings
|
||||||
|
audio.go # POST /v1/audio/*
|
||||||
|
files.go # GET/POST /v1/files/*
|
||||||
|
responses.go # POST /v1/responses
|
||||||
|
comfyui.go # ComfyUI reverse proxy
|
||||||
|
video_agent.go # Video generation agent
|
||||||
|
comfyui_agent.go # ComfyUI conversational agent (SSE)
|
||||||
|
search.go # POST /api/search
|
||||||
|
admin/ # Admin handlers
|
||||||
|
users.go
|
||||||
|
apikeys.go
|
||||||
|
sites.go
|
||||||
|
usage.go
|
||||||
|
routing.go
|
||||||
|
orgs.go
|
||||||
|
settings.go
|
||||||
|
update.go
|
||||||
|
proxycore/
|
||||||
|
provider.go # Provider interface
|
||||||
|
orchestrator.go # Core orchestrator (route, failover, fallback)
|
||||||
|
channel.go # Channel selection + load balancing
|
||||||
|
request.go # Request building (headers, URL)
|
||||||
|
failover.go # Retry + degradation strategy
|
||||||
|
provider/
|
||||||
|
openai/
|
||||||
|
chat.go # Chat completion (non-streaming)
|
||||||
|
stream.go # Chat completion (SSE streaming)
|
||||||
|
models.go # Models list
|
||||||
|
images.go # Image generation/edits
|
||||||
|
embeddings.go # Embeddings
|
||||||
|
audio.go # TTS/STT
|
||||||
|
files.go # File upload/list/content
|
||||||
|
responses.go # Responses API
|
||||||
|
anthropic/
|
||||||
|
messages.go # Messages (non-streaming)
|
||||||
|
stream.go # Messages (SSE streaming)
|
||||||
|
gemini/
|
||||||
|
content.go # Generate content (non-streaming)
|
||||||
|
stream.go # Generate content (SSE streaming)
|
||||||
|
transformer/
|
||||||
|
canonical/
|
||||||
|
request.go # Canonical request type
|
||||||
|
response.go # Canonical response type
|
||||||
|
conversion.go # Canonical ↔ Provider conversion
|
||||||
|
anthropic/
|
||||||
|
inbound.go # OpenAI → Anthropic request conversion
|
||||||
|
outbound.go # Anthropic → OpenAI response conversion
|
||||||
|
gemini/
|
||||||
|
inbound.go # OpenAI → Gemini request conversion
|
||||||
|
outbound.go # Gemini → OpenAI response conversion
|
||||||
|
auth/
|
||||||
|
jwt.go # JWT generation + validation
|
||||||
|
session.go # Session store
|
||||||
|
notify/
|
||||||
|
telegram.go # Telegram bot notification
|
||||||
|
webhook.go # Webhook (WeChat/Feishu/generic)
|
||||||
|
others.go # Bark, ServerChan, SMTP
|
||||||
|
throttle.go # Cooldown merging
|
||||||
|
oauth/
|
||||||
|
provider.go # OAuth provider interface
|
||||||
|
session.go # OAuth session storage
|
||||||
|
codex.go # Codex OAuth
|
||||||
|
claude.go # Claude OAuth
|
||||||
|
gemini.go # Gemini OAuth
|
||||||
|
cron/
|
||||||
|
scheduler.go # Background job scheduler
|
||||||
|
desktop/
|
||||||
|
adapter.go # Electron desktop backend adapter
|
||||||
|
runtime.go # Desktop runtime status
|
||||||
|
services/
|
||||||
|
filestore.go # Proxy file storage
|
||||||
|
search.go # Search service
|
||||||
|
pkg/
|
||||||
|
httputil/
|
||||||
|
sse.go # SSE writer utilities
|
||||||
|
stream.go # HTTP streaming relay
|
||||||
|
proxy.go # HTTP reverse proxy tools
|
||||||
|
types/
|
||||||
|
api.go # Shared API types
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Provider Core Proxy
|
||||||
|
|
||||||
|
### P2-A: HTTP Utility Layer
|
||||||
|
|
||||||
|
#### Task 1: SSE Writer (`pkg/httputil/sse.go`)
|
||||||
|
Create a helper for Server-Sent Events over `http.ResponseWriter`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package httputil
|
||||||
|
|
||||||
|
// SSEWriter wraps http.ResponseWriter for SSE streaming.
|
||||||
|
type SSEWriter struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
flusher http.Flusher
|
||||||
|
done <-chan struct{} // for client disconnect detection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSSEWriter(w http.ResponseWriter) (*SSEWriter, error)
|
||||||
|
func (s *SSEWriter) WriteEvent(eventType string, data []byte) error
|
||||||
|
func (s *SSEWriter) WriteJSON(eventType string, v interface{}) error
|
||||||
|
func (s *SSEWriter) Done() <-chan struct{}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Set `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`
|
||||||
|
- Each event: `data: {json}\n\n`
|
||||||
|
- Must detect client disconnection via `r.Context().Done()`
|
||||||
|
- Must flush after each event
|
||||||
|
|
||||||
|
#### Task 2: HTTP Stream Relay (`pkg/httputil/stream.go`)
|
||||||
|
Relay an upstream SSE/streaming response to a downstream `SSEWriter`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// RelayStream copies an upstream streaming response to w. It handles
|
||||||
|
// chunked transfer from an *http.Response body and forwards data.
|
||||||
|
func RelayStream(ctx context.Context, dst *SSEWriter, src *http.Response) error
|
||||||
|
```
|
||||||
|
|
||||||
|
- Copy bytes from `src.Body` to `dst`
|
||||||
|
- Stop when context is cancelled (client disconnect)
|
||||||
|
- Close `src.Body` when done
|
||||||
|
|
||||||
|
#### Task 3: HTTP Proxy Utilities (`pkg/httputil/proxy.go`)
|
||||||
|
```go
|
||||||
|
// CopyHeaders copies headers from src to dst.
|
||||||
|
func CopyHeaders(dst, src http.Header)
|
||||||
|
|
||||||
|
// ForwardedHeaderSet builds the X-Forwarded-* headers.
|
||||||
|
func ForwardedHeaderSet(r *http.Request, host string) http.Header
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 4: Shared Types (`pkg/types/api.go`)
|
||||||
|
Define shared request/response types used across the application:
|
||||||
|
- `ChatRequest`, `ChatMessage`, `ChatChoice`
|
||||||
|
- `StreamChoice`, `Delta`
|
||||||
|
- `Model`, `ModelList`
|
||||||
|
- `ErrorResponse`, `Usage`
|
||||||
|
- `ImageRequest`, `ImageResponse`
|
||||||
|
- `EmbeddingRequest`, `EmbeddingResponse`
|
||||||
|
- `AudioRequest`, `AudioResponse`
|
||||||
|
- `FileObject`, `FileList`
|
||||||
|
- `APIResponse`, `PaginatedResponse`
|
||||||
|
- SSE event types: `TextEvent`, `ImageEvent`, `CharacterPrompts`, `SceneEvent`, `TTSEvent`, `WorkflowEvent`, `DoneEvent`
|
||||||
|
|
||||||
|
### P2-B: Proxy Core Skeleton
|
||||||
|
|
||||||
|
#### Task 5: Provider Interface (`internal/proxycore/provider.go`)
|
||||||
|
Define the core `Provider` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Provider defines the interface for upstream AI API backends.
|
||||||
|
type Provider interface {
|
||||||
|
// Name returns the provider identifier (e.g., "openai", "anthropic").
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// ChatCompletion sends a non-streaming chat completion request.
|
||||||
|
ChatCompletion(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
|
||||||
|
|
||||||
|
// ChatCompletionStream sends a streaming chat completion request.
|
||||||
|
// Returns a channel of StreamEvents.
|
||||||
|
ChatCompletionStream(ctx context.Context, req *ChatRequest) (<-chan StreamEvent, error)
|
||||||
|
|
||||||
|
// Models returns the list of available models from this provider.
|
||||||
|
Models(ctx context.Context) ([]Model, error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Start minimal. Only add methods that are actually implemented. The interface will grow as providers are added.
|
||||||
|
|
||||||
|
#### Task 6: Failover Strategy (`internal/proxycore/failover.go`)
|
||||||
|
```go
|
||||||
|
// FailoverPolicy defines how to handle upstream failures.
|
||||||
|
type FailoverPolicy struct {
|
||||||
|
MaxRetries int
|
||||||
|
RetryDelay time.Duration
|
||||||
|
FallbackOrder []string // provider names in fallback order
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldRetry determines if a request should be retried based on the error.
|
||||||
|
func ShouldRetry(err error, attempt int) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 7: Channel Selection (`internal/proxycore/channel.go`)
|
||||||
|
```go
|
||||||
|
// ChannelSelector picks the best upstream channel for a given model request.
|
||||||
|
type ChannelSelector struct {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChannelSelector) Select(ctx context.Context, model string, preferProvider string) (*ProviderChannel, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Weighted random selection based on site priority and weight
|
||||||
|
- Skip disabled/cooldown sites
|
||||||
|
- Return error if no channel is available
|
||||||
|
|
||||||
|
#### Task 8: Core Orchestrator (`internal/proxycore/orchestrator.go`)
|
||||||
|
```go
|
||||||
|
// Orchestrator manages the full lifecycle of a proxy request.
|
||||||
|
type Orchestrator struct {
|
||||||
|
selector *ChannelSelector
|
||||||
|
failover *FailoverPolicy
|
||||||
|
registry *ProviderRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrchestrator(selector *ChannelSelector, failover *FailoverPolicy, registry *ProviderRegistry) *Orchestrator
|
||||||
|
|
||||||
|
// Execute handles a proxy request: select channel, call provider, handle failover.
|
||||||
|
func (o *Orchestrator) Execute(ctx context.Context, req *ProxyRequest) (*ProxyResponse, error)
|
||||||
|
|
||||||
|
// ExecuteStream handles a streaming proxy request.
|
||||||
|
func (o *Orchestrator) ExecuteStream(ctx context.Context, req *ProxyRequest) (<-chan StreamEvent, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 9: Request Builder (`internal/proxycore/request.go`)
|
||||||
|
```go
|
||||||
|
// BuildUpstreamRequest constructs an HTTP request to the upstream provider.
|
||||||
|
// Injects headers (x-boosapi-*, authorization, user-agent, content-type).
|
||||||
|
// Handles URL construction, query parameters, body serialization.
|
||||||
|
func BuildUpstreamRequest(ctx context.Context, channel *ProviderChannel, body []byte) (*http.Request, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Inject `Content-Type: application/json`
|
||||||
|
- Set `Authorization: Bearer <channel API key>`
|
||||||
|
- Pass through `x-boosapi-*` headers from downstream
|
||||||
|
- Handle streaming vs non-streaming content type
|
||||||
|
|
||||||
|
### P2-C: OpenAI Provider
|
||||||
|
|
||||||
|
#### Task 10: Chat Completion Non-Streaming (`internal/provider/openai/chat.go`)
|
||||||
|
```go
|
||||||
|
type Provider struct {
|
||||||
|
baseURL string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) ChatCompletion(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `{baseURL}/v1/chat/completions`
|
||||||
|
- Map `ChatRequest` → OpenAI request JSON
|
||||||
|
- Parse OpenAI response JSON → `ChatResponse`
|
||||||
|
- Handle errors (4xx, 5xx, rate limits)
|
||||||
|
|
||||||
|
#### Task 11: Chat Completion Streaming (`internal/provider/openai/stream.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) ChatCompletionStream(ctx context.Context, req *ChatRequest) (<-chan StreamEvent, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `{baseURL}/v1/chat/completions` with `stream: true`
|
||||||
|
- Parse SSE `data: {...}` lines
|
||||||
|
- Each line → `StreamEvent`
|
||||||
|
- On `[DONE]` → close channel
|
||||||
|
- Use `httputil.SSEWriter` for downstream forwarding
|
||||||
|
|
||||||
|
#### Task 12: Models (`internal/provider/openai/models.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) Models(ctx context.Context) ([]Model, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- GET `{baseURL}/v1/models`
|
||||||
|
- Parse response, return model list
|
||||||
|
|
||||||
|
#### Task 13: Images (`internal/provider/openai/images.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) GenerateImage(ctx context.Context, req *ImageRequest) (*ImageResponse, error)
|
||||||
|
func (p *Provider) EditImage(ctx context.Context, req *ImageEditRequest) (*ImageResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `{baseURL}/v1/images/generations`
|
||||||
|
- POST `{baseURL}/v1/images/edits` (multipart form)
|
||||||
|
|
||||||
|
#### Task 14: Embeddings (`internal/provider/openai/embeddings.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) CreateEmbedding(ctx context.Context, req *EmbeddingRequest) (*EmbeddingResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 15: Audio (`internal/provider/openai/audio.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) CreateTranscription(ctx context.Context, req *AudioRequest) (*AudioResponse, error)
|
||||||
|
func (p *Provider) CreateTranslation(ctx context.Context, req *AudioRequest) (*AudioResponse, error)
|
||||||
|
func (p *Provider) CreateSpeech(ctx context.Context, req *TTSRequest) (*http.Response, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Transcriptions/Translations: multipart form POST
|
||||||
|
- Speech: JSON POST, return raw audio binary
|
||||||
|
|
||||||
|
#### Task 16: Files (`internal/provider/openai/files.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) ListFiles(ctx context.Context) ([]FileObject, error)
|
||||||
|
func (p *Provider) UploadFile(ctx context.Context, file *FileUpload) (*FileObject, error)
|
||||||
|
func (p *Provider) GetFile(ctx context.Context, fileID string) (*FileObject, error)
|
||||||
|
func (p *Provider) GetFileContent(ctx context.Context, fileID string) (io.ReadCloser, error)
|
||||||
|
func (p *Provider) DeleteFile(ctx context.Context, fileID string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 17: Responses API (`internal/provider/openai/responses.go`)
|
||||||
|
```go
|
||||||
|
type ResponsesProvider struct {
|
||||||
|
// wraps Provider for POST /v1/responses
|
||||||
|
}
|
||||||
|
func (p *ResponsesProvider) CreateResponse(ctx context.Context, req *ResponsesRequest) (*ResponsesResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
### P2-D: Anthropic Provider
|
||||||
|
|
||||||
|
#### Task 18: Messages Non-Streaming (`internal/provider/anthropic/messages.go`)
|
||||||
|
Implement Anthropic Messages API format.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Provider struct {
|
||||||
|
baseURL string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) Messages(ctx context.Context, req *AnthropicMessagesRequest) (*AnthropicMessagesResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `{baseURL}/v1/messages`
|
||||||
|
- Anthropic-specific request/response types
|
||||||
|
- Handle `x-api-key` header auth
|
||||||
|
|
||||||
|
#### Task 19: Messages Streaming (`internal/provider/anthropic/stream.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) MessagesStream(ctx context.Context, req *AnthropicMessagesRequest) (<-chan AnthropicStreamEvent, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST with `stream: true`
|
||||||
|
- Parse Anthropic SSE format (`event: message_start`, `event: content_block_delta`, etc.)
|
||||||
|
- Convert to unified StreamEvent
|
||||||
|
|
||||||
|
### P2-E: Gemini Provider
|
||||||
|
|
||||||
|
#### Task 22: Generate Content Non-Streaming (`internal/provider/gemini/content.go`)
|
||||||
|
Implement Google Gemini GenerateContent API.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Provider struct {
|
||||||
|
baseURL string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) GenerateContent(ctx context.Context, req *GeminiRequest) (*GeminiResponse, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `{baseURL}/v1beta/models/{model}:generateContent`
|
||||||
|
- Gemini-specific request/response types
|
||||||
|
- API key as query param
|
||||||
|
|
||||||
|
#### Task 23: Generate Content Streaming (`internal/provider/gemini/stream.go`)
|
||||||
|
```go
|
||||||
|
func (p *Provider) GenerateContentStream(ctx context.Context, req *GeminiRequest) (<-chan GeminiStreamEvent, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
### P2-F: Canonical Protocol Transformers
|
||||||
|
|
||||||
|
#### Task 26: Canonical Types (`internal/transformer/canonical/request.go`, `response.go`)
|
||||||
|
Define provider-agnostic request/response types that all providers convert to/from:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type CanonicalRequest struct {
|
||||||
|
Model string
|
||||||
|
Messages []CanonicalMessage
|
||||||
|
Stream bool
|
||||||
|
MaxTokens int
|
||||||
|
Temperature float64
|
||||||
|
// ... other common parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
type CanonicalResponse struct {
|
||||||
|
ID string
|
||||||
|
Model string
|
||||||
|
Choices []CanonicalChoice
|
||||||
|
Usage *Usage
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 27: Conversions (`internal/transformer/canonical/conversion.go`)
|
||||||
|
```go
|
||||||
|
// ToOpenAI converts a canonical request to OpenAI format.
|
||||||
|
func ToOpenAI(req *CanonicalRequest) (*OpenAIChatRequest, error)
|
||||||
|
|
||||||
|
// FromOpenAIResponse converts an OpenAI response to canonical format.
|
||||||
|
func FromOpenAIResponse(resp *OpenAIChatResponse) (*CanonicalResponse, error)
|
||||||
|
|
||||||
|
// ToAnthropic, FromAnthropicResponse, ToGemini, FromGeminiResponse...
|
||||||
|
```
|
||||||
|
|
||||||
|
### P2-G: API Route Registration
|
||||||
|
|
||||||
|
#### Task 30-37: Handlers + Router
|
||||||
|
|
||||||
|
Create handler files under `internal/server/handler/` and register routes in `internal/server/router.go`.
|
||||||
|
|
||||||
|
**Chat handler** (`handler/chat.go`):
|
||||||
|
```go
|
||||||
|
type ChatHandler struct {
|
||||||
|
orchestrator *proxycore.Orchestrator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChatHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Parse `ChatRequest` from body
|
||||||
|
- If `stream: true` → use SSE streaming path
|
||||||
|
- If `stream: false` → use non-streaming path
|
||||||
|
- Return OpenAI-compatible response format
|
||||||
|
|
||||||
|
**Router** (`server/router.go`):
|
||||||
|
```go
|
||||||
|
func RegisterRoutes(r chi.Router, deps *Dependencies) {
|
||||||
|
// /v1 routes (proxy token protected)
|
||||||
|
r.Route("/v1", func(r chi.Router) {
|
||||||
|
r.Use(middleware.ProxyToken(deps.Config.ProxyToken))
|
||||||
|
r.Post("/chat/completions", deps.ChatHandler.ServeHTTP)
|
||||||
|
r.Get("/models", deps.ModelsHandler.ServeHTTP)
|
||||||
|
r.Post("/images/generations", deps.ImagesHandler.Generate)
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
|
||||||
|
// /api routes
|
||||||
|
r.Route("/api", func(r chi.Router) {
|
||||||
|
r.Post("/comfyui-agent/chat", deps.ComfyUIAgentHandler.ServeHTTP)
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
|
||||||
|
// /admin routes (auth token protected)
|
||||||
|
r.Route("/admin", func(r chi.Router) {
|
||||||
|
r.Use(middleware.AuthToken(deps.Config.AuthToken))
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Management API
|
||||||
|
|
||||||
|
#### Task 38: JWT Auth (`internal/auth/jwt.go`)
|
||||||
|
Implement JWT token generation and validation for admin sessions.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GenerateToken(secret string, userID string, role string) (string, error)
|
||||||
|
func ValidateToken(secret string, tokenString string) (*Claims, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task 39-46: Admin Handlers
|
||||||
|
|
||||||
|
Each handler follows this pattern:
|
||||||
|
```go
|
||||||
|
type UsersHandler struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *UsersHandler) List(w http.ResponseWriter, r *http.Request) // GET /admin/users
|
||||||
|
func (h *UsersHandler) Get(w http.ResponseWriter, r *http.Request) // GET /admin/users/{id}
|
||||||
|
func (h *UsersHandler) Create(w http.ResponseWriter, r *http.Request) // POST /admin/users
|
||||||
|
func (h *UsersHandler) Update(w http.ResponseWriter, r *http.Request) // PUT /admin/users/{id}
|
||||||
|
func (h *UsersHandler) Delete(w http.ResponseWriter, r *http.Request) // DELETE /admin/users/{id}
|
||||||
|
```
|
||||||
|
|
||||||
|
Required admin endpoints:
|
||||||
|
| Route | Handler | SQL Query |
|
||||||
|
|-------|---------|-----------|
|
||||||
|
| `GET /admin/users` | `UsersHandler.List` | `SELECT * FROM users ORDER BY created_at DESC` |
|
||||||
|
| `POST /admin/users` | `UsersHandler.Create` | `INSERT INTO users ...` |
|
||||||
|
| `GET /admin/users/{id}` | `UsersHandler.Get` | `SELECT * FROM users WHERE id = ?` |
|
||||||
|
| `PUT /admin/users/{id}` | `UsersHandler.Update` | `UPDATE users SET ... WHERE id = ?` |
|
||||||
|
| `DELETE /admin/users/{id}` | `UsersHandler.Delete` | `DELETE FROM users WHERE id = ?` |
|
||||||
|
| `GET /admin/api-keys` | `APIKeysHandler.List` | `SELECT * FROM api_keys` |
|
||||||
|
| `POST /admin/api-keys` | `APIKeysHandler.Create` | `INSERT INTO api_keys ...` |
|
||||||
|
| `GET /admin/sites` | `SitesHandler.List` | `SELECT * FROM sites ORDER BY priority DESC` |
|
||||||
|
| `POST /admin/sites` | `SitesHandler.Create` | `INSERT INTO sites ...` |
|
||||||
|
| `PUT /admin/sites/{id}` | `SitesHandler.Update` | `UPDATE sites SET ... WHERE id = ?` |
|
||||||
|
| `DELETE /admin/sites/{id}` | `SitesHandler.Delete` | `DELETE FROM sites WHERE id = ?` |
|
||||||
|
| `GET /admin/usage` | `UsageHandler.Summary` | `SELECT ... FROM usage_logs GROUP BY ...` |
|
||||||
|
| `GET /admin/settings` | `SettingsHandler.Get` | Application settings |
|
||||||
|
| `PUT /admin/settings` | `SettingsHandler.Update` | Application settings |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Value-Added Features
|
||||||
|
|
||||||
|
#### Task 47: ComfyUI Proxy (`internal/server/handler/comfyui.go`)
|
||||||
|
Reverse proxy for ComfyUI (port 8188). Key endpoints:
|
||||||
|
- `POST /api/comfyui/prompt` → `POST /prompt`
|
||||||
|
- `GET /api/comfyui/history` → `GET /history`
|
||||||
|
- `GET /api/comfyui/view` → `GET /view`
|
||||||
|
- `GET /api/comfyui/proxy/ui*` → `GET /*` (with HTML rewriting)
|
||||||
|
|
||||||
|
#### Task 48: Video Agent (`internal/server/handler/video_agent.go`)
|
||||||
|
One-shot script → video pipeline. Uses the gateway's own `/v1/chat/completions` and `/v1/images/generations` and `/v1/audio/speech`.
|
||||||
|
|
||||||
|
Endpoints:
|
||||||
|
- `POST /api/video-agent/analyze`
|
||||||
|
- `POST /api/video-agent/character-prompts`
|
||||||
|
- `POST /api/video-agent/generate-character`
|
||||||
|
- `POST /api/video-agent/scene-prompt`
|
||||||
|
- `POST /api/video-agent/generate-tts`
|
||||||
|
- `POST /api/video-agent/full-workflow`
|
||||||
|
|
||||||
|
#### Task 49: ComfyUI Agent SSE Chat (`internal/server/handler/comfyui_agent.go`)
|
||||||
|
Conversational agent using SSE streaming. The LLM generates structured SSE events directly.
|
||||||
|
|
||||||
|
- `POST /api/comfyui-agent/chat` — receives user message, streams agent response
|
||||||
|
- System prompt instructs LLM to emit SSE-formatted output
|
||||||
|
- Event types: `text`, `character_prompts`, `image`, `scene`, `tts`, `comfyui_workflow`, `done`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Notifications + Operations
|
||||||
|
|
||||||
|
#### Task 52-55: Notification Service (`internal/notify/`)
|
||||||
|
Multi-channel notification dispatch with cooldown merging.
|
||||||
|
|
||||||
|
| Channel | Config key | Delivery |
|
||||||
|
|---------|-----------|----------|
|
||||||
|
| Telegram | `telegramEnabled`, `telegramBotToken`, `telegramChatId` | Bot API |
|
||||||
|
| WeChat Work | `wecom*` | Webhook |
|
||||||
|
| Feishu | `feishu*` | Webhook |
|
||||||
|
| Generic Webhook | `webhookUrl` | JSON POST |
|
||||||
|
| Bark | `bark*` | HTTP (iOS) |
|
||||||
|
| ServerChan | `serverchan*` | HTTP |
|
||||||
|
| SMTP | `smtp*` | Email |
|
||||||
|
|
||||||
|
#### Task 58-59: OAuth (`internal/oauth/`)
|
||||||
|
Provider OAuth flows for Claude, Codex, Gemini:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// OAuthProvider handles the OAuth flow for an AI platform.
|
||||||
|
type OAuthProvider interface {
|
||||||
|
AuthURL(state string) string
|
||||||
|
ExchangeCode(ctx context.Context, code string) (*OAuthToken, error)
|
||||||
|
RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation Requirements
|
||||||
|
|
||||||
|
Each task must include appropriate documentation:
|
||||||
|
|
||||||
|
### Code-Level Documentation
|
||||||
|
- Every exported Go function must have a doc comment
|
||||||
|
- Every exported type must have a doc comment
|
||||||
|
- Every package must have a `package` doc comment
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```go
|
||||||
|
// Package provider defines the Provider interface for upstream AI API backends.
|
||||||
|
//
|
||||||
|
// Each provider (OpenAI, Anthropic, Gemini) implements this interface to
|
||||||
|
// support chat completions, streaming, and other capabilities through
|
||||||
|
// a unified abstraction.
|
||||||
|
package provider
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Documentation
|
||||||
|
When adding a new handler, document the endpoint in the handler file:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// POST /v1/chat/completions
|
||||||
|
//
|
||||||
|
// Creates a model response for the given chat conversation.
|
||||||
|
// Supports both streaming (SSE) and non-streaming responses.
|
||||||
|
//
|
||||||
|
// Request: ChatRequest
|
||||||
|
// Response (non-streaming): ChatResponse
|
||||||
|
// Response (streaming): SSE events of StreamChoice
|
||||||
|
//
|
||||||
|
// Errors:
|
||||||
|
// 400 - Invalid request body
|
||||||
|
// 401 - Missing or invalid API key
|
||||||
|
// 429 - Rate limited
|
||||||
|
// 500 - Upstream error
|
||||||
|
```
|
||||||
|
|
||||||
|
### User-Facing Documentation
|
||||||
|
Update the relevant doc files under `docs/` when adding significant functionality:
|
||||||
|
- `docs/deployment.md` — Environment variables, Docker setup
|
||||||
|
- `docs/operations.md` — Log format, health checks, troubleshooting
|
||||||
|
- `docs/management-api.md` — Admin API reference
|
||||||
|
- `docs/architecture.md` — Architecture overview (create if missing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
Every handler must have a test:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestChatHandler_NonStreaming(t *testing.T) {
|
||||||
|
// Setup mock server for upstream
|
||||||
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(openai.ChatResponse{...})
|
||||||
|
}))
|
||||||
|
defer upstream.Close()
|
||||||
|
|
||||||
|
// Setup handler with mock upstream URL
|
||||||
|
handler := NewChatHandler(&Config{UpstreamBaseURL: upstream.URL})
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
body := `{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}`
|
||||||
|
req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
|
||||||
|
req.Header.Set("Authorization", "Bearer test-token")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Serve
|
||||||
|
handler.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, http.StatusOK, rec.Code)
|
||||||
|
var resp ChatResponse
|
||||||
|
json.Unmarshal(rec.Body.Bytes(), &resp)
|
||||||
|
assert.Equal(t, "gpt-4", resp.Model)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Coverage Targets
|
||||||
|
- Provider packages: 80%+ (mock HTTP server)
|
||||||
|
- Handler packages: 80%+ (httptest)
|
||||||
|
- Transformer packages: 90%+ (pure function tests)
|
||||||
|
- Utility packages: 90%+
|
||||||
|
- Overall project: 70%+
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provider Implementation Priority
|
||||||
|
|
||||||
|
| Provider | Priority | Complexity | When |
|
||||||
|
|----------|----------|-----------|------|
|
||||||
|
| OpenAI (Chat, Models) | P0 | Medium | Phase 2-A |
|
||||||
|
| OpenAI (Images, Embeddings, Audio, Files) | P0 | Low-Medium | Phase 2-A |
|
||||||
|
| Anthropic Messages | P0 | High (different auth, streaming format) | Phase 2-B |
|
||||||
|
| Google Gemini | P1 | High (different API structure) | Phase 2-C |
|
||||||
|
| OneAPI / NewAPI | P1 | Low (OpenAI-compatible) | Phase 3 |
|
||||||
|
| Claude Platform | P1 | Medium | Phase 3 |
|
||||||
|
| Codex | P2 | Medium | Phase 4 |
|
||||||
|
| VoAPI / RIX / Super | P2 | Low | Phase 4 |
|
||||||
|
| Veloera / Sub2 / DoneHub | P2 | Low | Phase 4 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
CGO_ENABLED=0 go build -o boosapi ./cmd/boosapi/
|
||||||
|
|
||||||
|
# Run
|
||||||
|
AUTH_TOKEN=admin PROXY_TOKEN=proxy ./boosapi
|
||||||
|
|
||||||
|
# Test
|
||||||
|
go test ./... -v -count=1
|
||||||
|
|
||||||
|
# Vet
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker build -f docker/Dockerfile -t boosapi:latest .
|
||||||
|
docker run -p 4000:4000 -e AUTH_TOKEN=admin -e PROXY_TOKEN=proxy boosapi:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Before Committing
|
||||||
|
|
||||||
|
1. `go vet ./...` — zero warnings
|
||||||
|
2. `go test ./...` — all tests pass
|
||||||
|
3. `gofmt -s -w .` — code is formatted
|
||||||
|
4. `git diff --cached` — review for secrets (no real IPs, tokens, passwords)
|
||||||
|
5. Commit message follows convention: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
|
||||||
|
6. Sign off: `Signed-off-by: Boos4721 <boos4721@icloud.com>`
|
||||||
Reference in New Issue
Block a user