chore: remove local agent instructions and test env
This commit is contained in:
@@ -1,103 +0,0 @@
|
||||
# BoosAPI Engineering Rules
|
||||
|
||||
> **AI开发**: 使用 **gpt-5.6**(`api.boos.lat/v1`)进行开发工作。
|
||||
> 本项目为纯 Rust 项目,无 Node.js 运行时依赖。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
boosapi-rs/ ← 根目录(已迁移至此)
|
||||
├── src/
|
||||
│ ├── main.rs ← 入口
|
||||
│ ├── lib.rs
|
||||
│ ├── config.rs
|
||||
│ ├── error.rs
|
||||
│ ├── routes/
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── comfyui_agent.rs ← ComfyUI Agent SSE 聊天
|
||||
│ │ ├── comfyui/ ← ComfyUI 代理路由
|
||||
│ │ ├── proxy/ ← OpenAI 兼容代理
|
||||
│ │ ├── auth/ ← 认证
|
||||
│ │ └── ...
|
||||
│ ├── proxy/ ← 代理核心(Conductor/Token Router)
|
||||
│ ├── services/ ← 后台服务
|
||||
│ ├── db/ ← 数据库
|
||||
│ └── transformers/ ← 协议转换
|
||||
├── dist/web/ ← 前端构建产物(由 Rust 静态 serve)
|
||||
├── migrations/ ← SQLite 迁移
|
||||
├── config.toml ← 运行配置
|
||||
├── Dockerfile
|
||||
└── Cargo.toml
|
||||
|
||||
## ComfyUI Agent
|
||||
|
||||
ComfyUI Agent 默认走 **本地模式**:复用本服务自身的 `/v1/*` 路由,bearer 用启动时引导出来的内置 downstream key(`name='comfyui-agent' AND user_id IS NULL`,密钥格式 `sk-comfyui-{uuid_v4_hex}`,同时镜像写入 `settings.builtin_key.comfyui_agent.secret`)。
|
||||
|
||||
行为契约:
|
||||
- **Local(默认)**:`upstream_base` = `http://{server.host}/v1`(host 是 `0.0.0.0` / `::` 时改写为 `127.0.0.1`),bearer = 内置 key;`/api/comfyui-agent/status` 回 `mode: "local"` + `keyName: "comfyui-agent"`
|
||||
- **FallbackRemote**:当 `BOOSAPI_UPSTREAM_BASE`(trim 后非空)被设置时切换到外部上游;`BOOSAPI_UPSTREAM_KEY` 缺省时仍用内置 key;status 回 `mode: "fallback_remote"` + `keyName: "external"`
|
||||
- 内置 key 受保护:admin 列表对其 key 字段打码(`••••<后4位>`),删除返 403,禁用(`enabled=false`)返 403;`builtin_key.*` settings 不在 list 中暴露,PUT 返 403
|
||||
- 启动期 `ensure_comfyui_agent_key` 是幂等的:缺密钥就生成、`enabled=0` 自动恢复并 `tracing::warn!`、settings 与 downstream_api_keys 双向同步
|
||||
|
||||
支持的结构化标记(输出由 LLM 生成、由 agent SSE 解析下发):
|
||||
- 【CHARACTER】 → 自动调用 gpt-image-2 生成四视图
|
||||
- 【SCENE】 → 自动生成场景图
|
||||
- 【TTS】 → 自动调用 TTS API
|
||||
- 【WORKFLOW】 → 输出 ComfyUI 工作流 JSON
|
||||
|
||||
## 即梦 Provider(图片 + 视频)
|
||||
|
||||
火山引擎「智能视觉 / CV」异步生成 provider,图片与视频共用一套「提交 → 轮询 → 转存 → 计费」状态机,靠 `capability → req_key` 映射区分能力。
|
||||
|
||||
- **能力映射**(`services/jimeng/types.rs` 单一事实源):
|
||||
- 图片 `JimengCapability::Image` → `req_key = jimeng_t2i_v40`,轮询 ~2s / 超时 ~120s,产物 `image_urls[]`(或 `binary_data_base64[]`),24h 有效
|
||||
- 视频 `JimengCapability::Video` → `req_key = jimeng_ti2v_v30_pro`,轮询 ~8s / 超时 ~600s,产物 `video_url`,**仅 1h 有效**
|
||||
- 新增能力(视频3.0 / OmniHuman 等)只加枚举分支,submit/poll/转存逻辑不动
|
||||
- **签名只当签名器**:用 `volc-visual-sdk` 的 `VisualClient::build_signed_request`(纯签名 V4),HTTP 走 metapi 自己的 async `state.http`,绝不调 SDK 的 blocking `request()`(依赖以 `default-features = false` 引入,不拉 `reqwest::blocking`)。Action 显式传 `CVSync2AsyncSubmitTask` / `CVSync2AsyncGetResult`(Version `2022-08-31`,走 query),业务参数走 JSON body。
|
||||
- **解析铁律**:先判外层 `code == 10000` 再读 `data.status`(错误响应 `data:null`,顺序反了会 panic);错误码可重试性见 `types::classify`(输入审核类不可重试,QPS/输出审核类可重试,内部错误码视频可重试、图片不重试)。
|
||||
- **任务模型零迁移**:复用 `background_tasks`(`task_type = jimeng-image-generation` / `jimeng-video-generation`,本地 id `jmi_*` / `jmv_*`),火山 `task_id` 存进 `result_json.volcTaskId`,进度 1=已提交 / 2=生成中 / 3=转存中 / 4=完成。
|
||||
- **产物转存**:轮询到 `done` 立即 `workspace::record_remote_asset`(视频 1h 过期硬约束,绝不透传火山临时 URL),对外只发本地 `public_id` + `/api/user/workspace/assets/{id}/file`。
|
||||
- **计费**:复用 `UsageTracker` 的 `record_comfy_task_success/failure`(`model = req_key`,单价来自 `config.jimeng.{image_cost,video_cost}`,首版默认 0)。
|
||||
- **路由**(鉴权照 `picture_agent` 的 bearer / JWT 解析):
|
||||
- `POST /api/jimeng/image/jobs`、`POST /api/jimeng/video/jobs` → 立即返回本地 task_id,后台 `tokio::spawn` 轮询
|
||||
- `GET /api/jimeng/jobs`、`GET /api/jimeng/jobs/{id}`、`POST /api/jimeng/jobs/{id}/cancel`、`GET /api/jimeng/status`
|
||||
- **配置**:`[jimeng]` 段,AK/SK 走 env(`BOOSAPI_JIMENG__ACCESS_KEY` / `__SECRET_KEY` / 可选 `__SESSION_TOKEN`),不硬编码;`enabled=false` 时路由返回 503「能力未启用」。
|
||||
- **首版范围**:图生视频只支持 `image_urls`(公网图 URL),前端上传 → 转存留二期;`frames ∈ {121(5s), 241(10s)}`,`aspect_ratio` 白名单。
|
||||
|
||||
### 即梦在「站点管理 / 连接管理」里的特殊处理(platform=`jimeng`)
|
||||
|
||||
即梦作为一个 `platform = "jimeng"` 录进现有 `sites` + `accounts` 体系(复用 CRUD / 列表 / 状态 UI),但因为它**不是 OpenAI 兼容协议**(火山 V4 HMAC 签名、异步两步生成、无 `/models` 端点),所有 OpenAI 专属路径都为它开了**干净的分支**,绝不污染其它平台:
|
||||
|
||||
- **平台判定**:`routes::admin::accounts::is_jimeng_platform(&site.platform)`(大小写不敏感)是唯一事实源;站点 `platform` 字段为自由字符串,无白名单,前端下拉新增 `jimeng` 选项即可。
|
||||
- **AK/SK 存法**:AK 落 `accounts.access_token`;SK + region/host/session_token 收进 `extra_config.jimeng.{secretKey,region,host,sessionToken}` JSON。`credentialMode` 强制 `apikey`(只在「API Key 管理」分段出现,永不进 session 分段)。**SK / sessionToken 是机密**:列表 / get / update 响应一律用 `mask_account_extra_config` 替换为 `••••`;带掩码的 `extra_config` 回写会被 `update_account` 拒绝(留空即保留原值)。
|
||||
- **验证(签名探测)**:`verify_token` 检测到 jimeng 平台时走 `verify_jimeng_token` → `JimengProvider::from_credentials(ak, sk, region, host, session).verify()`,对 GetResult 发一个哨兵 `task_id` 的最小签名请求。火山网关在业务层之前校验 V4 签名:签名通过 → 业务层报「任务不存在」(即 `Authenticated`);签名失败 → 网关返回鉴权类 `Code`(`classify_verify_response` 按 `is_auth_error_code` 判定 `AuthFailed`)。**绝不调 `/models`**,绝不触发生成。
|
||||
- **不进路由表**:`create_account` 检测 jimeng 后走 `create_jimeng_account`,单条插入、**不** `spawn_route_rebuild`;前端 `handleTokenAdd` 对 jimeng 也跳过 `checkModels` / `rebuildRoutes`。
|
||||
- **站点 probe 短路**:`build_probe_plan`(probe-now / probe-stream 共用入口)对 jimeng 直接 `BadRequest`,提示改用连接管理的「验证」做签名连通性检查(逐模型 chat 探测对它无意义)。
|
||||
- **运行健康来源**:jimeng 不走 OpenAI 代理通道(独立 `/api/jimeng/*`),`proxy_logs` 无记录,否则永远 `unknown`。改由 `derive_jimeng_runtime_health` 从最近一条 `background_tasks`(`jimeng-image/video-generation`)派生平台级健康(succeeded→healthy / failed→unhealthy / running·pending→进行中 / 无任务→「凭证验证通过后即可使用」),`account_response_value_with_jimeng` 在站点为 jimeng 时用它覆盖 proxy-log 派生值;其它平台传入的 jimeng 健康一律忽略(回归零影响,有断言测试守护)。
|
||||
- **前端表单**:`Sites.tsx` 选 jimeng 时预填 URL `https://visual.volcengineapi.com`,隐藏「API 请求地址池」与「刷新后自动探测」;`Accounts.tsx` 在 API Key 分段对 jimeng 改成 AK + SK(必填)+ region/host/session(选填),点「验证 AK/SK 签名」做签名探测,校验通过才能添加。
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
# 开发
|
||||
cargo run --release
|
||||
|
||||
# 环境变量
|
||||
BOOSAPI_SERVER__HOST=0.0.0.0
|
||||
BOOSAPI_SERVER__PORT=4000
|
||||
BOOSAPI_UPSTREAM_BASE=https://api.boos.lat/v1
|
||||
BOOSAPI_UPSTREAM_KEY=sk-boos4721
|
||||
BOOSAPI_AUTH__AUTH_TOKEN=change-me-admin-token
|
||||
```
|
||||
|
||||
## 前端
|
||||
|
||||
前端为 React SPA,使用 Vite 构建,产物在 `dist/web/`:
|
||||
- 开发时用 Vite dev server(需 Node.js)
|
||||
- 生产时由 Rust 后端直接 serve 静态文件
|
||||
|
||||
### 登录与会话
|
||||
|
||||
- 登录页位于 `src/web/pages/auth/`:`LoginShell` 提供共享外壳,`AdminLoginPage`(token / 账密双模式)与 `UserLoginPage`(账密)分别承担两类受众。OAuth 入口用 `OAuthSlot` 渲染占位区域,**实际行为由后续 change 接入**——当前不要在这里挂任何 onClick / 跳转。
|
||||
- 会话状态统一走 `src/web/authSession.ts` 的 `commitSession / clearSession / getSessionToken`,受众通过 `'admin' | 'user'` 区分;旧的 `persistAuthSession / clearAuthSession / getAuthToken / hasValidAuthSession`(及 `*UserSession` 同族)保留为 `@deprecated` 薄壳,新代码不要再引用。
|
||||
- 登录错误文案统一走 `src/web/loginError.ts` 的 `resolveLoginErrorMessage(status, raw)`;新增登录错误请加在该处而不是各自分支里手写。
|
||||
@@ -1,89 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
BoosAPI is an AI API gateway: an OpenAI/Anthropic/Gemini-compatible proxy with a token-routing/failover engine, a management console (React SPA), embedded ComfyUI workflow surfaces, billing, and payments. Backend is **pure Rust** (axum + sqlx + tokio); there is no Node.js runtime dependency at serving time — Node is only used to build frontend assets.
|
||||
|
||||
Project-internal docs are largely in Chinese (`AGENTS.md`, `README.md`); match that when editing them.
|
||||
|
||||
## Build & run
|
||||
|
||||
The wrapper script `./build-and-run.sh` is the normal entry point (builds frontend + backend, then runs). Key flags:
|
||||
|
||||
```bash
|
||||
./build-and-run.sh --check # cargo check only — fastest verification, no frontend, no run
|
||||
./build-and-run.sh # dev profile, reuse frontend cache, compile + start
|
||||
./build-and-run.sh --backend-only # recompile/restart backend, skip Vite
|
||||
./build-and-run.sh --fast-release # near-prod performance, faster than --release
|
||||
./build-and-run.sh --production # build + embed frontend/Workflow UI into binary -> dist/production/
|
||||
```
|
||||
|
||||
Cargo profiles: `dev` (default), `release` (LTO, slow), `fast-release` (opt-level 2, no LTO — use for staging verification). Direct cargo also works: `cargo run --release`, `cargo check`.
|
||||
|
||||
Config comes from `config.toml` plus env overrides via figment. Env vars use the `BOOSAPI_` prefix with `__` as the nesting separator, e.g. `BOOSAPI_SERVER__PORT=4000`, `BOOSAPI_AUTH__AUTH_TOKEN=...`, `BOOSAPI_PAYMENT__STRIPE_SECRET_KEY=...`. Prefer env vars for all secrets.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
cargo test # Rust tests (rstest, wiremock, axum-test, tracing-test)
|
||||
cargo test <name> # single Rust test by name substring
|
||||
npm test # web tests: vitest run --root src/web
|
||||
npm run test:watch # vitest watch
|
||||
npx vitest run src/web/api.test.ts # single web test file
|
||||
npm run typecheck # tsc for src/web (app + tests)
|
||||
```
|
||||
|
||||
Rust tests live in `#[cfg(test)]` modules alongside source. Web tests are `src/web/**/*.test.ts(x)`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Proxy core (`src/proxy/`) — the heart of the system
|
||||
|
||||
Request flow for `/v1/*`: **downstream key auth → token route resolution → channel selection → upstream call (with failover) → protocol transform → response**.
|
||||
|
||||
- `token_router/` — `TokenRouter` resolves a requested model name to candidate upstream channels using DB-backed route rows + regex patterns, with a short (2s) in-process cache. Call `invalidate_route_cache()` after mutating routes/channels. Failure cooldown per channel is configurable.
|
||||
- `conductor/` — `ProxyConductor::execute` drives attempt/failover loop (max 5 attempts). It is decoupled from the DB via injected closures (`select_channel`, `select_next_channel`, `record_success`, `record_failure`, optional `refresh_auth`). `retry_policy.rs` classifies failures into actions: failover, retry-same-channel, refresh-auth, or terminal.
|
||||
- `executors/`, `providers/`, `surfaces/`, `capabilities/`, `cli_profiles/` — execution backends and provider/capability metadata.
|
||||
|
||||
### Protocol transformers (`src/transformers/`)
|
||||
|
||||
`openai/`, `anthropic/`, `gemini/` convert between client-facing protocol and upstream protocol around a `canonical/` intermediate form. When adding model/protocol support, work through the canonical layer rather than point-to-point conversions.
|
||||
|
||||
### Routes (`src/routes/`)
|
||||
|
||||
`mod.rs` assembles the axum router by `.merge()`-ing each module's `routes()` and serves the SPA as a fallback (embedded via `include_dir` in production, or from `dist/web` on disk). `admin/` is the management API; `proxy/` is the OpenAI-compatible entry; `comfyui/`, `comfyui_agent.rs`, `workflow_ui.rs`, `digital_human.rs`, `money_printer.rs`, `picture_agent.rs` are the ComfyUI/creative surfaces.
|
||||
|
||||
### Services (`src/services/`)
|
||||
|
||||
Background and cross-cutting logic: `billing/`, `payment.rs`, `cron/`, `notification/`, `runtime_auth.rs`, `builtin_keys.rs`, `comfyui_process.rs`, `route_rebuild.rs`. Shared `AppState` (in `src/lib.rs`) is cloned into handlers and holds config, DB pool, HTTP client, and these service handles.
|
||||
|
||||
### Database
|
||||
|
||||
**SQLite is the only runtime path** — `AppState.db` is `Arc<SqlitePool>`, and all runtime queries target SQLite. The Postgres dependency and `boosapi migrate-sqlite-to-postgres` subcommand exist only to copy/migrate schema+data into Postgres; full Postgres runtime query compatibility is not done. Do not assume queries run against Postgres. Migrations live in `migrations/` (SQLite); `db/schema_compat.rs` applies runtime compatibility fixups after migrations.
|
||||
|
||||
## ComfyUI Agent (local-mode contract)
|
||||
|
||||
The ComfyUI Agent defaults to **local mode**: it calls this service's own `/v1/*` routes using a bootstrapped built-in downstream key (`name='comfyui-agent' AND user_id IS NULL`, format `sk-comfyui-{uuid_hex}`, mirrored into `settings.builtin_key.comfyui_agent.secret`).
|
||||
|
||||
- Local (default): `upstream_base = http://{host}/v1` (host `0.0.0.0`/`::` rewritten to `127.0.0.1`); `/api/comfyui-agent/status` returns `mode: "local"`.
|
||||
- Fallback-remote: set `BOOSAPI_UPSTREAM_BASE` (non-empty) to use an external upstream; `BOOSAPI_UPSTREAM_KEY` falls back to the built-in key if unset.
|
||||
- The built-in key is **protected**: admin list masks it (`••••<last4>`), delete/disable return 403, and `builtin_key.*` settings are hidden from list / 403 on PUT. Startup `ensure_comfyui_agent_key` is idempotent (regenerates if missing, re-enables if disabled, keeps settings ↔ downstream_api_keys in sync).
|
||||
|
||||
Workflow backend modes (`[comfyui] backend`): `cloud` (Comfy Cloud, default), `runninghub`, `external` (your own ComfyUI), `boosapi` (built-in Rust workflow/API node executor), `docker` (legacy). Workflow UI has separate `cloud` and `external/runninghub` frontend asset bundles — install with `npm run workflow-ui:install:cloud` / `:external`.
|
||||
|
||||
## Frontend (two separate apps)
|
||||
|
||||
- `src/web/` — management console, React 18 + Vite, built to `dist/web/` and served by the Rust backend. `npm run dev` for HMR, `npm run build:web` to build.
|
||||
- `website/` — standalone marketing site, **Next.js 16 App Router + React 19**, its own package. Use `npm run website:dev` / `website:build` / `website:typecheck` (these proxy to `npm --prefix website`).
|
||||
|
||||
### Auth/session conventions (src/web)
|
||||
|
||||
- Session state goes through `authSession.ts`: `commitSession / clearSession / getSessionToken`, bucketed by audience `'admin' | 'user'`. The old `persistAuthSession / clearAuthSession / getAuthToken / hasValidAuthSession` family is `@deprecated` — do not use in new code.
|
||||
- Login pages are split under `pages/auth/`: `LoginShell` (shared shell) + `AdminLoginPage` (token / account modes) + `UserLoginPage`. OAuth entries render via `OAuthSlot` as placeholders only — do not wire onClick/navigation there.
|
||||
- Login error text is normalized in `loginError.ts` via `resolveLoginErrorMessage(status, raw)` — add new login errors there, not inline.
|
||||
|
||||
## Shared TS/Rust contracts (`src/shared/`)
|
||||
|
||||
This directory holds logic mirrored between Rust (`.rs`) and TypeScript (`.ts`/`.js`) — e.g. `tokenRouteContract`, `tokenRoutePatterns`, `modelBrand`, `platformIdentity`, `proxyLogMeta`, `siteInitializationPresets`. When changing a routing/identity contract, keep the Rust and TS sides in sync.
|
||||
Reference in New Issue
Block a user