From 3c061f3ddb3fbf6de90b2cfc51929699c353bb5d Mon Sep 17 00:00:00 2001 From: phamnazage-jpg Date: Fri, 29 May 2026 12:29:05 +0800 Subject: [PATCH] feat(routing): add canonical shadow provider pack --- docs/EXECUTION_BOARD.md | 8 +++ docs/ROUTE_LAB_VALIDATION.md | 3 + docs/SHADOW_PROVIDER_VALIDATION.md | 70 +++++++++++++++++++ internal/pack/source_loader_test.go | 20 ++++++ packs/openai-cn-pack-shadow-asxs/README.md | 29 ++++++++ .../openai-cn-pack-shadow-asxs/checksums.txt | 2 + packs/openai-cn-pack-shadow-asxs/pack.json | 10 +++ .../providers/gpt-asxs-shadow-lab.json | 31 ++++++++ 8 files changed, 173 insertions(+) create mode 100644 docs/SHADOW_PROVIDER_VALIDATION.md create mode 100644 packs/openai-cn-pack-shadow-asxs/README.md create mode 100644 packs/openai-cn-pack-shadow-asxs/checksums.txt create mode 100644 packs/openai-cn-pack-shadow-asxs/pack.json create mode 100644 packs/openai-cn-pack-shadow-asxs/providers/gpt-asxs-shadow-lab.json diff --git a/docs/EXECUTION_BOARD.md b/docs/EXECUTION_BOARD.md index dcaa231d..ac97bdf8 100644 --- a/docs/EXECUTION_BOARD.md +++ b/docs/EXECUTION_BOARD.md @@ -108,6 +108,14 @@ - 前端只看到一个逻辑分组 - 插件层先做 route 级 sticky,再把请求稳定转发到某个宿主 shadow group - 宿主继续只做单线路 group 内的 account sticky / 调度 + - 2026-05-29 已基于上述结论新增 canonical shadow pack: + - `packs/openai-cn-pack-shadow-asxs/` + - provider:`gpt-asxs-shadow-lab` + - 当前约束是: + - 一个 route 对应一个独立宿主 shadow group + - 宿主 shadow group 只承载 canonical upstream model:`gpt-5.4`、`gpt-5.4-mini` + - alias/public model 的抽象只保留在插件 `logical_group -> route -> shadow_model` 层,不再下沉到宿主 channel + - 设计与验收路径已单独沉淀到 `docs/SHADOW_PROVIDER_VALIDATION.md` - 2026-05-28 已新增插件整体需求盘点 `docs/PLUGIN_REQUIREMENTS_OVERVIEW_2026-05-28.md` - 已把“增加模型、维护逻辑分组、智能路由、供应商帐号导入与停启用、普通用户前端”五大功能域统一收口 - 并明确区分 `已完成 / 待优化 / 待完成 / 未来规划` diff --git a/docs/ROUTE_LAB_VALIDATION.md b/docs/ROUTE_LAB_VALIDATION.md index 5c8d4404..8a8df7bf 100644 --- a/docs/ROUTE_LAB_VALIDATION.md +++ b/docs/ROUTE_LAB_VALIDATION.md @@ -1,5 +1,8 @@ # Route Lab Validation +> 该文档现在只保留“同 group 多 channel + alias 下沉”失败实验的历史记录。 +> 针对插件前置路由架构的当前正确方向,请改看 [docs/SHADOW_PROVIDER_VALIDATION.md](/home/long/project/sub2api-cn-relay-manager/docs/SHADOW_PROVIDER_VALIDATION.md)。 + 本文件记录 `asxs + codex2api` 同组双线路实验的最小验证路径。 ## 目标 diff --git a/docs/SHADOW_PROVIDER_VALIDATION.md b/docs/SHADOW_PROVIDER_VALIDATION.md new file mode 100644 index 00000000..fade427c --- /dev/null +++ b/docs/SHADOW_PROVIDER_VALIDATION.md @@ -0,0 +1,70 @@ +# Shadow Provider Validation + +本文件记录插件前置路由架构下,`shadow provider` 的最小正确验证路径。 + +## 目标 + +验证以下结论是否成立: + +1. 插件层可以继续维护 `logical_group/public_model` +2. 宿主层的 `shadow group` 只承载 canonical upstream model +3. 真实 managed key 能通过宿主 `/v1/models` 与 `/v1/chat/completions` +4. 旧的 alias/public model 不再下沉到 stock host 的 `channel_mapped + restrict_models` 组合 + +## 设计结论 + +旧的 `route-lab` 已证明两件事: + +1. 当前宿主不支持“多个 channel 复用同一个 group” +2. 在 `weishaw/sub2api:0.1.129` 上,把 alias 继续下沉到宿主 shadow group,会触发 `channel pricing restriction` + +因此新的 shadow provider 约束是: + +```text +plugin logical group + -> route + -> shadow group (host) + -> canonical models only +``` + +## 当前实验 pack + +- pack 目录:`packs/openai-cn-pack-shadow-asxs` +- provider:`gpt-asxs-shadow-lab` + +## 实验建模 + +```text +Shadow Group: GPT ASXS Shadow 实验 + Channel: GPT ASXS Shadow + Models: + - gpt-5.4 + - gpt-5.4-mini +``` + +关键点: + +- shadow group 不再暴露 `gpt-5.4-asxs` +- 插件 route 层如果需要 alias,必须在 `logical_group_route_models.shadow_model` 之外处理 +- stock host 只看到 canonical 模型 + +## 建议验证顺序 + +1. 导入 `gpt-asxs-shadow-lab` +2. 记录 `group_id / channel_id / account_id` +3. 用宿主订阅 key 直接验证: + - `GET /v1/models` + - `POST /v1/chat/completions` +4. 再用插件管理面创建临时 `logical_group / route` +5. 调 `POST /api/routing/proxy/chat/completions` +6. 核对: + - `effective_gateway_key_source=managed_subscription` + - `forward.upstream_status=200` + - `route_decision_logs` 已写回同一 `request_id` + +## 成功标准 + +- managed key `/v1/models` 返回 canonical model +- managed key `/v1/chat/completions` 返回 `200` +- 插件 `route proxy` 走真实 shadow group 时返回 `200` +- 不再出现 `channel pricing restriction` diff --git a/internal/pack/source_loader_test.go b/internal/pack/source_loader_test.go index 9a5dba90..6ae16fe9 100644 --- a/internal/pack/source_loader_test.go +++ b/internal/pack/source_loader_test.go @@ -49,6 +49,26 @@ func TestLoadPathIncludesFirstBatchOfficialProviders(t *testing.T) { } } +func TestLoadPathSupportsShadowASXSPack(t *testing.T) { + loaded, err := LoadPath(filepath.Join("..", "..", "packs", "openai-cn-pack-shadow-asxs")) + if err != nil { + t.Fatalf("LoadPath(shadow-asxs-pack) error = %v", err) + } + if loaded.Manifest.PackID != "openai-cn-pack-shadow-asxs" { + t.Fatalf("PackID = %q, want %q", loaded.Manifest.PackID, "openai-cn-pack-shadow-asxs") + } + if len(loaded.Providers) != 1 { + t.Fatalf("Providers len = %d, want 1", len(loaded.Providers)) + } + provider := loaded.Providers[0] + if provider.ProviderID != "gpt-asxs-shadow-lab" { + t.Fatalf("ProviderID = %q, want %q", provider.ProviderID, "gpt-asxs-shadow-lab") + } + if len(provider.DefaultModels) != 2 || provider.DefaultModels[0] != "gpt-5.4" { + t.Fatalf("DefaultModels = %v, want canonical gpt-5.4 models", provider.DefaultModels) + } +} + func TestLoadPathSupportsZipArchive(t *testing.T) { tempDir := t.TempDir() archivePath := filepath.Join(tempDir, "openai-cn-pack.zip") diff --git a/packs/openai-cn-pack-shadow-asxs/README.md b/packs/openai-cn-pack-shadow-asxs/README.md new file mode 100644 index 00000000..5a62b521 --- /dev/null +++ b/packs/openai-cn-pack-shadow-asxs/README.md @@ -0,0 +1,29 @@ +# openai-cn-pack-shadow-asxs + +这是一个面向插件前置路由架构的 **shadow provider** 实验 pack。 + +它与旧的 `openai-cn-pack-route-lab` 有两个关键差异: + +- 不再尝试让多个 route 复用同一个宿主 group +- 不再把 route alias 下沉到宿主 shadow group + +当前 pack 只包含一条 canonical shadow provider: + +- `gpt-asxs-shadow-lab` + +它的设计约束是: + +- 一个 route 对应一个独立的宿主 shadow group +- 宿主 shadow group 只承载 canonical upstream model +- 插件层负责 `logical_group/public_model -> route -> shadow_model` 映射 +- 宿主层只负责该 shadow group 内的账号调度与 managed key 验证 + +当前期望模型: + +- `gpt-5.4` +- `gpt-5.4-mini` + +注意: + +- `public model alias` 的抽象要保留在插件 `logical_group` 层,不要再写进宿主 shadow provider +- 后续如果要增加 `codex2api` shadow route,应新增独立 shadow provider / shadow group,而不是与 `asxs` 共用同一个宿主 group diff --git a/packs/openai-cn-pack-shadow-asxs/checksums.txt b/packs/openai-cn-pack-shadow-asxs/checksums.txt new file mode 100644 index 00000000..d135efde --- /dev/null +++ b/packs/openai-cn-pack-shadow-asxs/checksums.txt @@ -0,0 +1,2 @@ +ea12d19da5281c593b3710b9ea9e9fd3ebd3e092ad0f738b4a2e87e65a0f8fc7 pack.json +b2f1765709a4aefc9d5d77896fb6f00ce5e11fe70cc8a80c5d09eb0135ce9dfb providers/gpt-asxs-shadow-lab.json diff --git a/packs/openai-cn-pack-shadow-asxs/pack.json b/packs/openai-cn-pack-shadow-asxs/pack.json new file mode 100644 index 00000000..9bd52a3d --- /dev/null +++ b/packs/openai-cn-pack-shadow-asxs/pack.json @@ -0,0 +1,10 @@ +{ + "pack_id": "openai-cn-pack-shadow-asxs", + "version": "0.1.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-shadow-asxs/providers/gpt-asxs-shadow-lab.json b/packs/openai-cn-pack-shadow-asxs/providers/gpt-asxs-shadow-lab.json new file mode 100644 index 00000000..66b8c5df --- /dev/null +++ b/packs/openai-cn-pack-shadow-asxs/providers/gpt-asxs-shadow-lab.json @@ -0,0 +1,31 @@ +{ + "provider_id": "gpt-asxs-shadow-lab", + "display_name": "GPT asxs shadow 实验", + "base_url": "https://api.asxs.top/v1", + "platform": "openai", + "account_type": "apikey", + "default_models": ["gpt-5.4", "gpt-5.4-mini"], + "smoke_test_model": "gpt-5.4", + "group_template": { + "name": "GPT ASXS Shadow 实验", + "rate_multiplier": 1.0 + }, + "channel_template": { + "name": "GPT ASXS Shadow", + "model_mapping": { + "gpt-5.4": "gpt-5.4", + "gpt-5.4-mini": "gpt-5.4-mini" + } + }, + "plan_template": { + "name": "GPT ASXS Shadow 实验套餐", + "price": 19.9, + "validity_days": 30, + "validity_unit": "day" + }, + "import": { + "supports_multi_key": true, + "supports_strict": true, + "supports_partial": true + } +}