From 1c02fcdaa7998d2d57955e8cc457f3d195b5861f Mon Sep 17 00:00:00 2001 From: phamnazage-jpg Date: Tue, 12 May 2026 21:46:19 +0800 Subject: [PATCH] chore: bootstrap repository --- README.md | 66 ++ cmd/.gitkeep | 0 ...05-12-sub2api-cn-relay-manager-solution.md | 619 +++++++++++ ...pi-cn-relay-manager-implementation-plan.md | 973 ++++++++++++++++++ internal/.gitkeep | 0 packs/openai-cn-pack/README.md | 18 + packs/openai-cn-pack/pack.json.example | 10 + .../providers/deepseek.json.example | 37 + web/.gitkeep | 0 9 files changed, 1723 insertions(+) create mode 100644 README.md create mode 100644 cmd/.gitkeep create mode 100644 docs/2026-05-12-sub2api-cn-relay-manager-solution.md create mode 100644 docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md create mode 100644 internal/.gitkeep create mode 100644 packs/openai-cn-pack/README.md create mode 100644 packs/openai-cn-pack/pack.json.example create mode 100644 packs/openai-cn-pack/providers/deepseek.json.example create mode 100644 web/.gitkeep diff --git a/README.md b/README.md new file mode 100644 index 00000000..00247d67 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# sub2api-cn-relay-manager + +`sub2api-cn-relay-manager` 是一个独立于 `sub2api` 宿主仓库的外部伴生安装器 / 控制面项目。 + +目标不是修改 `sub2api`,也不是把国产模型能力硬塞进宿主源码,而是通过 `sub2api` 已有的管理 API,把“国产模型 OpenAI 兼容中转能力”以独立交付物的形式安装到任意一台已部署的 `sub2api` 实例上。 + +## 项目定位 + +- 宿主:第三方开源系统 `sub2api` +- 约束:不修改宿主代码,不 fork 宿主,不要求宿主内置原生插件运行时 +- 交付:一个独立控制面项目 + 一个或多个 `model_pack` +- 结果:管理员可一键导入多个国产模型 key,普通用户继续只用 `sub2api` 标准 API + +## 这个项目解决的问题 + +- 不同机器部署了 `sub2api` 后,如何用同一套交付物补齐国产模型中转能力 +- 如何把多个国产模型 key 批量导入为可用的宿主资源 +- 如何在不改宿主的前提下实现热生效、探测、对账和漂移发现 +- 如何让普通用户完全无感,继续走 `sub2api` 标准接口 + +## 非目标 + +- 不做宿主原生插件系统 +- 不做任意第三方 Go 代码动态加载 +- 不要求宿主识别“插件”概念 +- 不接管宿主网关流量 + +## 目录结构 + +```text +sub2api-cn-relay-manager/ + cmd/ # 未来 CLI / server 入口 + internal/ # 未来控制面后端实现 + web/ # 未来管理端前端 + docs/ + 2026-05-12-sub2api-cn-relay-manager-solution.md + packs/ + openai-cn-pack/ + README.md + pack.json.example + providers/ + deepseek.json.example +``` + +## 交付模型 + +本项目最终会拆成两个可独立发布的产物: + +1. `sub2api-cn-relay-manager` + - 外部控制面 / 安装器 + - 连接已有 `sub2api` + - 安装模型包 + - 调用宿主管理 API 创建资源 + - 做 smoke test、对账和状态展示 + +2. `model_pack` + - 只包含 provider 定义、默认模板、导入规则和校验信息 + - 不携带宿主执行代码 + - 可以跨机器复用 + +## 当前文档 + +完整方案见: + +- [docs/2026-05-12-sub2api-cn-relay-manager-solution.md](./docs/2026-05-12-sub2api-cn-relay-manager-solution.md) + diff --git a/cmd/.gitkeep b/cmd/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/2026-05-12-sub2api-cn-relay-manager-solution.md b/docs/2026-05-12-sub2api-cn-relay-manager-solution.md new file mode 100644 index 00000000..72c31558 --- /dev/null +++ b/docs/2026-05-12-sub2api-cn-relay-manager-solution.md @@ -0,0 +1,619 @@ +# sub2api-cn-relay-manager 方案文档 + +日期:2026-05-12 + +## 1. 背景 + +宿主 `sub2api` 是一个第三方开源系统,用户较多,迭代很快,且每周都会发布新版本。 + +当前目标不是修改宿主源码,也不是等待宿主内置原生插件运行时,而是建立一个完全独立交付的外部伴生项目,让任意一台已部署的 `sub2api` 都能通过安装该项目和导入模型包,快速具备国产模型 OpenAI 兼容中转能力。 + +## 2. 目标 + +本方案必须满足以下业务目标: + +1. 独立交付,不修改宿主 `sub2api` 源码 +2. 可跨机器复用,在任意一台兼容版本的 `sub2api` 上重复安装 +3. 可一键导入多个国产模型 key +4. 导入后让普通用户继续通过 `sub2api` 标准 API 使用国产模型 +5. 尽量热生效,不依赖宿主重编译 +6. 能主动发现“内容未真正生效”“生效后漂移”“部分账号失效” + +## 3. 核心结论 + +在“不改宿主代码”的前提下,不能把该能力定义为“宿主原生插件”。 + +正确做法是: + +- 把这套能力定义为一个独立的外部伴生控制面项目 +- 把国产模型接入定义为可安装的 `model_pack` +- 由控制面调用宿主已有管理 API 自动创建 `group / channel / account / plan` +- 由控制面维护安装状态、导入批次、探测结果和对账结果 + +因此,业务上它表现得像“独立安装插件”,但技术上它是“外部伴生安装器 + 模型资源包”。 + +## 3.1 宿主零改动硬约束 + +为了确保方案始终满足“绝不改宿主代码”,补充以下硬约束: + +1. 不修改宿主源码 +2. 不 fork 宿主并运行自定义二进制 +3. 不直接写宿主数据库 +4. 不向宿主容器或宿主目录写入运行时代码或配置补丁 +5. 不依赖宿主未公开的动态加载、初始化钩子或内部运行时对象 +6. 只通过宿主现有 HTTP 管理 API 和宿主公开标准 API 工作 + +这意味着本方案的本质是: + +- 技术上:宿主外部自动化编排 +- 业务上:像独立插件一样交付和安装 + +## 3.2 审核后的最终结论 + +经过对宿主现有能力复核后,可以确认: + +- 该方案可以在零宿主代码改动前提下完成国产模型中转能力增加 +- 该方案不能伪装成宿主原生插件中心 +- 该方案必须把“访问闭环”和“对账闭环”都纳入首版 + +## 4. 为什么这条路线成立 + +虽然宿主没有原生插件运行时,但它已有足够的管理 API: + +- 创建 group:`POST /api/v1/admin/groups` +- 创建 account:`POST /api/v1/admin/accounts` +- 批量创建 account:`POST /api/v1/admin/accounts/batch` +- 测试 account:`POST /api/v1/admin/accounts/:id/test` +- 查询 account 模型:`GET /api/v1/admin/accounts/:id/models` +- 创建 channel:`POST /api/v1/admin/channels` +- 创建 plan:`POST /api/v1/admin/plans` + +这意味着控制面完全可以把 `sub2api` 当作一个可编排宿主,而不需要侵入其源码。 + +但这里有一个经过审核后必须写明的宿主约束: + +- 宿主标准 API 网关要求请求最终落到“已分组 API key”或“有效 subscription” +- 仅仅创建 `group / channel / account / plan`,还不足以保证普通用户已经可以调用国产模型 + +因此,本方案不能只做资源创建,还必须补齐用户访问闭环。 + +## 5. 总体架构 + +整体拆成两个发布物: + +### 5.1 控制面 / 安装器 + +项目名: + +`sub2api-cn-relay-manager` + +职责: + +- 连接宿主 `sub2api` +- 安装 `model_pack` +- 预检宿主版本与 API 能力 +- 一键导入多个 key +- 通过宿主管理 API 创建和维护资源 +- 做 smoke test +- 做持续对账 +- 对外展示“可用 / 降级 / 漂移 / 失败”状态 + +### 5.2 模型包 + +项目内置样例: + +`packs/openai-cn-pack/` + +职责: + +- 提供 provider 定义 +- 提供默认 group / channel / plan / account 模板 +- 提供 model mapping 和 smoke test model +- 提供导入策略和校验约束 + +模型包不携带宿主执行逻辑。 + +## 6. 运行时组件 + +控制面内部建议拆成以下模块: + +### 6.1 Host Adapter + +宿主适配器,只负责把控制面操作翻译成 `sub2api` admin API 调用。 + +接口建议: + +- `GetHostVersion()` +- `ProbeCapabilities()` +- `CreateGroup()` +- `CreateChannel()` +- `CreatePlan()` +- `CreateAccount()` +- `BatchCreateAccounts()` +- `TestAccount()` +- `GetAccountModels()` +- `AssignSubscription()` +- `CheckAccessPath()` +- `DeleteGroup()` +- `DeleteChannel()` +- `DeletePlan()` +- `DeleteAccount()` +- `ListManagedResources()` + +### 6.2 Pack Runtime + +负责读取和校验 `model_pack`: + +- `pack.json` +- `providers/*.json` +- `checksums.txt` + +### 6.3 Provision Engine + +负责把一个 provider + 一批 key 变成宿主真实资源: + +- 创建 group +- 创建 channel +- 创建 plan +- 创建 account +- 绑定 group / model mapping / pricing +- 调用测试接口验证账号 + +### 6.4 Reconciler + +负责持续对账,主动发现: + +- 资源是否还存在 +- key 是否仍可用 +- 模型是否仍可列出 +- 测试是否通过 +- 宿主资源是否被人工改坏 + +### 6.5 State Store + +负责维护控制面自己的状态,因为宿主不知道“插件”概念。 + +## 7. 模型包结构 + +建议结构: + +```text +openai-cn-pack/ + pack.json + providers/ + deepseek.json + kimi.json + qwen.json + glm.json + minimax.json + checksums.txt + docs/ +``` + +## 8. 模型包协议 + +### 8.1 pack.json + +```json +{ + "pack_id": "openai-cn-pack", + "version": "1.0.0", + "vendor": "YourTeam", + "target_host": "sub2api", + "min_host_version": "0.1.126", + "max_host_version": "0.2.x", + "providers_dir": "providers", + "checksum_file": "checksums.txt" +} +``` + +### 8.2 provider 定义 + +每个 provider 文件至少包含: + +- `provider_id` +- `display_name` +- `base_url` +- `platform` +- `account_type` +- `default_models` +- `smoke_test_model` +- `group_template` +- `channel_template` +- `plan_template` +- `import` + +示例字段: + +```json +{ + "provider_id": "deepseek", + "display_name": "DeepSeek OpenAI Compatible", + "base_url": "https://api.deepseek.com", + "platform": "openai", + "account_type": "api", + "default_models": ["deepseek-chat", "deepseek-reasoner"], + "smoke_test_model": "deepseek-chat", + "group_template": { + "name": "DeepSeek 默认分组", + "subscription_type": "subscription", + "rate_multiplier": 1.0 + }, + "channel_template": { + "name": "DeepSeek 默认渠道", + "billing_model_source": "channel_mapped", + "restrict_models": true, + "model_mapping": { + "deepseek-chat": "deepseek-chat", + "deepseek-reasoner": "deepseek-reasoner" + } + }, + "plan_template": { + "name": "DeepSeek 默认套餐", + "price": 19.9, + "validity_days": 30, + "for_sale": true + }, + "import": { + "supports_multi_key": true, + "supports_strict": true, + "supports_partial": true + } +} +``` + +## 9. 一键导入流程 + +### 9.1 安装模型包 + +1. 上传 `openai-cn-pack.zip` +2. 控制面解压并校验 +3. 读取 `pack.json` +4. 校验宿主版本兼容 +5. 执行宿主 API 能力探测 +6. 注册 provider 定义到控制面状态库 + +### 9.2 导入多个 key + +1. 管理员选择 provider +2. 粘贴多个 key 或上传 `txt / csv` +3. 选择导入模式: + - `strict` + - `partial` +4. 运行预检 +5. 创建或复用 group +6. 创建或复用 channel +7. 创建或复用 plan +8. 批量创建 accounts +9. 逐个运行账号测试 +10. 汇总结果并写入批次记录 + +### 9.3 普通用户使用 + +导入完成后,普通用户继续使用宿主标准 API: + +- `/v1/chat/completions` +- `/v1/responses` +- 宿主已支持的其他 OpenAI 标准接口 + +控制面不接管用户流量,只负责把宿主配置成“可中转国产模型”。 + +### 9.4 用户访问闭环 + +这是方案审核后新增的强制章节。 + +宿主网关不是只要存在上游 account 就能调用。 + +实际还需要满足以下至少一条: + +1. 用户持有的 API key 已绑定到目标 group +2. 用户在目标 group 上拥有有效 subscription + +否则,普通用户即使拿到宿主标准 API 地址,也不能真正使用国产模型。 + +因此控制面必须明确支持两种访问模式: + +#### 模式 A:Subscription 模式 + +适用场景: + +- SaaS 运营 +- 后台给用户开通套餐 +- 管理员按用户分配访问能力 + +控制面职责: + +- 创建 subscription 类型 group +- 创建默认可售或可分配 plan +- 调用宿主订阅分配接口,把 group 分配给目标用户 + +#### 模式 B:User Self-Service API Key 模式 + +适用场景: + +- 用户已经登录宿主后台 +- 用户自己创建 API key +- 用户自己把 key 绑定到控制面准备好的 group + +控制面职责: + +- 负责准备好 group / channel / account / plan +- 确保目标 group 对用户可见、可分配、可购买或可授权 + +#### 首版限制 + +在零宿主代码改动前提下,控制面不能把“管理员代用户签发最终 API key”作为首版硬依赖。 + +因此首版应优先保证: + +- subscription 模式闭环可用 +- user self-service API key 模式可用 + +## 10. 导入模式 + +### 10.1 strict + +- 任一 key 创建失败或测试失败 +- 整个批次回滚 +- 适合正式生产导入 + +### 10.2 partial + +- 成功的保留 +- 失败的单独记录 +- 适合大批量导入 + +### 10.3 导入完成判定 + +经过审核,单纯“资源创建成功”不再视为导入完成。 + +导入完成至少需要满足: + +1. 目标 group 存在 +2. 目标 channel 存在并已绑定 group +3. 目标 plan 存在,若当前访问模式依赖 plan +4. 至少一个 account 创建成功 +5. 至少一个 account smoke test 或模型探测通过 +6. 至少一种用户访问模式已经被验证可用 + +不满足上述条件时,只能标记为: + +- `degraded` +- `failed` + +## 11. 数据模型 + +控制面至少需要以下表: + +### 11.1 hosts + +- 宿主实例信息 +- base_url +- 宿主版本 +- 最近能力探测结果 + +### 11.2 packs + +- 模型包版本 +- checksum +- 安装时间 + +### 11.3 providers + +- provider 元信息 +- pack 归属 + +### 11.4 provider_installs + +- 某台宿主安装了哪些 provider +- 当前状态 + +### 11.5 import_batches + +- 一次导入批次 +- provider +- 模式 +- 请求条数 +- 成功条数 +- 失败条数 + +### 11.6 managed_resources + +- 宿主侧 group/channel/plan/account 映射 +- 宿主资源 ID +- 控制面资源键 + +### 11.7 reconcile_runs + +- 每次对账执行结果 + +### 11.8 probe_results + +- 账号测试与模型探测结果 + +## 12. 状态机 + +### 12.1 provider 安装状态 + +- `discovered` +- `validated` +- `installed` +- `active` +- `degraded` +- `drifted` +- `failed` +- `disabled` + +### 12.2 导入批次状态 + +- `pending` +- `running` +- `succeeded` +- `partially_succeeded` +- `rolled_back` +- `failed` + +## 13. 热生效与重启建议 + +在本方案下,大多数操作应视为热生效: + +- 创建 group +- 创建 channel +- 创建 plan +- 创建 account +- 更新 model mapping +- 导入多个 key + +因为这些动作都是通过宿主已有 admin API 写入运行时资源。 + +因此默认策略是: + +- 成功写入宿主并通过测试 = 热生效 +- 如果出现宿主缓存、运行时异常、版本兼容问题,控制面只给出 `restart_recommended` +- 控制面可选支持宿主重启适配器,但不作为首版必需能力 + +审核补充: + +- `restart_recommended` 只是运维建议,不应成为主成功路径 +- 只要核心能力依赖“必须重启宿主后才能导入成功”,就视为不满足首版目标 +- 因此首版所有核心链路必须默认按热生效设计 + +## 14. 主动发现未生效 + +控制面必须周期性对账,而不是只看“导入成功”。 + +检查项至少包括: + +1. 记录中的 group 是否还存在 +2. channel 是否还存在 +3. plan 是否还存在 +4. account 是否还存在 +5. account 测试是否通过 +6. account 模型列表是否仍可读取 +7. provider 的关键模型是否能通过一次标准测试请求 +8. 控制面记录与宿主实际资源是否一致 +9. 至少一种用户访问模式仍然成立 + +对于第 9 条,控制面至少要检查一种访问路径: + +- subscription 模式下:目标用户对该 group 是否仍有有效 subscription +- user self-service API key 模式下:是否存在已绑定目标 group 的有效用户 API key 样本 + +只要任一项不一致,就应标记: + +- `degraded` +- `drifted` +- `failed` + +## 15. 外部 API 设计 + +控制面对外至少提供: + +### 15.1 宿主管理 + +- `POST /api/hosts` +- `GET /api/hosts` +- `POST /api/hosts/:id/probe` + +### 15.2 模型包管理 + +- `POST /api/packs/install` +- `GET /api/packs` +- `GET /api/packs/:id/providers` + +### 15.3 provider 导入 + +- `POST /api/providers/:provider_id/preview-import` +- `POST /api/providers/:provider_id/import` +- `GET /api/providers/:provider_id/import-batches` +- `POST /api/import-batches/:id/rollback` + +### 15.4 访问闭环管理 + +- `POST /api/providers/:provider_id/access/preview` +- `POST /api/providers/:provider_id/access/assign-subscriptions` +- `GET /api/providers/:provider_id/access/status` + +说明: + +- 首版优先支持 subscription 访问闭环 +- 不把“管理员代用户创建最终 API key”作为首版必需能力 + +### 15.5 对账与探测 + +- `POST /api/providers/:provider_id/reconcile` +- `GET /api/providers/:provider_id/status` +- `GET /api/providers/:provider_id/resources` + +## 16. 首版 CLI + +建议首版同时提供 CLI,降低自动化接入门槛: + +```bash +cnrelay host add --base-url https://your-sub2api --admin-token xxx +cnrelay pack install ./openai-cn-pack.zip +cnrelay provider import deepseek --keys-file deepseek.txt --mode partial --smoke-test +cnrelay provider status deepseek +cnrelay reconcile run +``` + +## 17. 与宿主升级的兼容策略 + +由于宿主 `sub2api` 每周发布新版本,控制面必须内置兼容矩阵: + +1. 安装前先探测宿主版本 +2. 再跑一组能力探针: + - groups create/list + - accounts create/test/models + - channels create/list + - plans create/list + - subscriptions assign/list + - 至少一种用户访问闭环探测 +3. 任何一项不满足,即阻断安装或阻断导入 +4. 对宿主版本维护 `supported / warning / unsupported` + +这样可以避免宿主快速迭代导致导入流程静默失效。 + +## 18. MVP 范围 + +首版只支持: + +- 一个宿主适配器:`sub2api` +- 一种包类型:`openai-cn-pack` +- 5 个 provider 模板以内 +- 多 key 导入 +- strict / partial 两种模式 +- account 测试与模型探测 +- subscription 访问闭环 +- 对账与漂移发现 + +首版不做: + +- 多宿主类型 +- 在线插件市场 +- 远程代码执行 +- 宿主内嵌 UI +- 宿主数据库直写 +- 管理员代用户签发最终 API key + +## 19. 验收标准 + +满足以下标准即视为方案落地成功: + +1. 任意一台兼容版本的 `sub2api`,只通过控制面和模型包即可接入至少一个国产模型 provider +2. 支持一次导入多个 key,并得到逐 key 成功/失败结果 +3. 导入成功后,至少一种用户访问模式已经被控制面验证可用 +4. 普通用户继续通过 `sub2api` 标准 API 成功调用目标模型 +5. 控制面可检测删除、失效、模型缺失、测试失败、访问闭环断裂等漂移 +6. 宿主升级后,控制面能在导入前给出兼容性结论,而不是静默失败 + +## 20. 下一步 + +建议下一步直接进入实现计划拆解,顺序如下: + +1. 定义控制面状态库 schema +2. 实现 `sub2api` 宿主适配器 +3. 实现 `model_pack` schema 校验器 +4. 实现 provider 导入引擎 +5. 实现对账器 +6. 再补 CLI 和最小 Web UI diff --git a/docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md b/docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md new file mode 100644 index 00000000..d946f54a --- /dev/null +++ b/docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md @@ -0,0 +1,973 @@ +# sub2api-cn-relay-manager Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** 构建一个完全独立于宿主 `sub2api` 的外部伴生控制面,在不修改宿主代码的前提下,通过安装 `model_pack`、批量导入国产模型 key、自动创建宿主资源并完成用户访问闭环,让普通用户直接通过 `sub2api` 标准 API 使用国产模型。 + +**Architecture:** 采用“外部控制面 + `model_pack` + 宿主 HTTP API 适配器”架构。控制面只通过 `sub2api` 公开管理 API 和标准 API 工作,负责模型包安装、宿主能力探测、资源编排、导入回滚、访问闭环验证和持续对账;宿主继续承担标准 API 网关职责,不做任何源码、数据库或运行时注入修改。 + +**Tech Stack:** Go 1.24、Chi、`database/sql` + SQLite 驱动、PostgreSQL 兼容预留、OpenAPI 3.1、Go `testing` + `httptest`、前端首版后置,MVP 先提供 HTTP API、CLI 和 Docker 交付物。 + +--- + +## 1. 实施边界 + +### 1.1 必须满足 + +- 不修改宿主 `sub2api` 源码 +- 不 fork 宿主并运行自定义二进制 +- 不直接写宿主数据库 +- 不向宿主目录写入运行时代码、插件文件或配置补丁 +- 只通过宿主现有管理 API 和标准 API 工作 +- 首版必须完成从“导入 key”到“普通用户可实际调用”的闭环 + +### 1.2 首版不做 + +- 宿主原生插件中心 +- 任意后端代码插件加载 +- 代替用户签发宿主最终 API key +- 多宿主编排联邦 +- 自定义计费、结算或审计系统 + +## 2. 目标目录结构 + +```text +sub2api-cn-relay-manager/ + cmd/ + server/main.go + cli/main.go + internal/ + app/ + app.go + bootstrap.go + config/ + config.go + domain/ + host.go + pack.go + provider.go + resource.go + import_batch.go + reconcile.go + access_closure.go + host/ + sub2api/ + client.go + capability_probe.go + groups.go + channels.go + plans.go + accounts.go + subscriptions.go + gateway_probe.go + pack/ + manifest.go + provider_manifest.go + checksum.go + validator.go + loader.go + provision/ + preview_service.go + import_service.go + rollback_service.go + naming.go + access/ + planner.go + subscription_service.go + self_service_checker.go + reconcile/ + runner.go + drift_checker.go + probe_runner.go + store/ + migrations/ + sqlite/ + db.go + hosts_repo.go + host_capability_snapshots_repo.go + packs_repo.go + providers_repo.go + installs_repo.go + import_batches_repo.go + resources_repo.go + reconcile_runs_repo.go + probe_results_repo.go + access_closure_records_repo.go + api/ + http/ + router.go + middleware.go + hosts_handler.go + packs_handler.go + providers_handler.go + imports_handler.go + access_handler.go + reconcile_handler.go + dto/ + hosts.go + packs.go + providers.go + imports.go + access.go + reconcile.go + worker/ + scheduler.go + jobs.go + docs/ + api/openapi.yaml + plans/ + 2026-05-12-sub2api-cn-relay-manager-implementation-plan.md + packs/ + openai-cn-pack/ + pack.json.example + providers/ + deepseek.json.example + tests/ + integration/ + host_stub_test.go + install_pack_test.go + import_keys_test.go + access_closure_test.go + reconcile_test.go +``` + +## 3. 核心接口与契约 + +### 3.1 控制面对外 API + +#### 宿主管理 + +- `POST /api/hosts` +- `GET /api/hosts` +- `GET /api/hosts/{host_id}` +- `POST /api/hosts/{host_id}/probe` + +#### 模型包管理 + +- `POST /api/packs/install` +- `GET /api/packs` +- `GET /api/packs/{pack_id}` +- `GET /api/packs/{pack_id}/providers` + +#### Provider 导入 + +- `POST /api/providers/{provider_id}/preview-import` +- `POST /api/providers/{provider_id}/import` +- `GET /api/providers/{provider_id}/import-batches` +- `GET /api/import-batches/{batch_id}` +- `POST /api/import-batches/{batch_id}/rollback` + +#### 访问闭环 + +- `POST /api/providers/{provider_id}/access/preview` +- `POST /api/providers/{provider_id}/access/assign-subscriptions` +- `GET /api/providers/{provider_id}/access/status` + +#### 对账与状态 + +- `POST /api/providers/{provider_id}/reconcile` +- `GET /api/providers/{provider_id}/status` +- `GET /api/providers/{provider_id}/resources` + +### 3.2 宿主适配器接口 + +`internal/host/sub2api/client.go` 中定义单一适配接口: + +```go +type HostAdapter interface { + GetHostVersion(ctx context.Context) (string, error) + ProbeCapabilities(ctx context.Context) (HostCapabilities, error) + CreateGroup(ctx context.Context, req CreateGroupRequest) (GroupRef, error) + CreateChannel(ctx context.Context, req CreateChannelRequest) (ChannelRef, error) + CreatePlan(ctx context.Context, req CreatePlanRequest) (PlanRef, error) + CreateAccount(ctx context.Context, req CreateAccountRequest) (AccountRef, error) + BatchCreateAccounts(ctx context.Context, req BatchCreateAccountsRequest) ([]AccountRef, error) + TestAccount(ctx context.Context, accountID string) (ProbeResult, error) + GetAccountModels(ctx context.Context, accountID string) ([]string, error) + AssignSubscription(ctx context.Context, req AssignSubscriptionRequest) (SubscriptionRef, error) + CheckGatewayAccess(ctx context.Context, req GatewayAccessCheckRequest) (GatewayAccessResult, error) + DeleteGroup(ctx context.Context, groupID string) error + DeleteChannel(ctx context.Context, channelID string) error + DeletePlan(ctx context.Context, planID string) error + DeleteAccount(ctx context.Context, accountID string) error + ListManagedResources(ctx context.Context, req ListManagedResourcesRequest) (ManagedResourceSnapshot, error) +} +``` + +约束: + +- 所有宿主调用都必须通过该接口进入 +- 任何绕过该接口的 HTTP 调用都视为实现缺陷 +- 所有宿主 API 路径、请求体和响应体在 `internal/host/sub2api/` 内部封装,不向业务层泄漏 + +## 4. 状态机 + +### 4.1 Provider 安装状态 + +```text +discovered -> validated -> installed -> active + | | + v v + failed <-> degraded <-> drifted + | + v + disabled +``` + +状态定义: + +- `discovered`:模型包已上传,尚未完成结构校验 +- `validated`:包结构、校验和、版本兼容、宿主能力探测通过 +- `installed`:provider 元数据和模板已写入控制面状态库 +- `active`:宿主资源存在,账号探测成功,至少一种用户访问模式已验证可用 +- `degraded`:核心资源存在但 smoke test、模型探测或访问闭环部分失败 +- `drifted`:宿主资源与控制面记录不一致,例如资源被人工改动或删除 +- `failed`:安装、导入、回滚、对账任一关键步骤失败 +- `disabled`:provider 被显式停用,不再调度对账任务 + +### 4.2 导入批次状态 + +```text +pending -> previewed -> running -> succeeded + | | | + | | v + | -> partially_succeeded + v + failed -> rollback_running -> rolled_back +``` + +### 4.3 访问闭环状态 + +- `not_configured` +- `subscription_ready` +- `self_service_ready` +- `fully_ready` +- `broken` + +判定规则: + +- 仅 `subscription` 模式可用:`subscription_ready` +- 仅 `self-service` 模式可用:`self_service_ready` +- 两者都可用:`fully_ready` +- 两者都不可用:`broken` + +## 5. 校验流程 + +### 5.1 宿主接入校验 + +请求:`POST /api/hosts` + +流程: + +1. 校验 `base_url`、认证信息格式 +2. 拉取宿主版本 +3. 探测宿主管理 API 能力: + - group 创建 + - channel 创建 + - plan 创建 + - account 创建 / 测试 / 模型列举 + - subscription 分配 +4. 生成能力矩阵并落库 +5. 若任一关键能力缺失,宿主状态标记为 `unsupported` + +通过条件: + +- 宿主版本在 `pack.json` 兼容区间内 +- 所有首版硬依赖 API 均可调用 + +### 5.2 模型包装载校验 + +请求:`POST /api/packs/install` + +流程: + +1. 解压上传包到临时目录 +2. 校验 `pack.json` 存在且字段完整 +3. 校验 `providers/*.json` 至少存在一个 provider +4. 校验 `checksums.txt` +5. 校验 provider schema +6. 校验模型包声明的宿主版本兼容性 +7. 将包元数据、provider 元数据写入状态库 + +拒绝条件: + +- 缺少 `pack.json` +- provider 文件重复或 `provider_id` 冲突 +- `base_url` 非 HTTPS +- `default_models` 为空 +- `smoke_test_model` 不在 `default_models` 内 + +### 5.3 导入预检 + +请求:`POST /api/providers/{provider_id}/preview-import` + +流程: + +1. 规范化 key 列表:去空白、去重、去 BOM、过滤空行 +2. 校验导入模式是否允许:`strict` 或 `partial` +3. 生成建议资源名 +4. 检查宿主是否已有同名 group / channel / plan +5. 检查当前 provider 是否已有历史导入资源 +6. 计算执行计划: + - create + - reuse + - conflict +7. 输出预检报告,不落宿主资源 + +### 5.4 导入执行 + +请求:`POST /api/providers/{provider_id}/import` + +流程: + +1. 创建批次记录,状态 `pending` +2. 执行预检,成功后切换为 `previewed` +3. 创建或复用 group +4. 创建或复用 channel +5. 按访问模式决定是否创建或复用 plan +6. 批量创建 accounts +7. 对每个 account 执行: + - 宿主 `/test` + - 宿主 `/models` + - 关键模型 smoke probe +8. 若访问模式为 `subscription`: + - 预览待分配用户列表 + - 调用宿主分配接口 +9. 执行网关访问探测 +10. 写入资源映射、探测结果、访问闭环状态 +11. 根据模式决定成功、部分成功或回滚 + +### 5.5 回滚校验 + +触发条件: + +- `strict` 模式下任一关键步骤失败 +- 管理员主动回滚批次 + +流程: + +1. 读取批次关联资源 +2. 按依赖逆序删除: + - subscriptions + - accounts + - channel + - plan + - group +3. 删除成功后标记 `rolled_back` +4. 若部分删除失败,批次状态标记 `failed`,provider 状态标记 `drifted` + +### 5.6 对账校验 + +请求:`POST /api/providers/{provider_id}/reconcile` + +流程: + +1. 读取控制面记录的宿主资源快照 +2. 拉取宿主实际资源 +3. 核对 group / channel / plan / account 是否仍存在 +4. 对 active account 重新执行 `/test` 和 `/models` +5. 重新执行至少一种用户访问模式探测 +6. 汇总为: + - `active` + - `degraded` + - `drifted` + - `failed` + +## 6. API 请求与响应最小契约 + +### 6.1 `POST /api/hosts` + +请求体: + +```json +{ + "name": "prod-sub2api", + "base_url": "https://sub2api.example.com", + "auth": { + "type": "bearer", + "token": "xxxx" + } +} +``` + +成功响应: + +```json +{ + "host_id": "host_01", + "version": "0.1.126", + "status": "supported", + "capabilities": { + "groups": true, + "channels": true, + "plans": true, + "accounts": true, + "account_test": true, + "account_models": true, + "subscriptions": true + } +} +``` + +### 6.2 `POST /api/providers/{provider_id}/import` + +请求体: + +```json +{ + "host_id": "host_01", + "mode": "partial", + "access_mode": "subscription", + "keys": [ + "sk-1", + "sk-2" + ], + "subscription_targets": [ + { + "user_id": "user_01", + "duration_days": 30 + } + ] +} +``` + +成功响应: + +```json +{ + "batch_id": "batch_01", + "status": "succeeded", + "provider_status": "active", + "access_closure_status": "subscription_ready", + "created": { + "group_id": "g_01", + "channel_id": "c_01", + "plan_id": "p_01", + "account_ids": ["a_01", "a_02"] + } +} +``` + +## 7. 数据库与迁移计划 + +### 7.1 必建表 + +- `hosts` +- `host_capability_snapshots` +- `packs` +- `providers` +- `provider_installs` +- `import_batches` +- `import_batch_items` +- `managed_resources` +- `probe_results` +- `access_closure_records` +- `reconcile_runs` + +### 7.2 约束 + +- `providers.provider_id` 在同一 `pack_id` 下唯一 +- `managed_resources` 以 `host_id + provider_id + resource_type + logical_key` 唯一 +- `import_batch_items.raw_key_hash` 唯一约束只用于同一批次内去重 +- 密钥明文不落库,只存掩码、哈希和宿主侧 account ID + +## 8. 测试策略 + +### 8.1 单元测试 + +- `internal/pack/validator.go` +- `internal/provision/naming.go` +- `internal/access/planner.go` +- `internal/reconcile/drift_checker.go` + +### 8.2 宿主适配器契约测试 + +- 使用 `httptest.Server` 模拟 `sub2api` 管理 API +- 覆盖成功、鉴权失败、字段缺失、版本不兼容、接口漂移 + +### 8.3 集成测试 + +- 安装模型包 +- 多 key 导入 +- `strict` 失败回滚 +- `partial` 部分成功 +- `subscription` 访问闭环 +- 对账发现漂移 + +### 8.4 验收测试 + +- 真实连接一套测试版 `sub2api` +- 导入 `deepseek` 两个 key +- 为测试用户分配 subscription +- 使用该用户实际标准 API key 发起一次 `/v1/chat/completions` +- 验证请求成功进入目标 provider + +## 9. 分阶段里程碑 + +### Milestone 1:项目骨架与状态库 + +验收标准: + +- 控制面可启动 +- SQLite 可自动迁移 +- `POST /api/hosts`、`GET /api/hosts` 可用 +- 宿主能力探测结果可持久化 + +### Milestone 2:模型包安装与校验 + +验收标准: + +- 可安装 `openai-cn-pack` +- 可列出 provider +- 版本兼容和 checksum 校验有效 +- 非法 provider 包会被拒绝 + +### Milestone 3:多 key 导入与资源编排 + +验收标准: + +- 可对单个 provider 执行预检 +- 可批量导入多个 key +- 可创建 group / channel / plan / account +- `strict` / `partial` 两种模式行为正确 + +### Milestone 4:访问闭环 + +验收标准: + +- `subscription` 模式打通 +- 至少一个测试用户可通过宿主标准 API 调用目标国产模型 +- 控制面可返回 `subscription_ready` 或 `fully_ready` + +### Milestone 5:对账、漂移与回滚 + +验收标准: + +- 定时对账可运行 +- 宿主资源被人工删除时,provider 状态变为 `drifted` +- 可对批次执行回滚 +- 回滚后状态库与宿主资源一致 + +### Milestone 6:独立交付与部署文档 + +验收标准: + +- 可生成单二进制和 Docker 镜像 +- 提供 `.env.example` 和最小部署说明 +- 可通过 CLI 完成宿主接入、模型包装载和导入 +- 文档明确宿主零改动边界和支持矩阵 + +## 10. 逐任务实施清单 + +### Task 1: 建立项目骨架与配置加载 + +**Files:** +- Create: `cmd/server/main.go` +- Create: `cmd/cli/main.go` +- Create: `internal/app/app.go` +- Create: `internal/app/bootstrap.go` +- Create: `internal/config/config.go` + +**Step 1: 写配置加载单元测试** + +- Test: `tests/integration/config_bootstrap_test.go` +- 覆盖环境变量、默认值、缺失必填项 + +**Step 2: 运行测试验证失败** + +Run: `go test ./tests/integration -run TestConfigBootstrap -v` +Expected: FAIL,提示配置加载尚未实现 + +**Step 3: 写最小实现** + +- 完成配置结构、启动参数、默认 SQLite DSN + +**Step 4: 运行测试验证通过** + +Run: `go test ./tests/integration -run TestConfigBootstrap -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add cmd internal tests +git commit -m "feat: bootstrap control plane app skeleton" +``` + +### Task 2: 建立状态库与迁移 + +**Files:** +- Create: `internal/store/migrations/0001_init.sql` +- Create: `internal/store/sqlite/db.go` +- Create: `internal/store/sqlite/hosts_repo.go` +- Create: `internal/store/sqlite/packs_repo.go` +- Create: `internal/store/sqlite/providers_repo.go` + +**Step 1: 写迁移与仓储测试** + +- Test: `tests/integration/store_init_test.go` +- 覆盖建表、唯一约束、事务回滚 + +**Step 2: 跑失败测试** + +Run: `go test ./tests/integration -run TestStoreInit -v` +Expected: FAIL,表不存在 + +**Step 3: 实现最小迁移与仓储** + +- 先实现 `hosts`、`packs`、`providers` + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestStoreInit -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/store tests +git commit -m "feat: add state store migrations and repositories" +``` + +### Task 3: 实现宿主适配器与能力探测 + +**Files:** +- Create: `internal/host/sub2api/client.go` +- Create: `internal/host/sub2api/capability_probe.go` +- Create: `internal/host/sub2api/groups.go` +- Create: `internal/host/sub2api/channels.go` +- Create: `internal/host/sub2api/plans.go` +- Create: `internal/host/sub2api/accounts.go` +- Create: `internal/host/sub2api/subscriptions.go` + +**Step 1: 写 `httptest` 宿主桩和适配器测试** + +- Test: `tests/integration/host_stub_test.go` +- 覆盖版本获取、能力探测、错误映射 + +**Step 2: 先跑失败** + +Run: `go test ./tests/integration -run TestSub2APIHostAdapter -v` +Expected: FAIL,适配器未实现 + +**Step 3: 写最小适配器实现** + +- 只支持首版必需 API + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestSub2APIHostAdapter -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/host tests +git commit -m "feat: add sub2api host adapter and capability probe" +``` + +### Task 4: 实现模型包协议与安装校验 + +**Files:** +- Create: `internal/pack/manifest.go` +- Create: `internal/pack/provider_manifest.go` +- Create: `internal/pack/checksum.go` +- Create: `internal/pack/validator.go` +- Create: `internal/pack/loader.go` + +**Step 1: 写模型包校验测试** + +- Test: `tests/integration/install_pack_test.go` +- 覆盖合法包、缺失字段、坏 checksum、版本不兼容 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestInstallPack -v` +Expected: FAIL,包解析和校验未实现 + +**Step 3: 实现最小装载器** + +- 支持从 zip 和目录读取 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestInstallPack -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/pack tests +git commit -m "feat: add model pack loader and validator" +``` + +### Task 5: 实现导入预检与命名策略 + +**Files:** +- Create: `internal/provision/preview_service.go` +- Create: `internal/provision/naming.go` +- Create: `internal/domain/import_batch.go` + +**Step 1: 写预检测试** + +- Test: `tests/integration/import_preview_test.go` +- 覆盖 key 规范化、冲突判定、create/reuse/conflict 输出 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestImportPreview -v` +Expected: FAIL + +**Step 3: 实现预检逻辑** + +- 只做只读分析,不写宿主 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestImportPreview -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/provision internal/domain tests +git commit -m "feat: add provider import preview flow" +``` + +### Task 6: 实现多 key 导入、探测与回滚 + +**Files:** +- Create: `internal/provision/import_service.go` +- Create: `internal/provision/rollback_service.go` +- Create: `internal/store/sqlite/import_batches_repo.go` +- Create: `internal/store/sqlite/resources_repo.go` +- Create: `internal/store/sqlite/probe_results_repo.go` + +**Step 1: 写导入测试** + +- Test: `tests/integration/import_keys_test.go` +- 覆盖 `strict` 成功、`strict` 回滚、`partial` 部分成功 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestImportKeys -v` +Expected: FAIL + +**Step 3: 实现导入与回滚服务** + +- 创建宿主资源 +- 记录资源映射 +- 运行账号测试和模型探测 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestImportKeys -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/provision internal/store tests +git commit -m "feat: add multi-key import and rollback flow" +``` + +### Task 7: 实现访问闭环 + +**Files:** +- Create: `internal/access/planner.go` +- Create: `internal/access/subscription_service.go` +- Create: `internal/access/self_service_checker.go` +- Create: `internal/domain/access_closure.go` + +**Step 1: 写访问闭环测试** + +- Test: `tests/integration/access_closure_test.go` +- 覆盖 `subscription_ready`、`self_service_ready`、`broken` + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestAccessClosure -v` +Expected: FAIL + +**Step 3: 实现最小闭环服务** + +- 首版先保证 `subscription` 模式打通 +- `self-service` 只做绑定状态检查,不代用户签发 key + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestAccessClosure -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/access internal/domain tests +git commit -m "feat: add access closure services" +``` + +### Task 8: 实现对账与漂移检测 + +**Files:** +- Create: `internal/reconcile/runner.go` +- Create: `internal/reconcile/drift_checker.go` +- Create: `internal/reconcile/probe_runner.go` +- Create: `internal/store/sqlite/reconcile_runs_repo.go` + +**Step 1: 写对账测试** + +- Test: `tests/integration/reconcile_test.go` +- 覆盖正常、资源丢失、账号失效、访问闭环破坏 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestReconcile -v` +Expected: FAIL + +**Step 3: 实现对账器** + +- 拉取宿主资源快照 +- 比对状态库 +- 重新执行探测 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestReconcile -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/reconcile internal/store tests +git commit -m "feat: add provider reconcile and drift detection" +``` + +### Task 9: 暴露 HTTP API 与 OpenAPI 文档 + +**Files:** +- Create: `internal/api/http/router.go` +- Create: `internal/api/http/middleware.go` +- Create: `internal/api/http/hosts_handler.go` +- Create: `internal/api/http/packs_handler.go` +- Create: `internal/api/http/providers_handler.go` +- Create: `internal/api/http/imports_handler.go` +- Create: `internal/api/http/access_handler.go` +- Create: `internal/api/http/reconcile_handler.go` +- Create: `docs/api/openapi.yaml` + +**Step 1: 写 handler 测试** + +- Test: `tests/integration/http_api_test.go` +- 覆盖主要成功与失败路径 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestHTTPAPI -v` +Expected: FAIL + +**Step 3: 实现路由和 DTO** + +- 保持 handler 只做参数解析和错误映射 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestHTTPAPI -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add internal/api docs/api tests +git commit -m "feat: add control plane HTTP API" +``` + +### Task 10: 增加调度器、CLI 和端到端验收脚本 + +**Files:** +- Create: `internal/worker/scheduler.go` +- Create: `internal/worker/jobs.go` +- Modify: `cmd/cli/main.go` +- Create: `scripts/e2e/verify_with_host_stub.sh` + +**Step 1: 写调度与 CLI 测试** + +- Test: `tests/integration/cli_scheduler_test.go` +- 覆盖手动 reconcile 和定时调度 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestCLIScheduler -v` +Expected: FAIL + +**Step 3: 实现最小调度与 CLI** + +- 支持 `host add`、`pack install`、`provider import`、`reconcile run` + +**Step 4: 跑全量测试** + +Run: `go test ./...` +Expected: PASS + +**Step 5: Commit** + +```bash +git add cmd internal scripts tests +git commit -m "feat: add scheduler cli and e2e verification script" +``` + +### Task 11: 补齐独立交付物与部署文档 + +**Files:** +- Create: `Dockerfile` +- Create: `.env.example` +- Create: `deploy/docker-compose.yml` +- Create: `docs/deployment.md` +- Modify: `README.md` + +**Step 1: 写交付物检查测试** + +- Test: `tests/integration/distribution_smoke_test.go` +- 覆盖配置样例、镜像启动参数、CLI 帮助输出 + +**Step 2: 跑失败** + +Run: `go test ./tests/integration -run TestDistributionSmoke -v` +Expected: FAIL + +**Step 3: 实现最小交付物** + +- 提供可运行镜像 +- 提供本地 SQLite 默认部署 +- 文档明确对接宿主所需环境变量 + +**Step 4: 跑测试** + +Run: `go test ./tests/integration -run TestDistributionSmoke -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add Dockerfile .env.example deploy docs README.md tests +git commit -m "docs: add distribution artifacts and deployment guide" +``` + +## 11. 最终验收清单 + +- 从空状态启动控制面 +- 连接一套兼容版本的 `sub2api` +- 安装 `openai-cn-pack` +- 导入 `deepseek` 两个 key +- 成功创建宿主资源并记录映射 +- 至少一个账号通过测试与模型探测 +- 至少一种用户访问模式验证成功 +- 使用宿主标准 API 成功调用国产模型 +- 人工删除一个宿主 account 后,对账将 provider 状态标记为 `drifted` +- 执行批次回滚后,宿主残留资源清理完成 + +## 12. 风险与收敛策略 + +- 宿主管理 API 漂移风险:所有 HTTP 路径和字段通过 `HostAdapter` 封装,并为版本差异保留适配层 +- 宿主权限模型不稳定:在 `POST /api/hosts` 阶段强制能力探测,不满足即拒绝接入 +- 不同 provider 规则差异:统一约束到 `providers/*.json`,首版只支持 OpenAI-compatible provider +- 访问闭环误判:首版必须落真实标准 API 探测,不能只以资源存在判定成功 diff --git a/internal/.gitkeep b/internal/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packs/openai-cn-pack/README.md b/packs/openai-cn-pack/README.md new file mode 100644 index 00000000..85c57deb --- /dev/null +++ b/packs/openai-cn-pack/README.md @@ -0,0 +1,18 @@ +# openai-cn-pack + +这是 `sub2api-cn-relay-manager` 的最小模型包样例。 + +它不是宿主原生插件,而是一个可被控制面读取的 `model_pack`,用于描述国产模型 provider 的默认接入模板、默认模型映射、默认套餐和导入约束。 + +当前目录仅提供协议样例: + +- `pack.json.example` +- `providers/deepseek.json.example` + +后续真实交付时,可以扩展更多 provider: + +- `kimi.json` +- `qwen.json` +- `glm.json` +- `minimax.json` + diff --git a/packs/openai-cn-pack/pack.json.example b/packs/openai-cn-pack/pack.json.example new file mode 100644 index 00000000..55147089 --- /dev/null +++ b/packs/openai-cn-pack/pack.json.example @@ -0,0 +1,10 @@ +{ + "pack_id": "openai-cn-pack", + "version": "1.0.0", + "vendor": "YourTeam", + "target_host": "sub2api", + "min_host_version": "0.1.126", + "max_host_version": "0.2.x", + "providers_dir": "providers", + "checksum_file": "checksums.txt" +} diff --git a/packs/openai-cn-pack/providers/deepseek.json.example b/packs/openai-cn-pack/providers/deepseek.json.example new file mode 100644 index 00000000..6781f960 --- /dev/null +++ b/packs/openai-cn-pack/providers/deepseek.json.example @@ -0,0 +1,37 @@ +{ + "provider_id": "deepseek", + "display_name": "DeepSeek OpenAI Compatible", + "base_url": "https://api.deepseek.com", + "platform": "openai", + "account_type": "api", + "default_models": [ + "deepseek-chat", + "deepseek-reasoner" + ], + "smoke_test_model": "deepseek-chat", + "group_template": { + "name": "DeepSeek 默认分组", + "subscription_type": "subscription", + "rate_multiplier": 1.0 + }, + "channel_template": { + "name": "DeepSeek 默认渠道", + "billing_model_source": "channel_mapped", + "restrict_models": true, + "model_mapping": { + "deepseek-chat": "deepseek-chat", + "deepseek-reasoner": "deepseek-reasoner" + } + }, + "plan_template": { + "name": "DeepSeek 默认套餐", + "price": 19.9, + "validity_days": 30, + "for_sale": true + }, + "import": { + "supports_multi_key": true, + "supports_strict": true, + "supports_partial": true + } +} diff --git a/web/.gitkeep b/web/.gitkeep new file mode 100644 index 00000000..e69de29b