Files
sub2api-cn-relay-manager/docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md
2026-05-22 13:38:56 +08:00

12 KiB
Raw Blame History

SPEC: Batch Auto-Import by URL + Key (v2)

日期2026-05-21 技术架构:docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md

1. Objective

V2 的目标不是“又一条导入命令”,而是把这件事做成稳定、可恢复、可追踪的控制面能力:

  1. 上游发现:基于 (base_url, api_key) 自动发现模型,而不是默认信任人工输入
  2. 模型纠错:自动归一化、别名匹配、推荐正确模型名
  3. 兼容画像:记录每个上游和每个模型的兼容能力,避免重复踩坑
  4. 宿主演化:自动创建/更新 channel、account、provider binding
  5. 异步确认:吸收宿主异步 probe、首次 403/503 的预热窗口
  6. 闭环验证:以宿主网关真实 /v1/chat/completions 结果作为最终可用性判断
  7. 结果可视:提供 run 列表、run 详情、item 详情,而不是只靠日志和 artifact

2. Scope

V2 在现有 v1 pack-based 路径旁边新增一条URL + key auto-import 路径。

In Scope

  • 批量输入 (base_url, api_key, requested_models?)
  • 兼容 subscriptionself_service
  • 运行态状态持久化
  • 后台异步确认与有限重试
  • 最小结果 API 与结果页
  • 模型纠错与 capability profile 持久化

Out of Scope

  • 多 key 自动负载均衡
  • 宿主数据库直连
  • 自动价格发现/自动调价
  • 实时 WebSocket 推送
  • 复杂工作台前端

3. Canonical Contract

V2 从这里开始只认一套 canonical contract后续所有文档、API、页面、状态库都必须遵循这套命名。

3.1 ID 规则

  • run_id:一次批量导入任务 ID字符串
  • item_idrun 内单条导入记录 ID字符串
  • provider_id{normalized_host}-{url_hash_last8}

provider_id 生成规则:

  1. 取完整 base_url 规范化后参与计算,不能只取 host
  2. normalized_host 用于可读性
  3. url_hash_last8 用于区分同 host 不同 path

示例:

  • https://api.deepseek.com/v1api-deepseek-9f31c2ab
  • https://api.deepseek.com/proxy/v1api-deepseek-4a2d88f1

3.2 Run 级状态

run.state 固定为:

  • running
  • completed
  • completed_with_warnings
  • failed
  • cancelled

说明:

  • completed_with_warnings 是 run 级总状态
  • 页面可以显示成黄色 badge warning
  • 但 API/状态库里一律写全量枚举值 completed_with_warnings

3.3 Item 级状态

item.current_stage 固定为:

  • probe
  • provision
  • confirm
  • validate
  • done

item.confirmation_status 固定为:

  • pending
  • confirmed
  • advisory
  • failed

item.access_status 固定为:

  • unknown
  • active
  • degraded
  • broken

约束:

  • confirmation_status 只描述“宿主异步窗口是否已确认稳定”
  • access_status 只描述“最终网关真实可用性”
  • Validation Engineaccess_status 的唯一写入方

3.4 Legacy 兼容规则

V1 的 import_batches / import_batch_items / managed_resources 继续保留,但在 V2 中:

  • 仅作为 legacy execution evidence 或资源关联来源
  • 不再作为结果页主数据源
  • V2 结果页/API 只读 import_runs / import_run_items / import_run_item_events

4. Request / Result Contract

4.1 Batch Import Request

BatchImportRunRequest
  - host_id: string
  - mode: "strict" | "partial"
  - access_mode: "subscription" | "self_service"
  - confirm_wait_timeout_sec: int           # CLI/HTTP 可选等待时间
  - entries: []BatchImportEntry
  - subscription_users: []string            # access_mode=subscription 必填
  - subscription_days: int                  # access_mode=subscription 必填
  - probe_api_key: string                   # access_mode=self_service 必填

BatchImportEntry
  - base_url: string
  - api_key: string
  - requested_models: []string              # 可选,仅作为提示

4.2 Access Mode 必填规则

subscription

  • 必填:subscription_users
  • 必填:subscription_days
  • 不接受只写 access_mode=subscription 但不带订阅目标

self_service

  • 必填:probe_api_key
  • probe_api_key 用于最终 gateway access validation

4.3 Batch Import Result

BatchImportRunResult
  - run_id: string
  - state: string
  - total_items: int
  - active_items: int
  - degraded_items: int
  - broken_items: int
  - warning_items: int
  - result_page: string

4.4 Item Projection

BatchImportRunItemView
  - item_id: string
  - base_url: string
  - provider_id: string
  - requested_models: []string
  - raw_models: []string
  - normalized_models: []string
  - resolved_smoke_model: string | null
  - recommended_models: []string
  - current_stage: string
  - confirmation_status: string
  - access_status: string
  - retry_count: int
  - last_retry_at: string | null
  - advisory_messages: []string
  - last_error_stage: string | null
  - last_error: string | null
  - channel_id: int64 | null
  - account_id: int64 | null
  - capability_profile: object

5. Core Pipeline

5.1 Five-stage pipeline

Stage 0: Run Setup
  create import_run + import_run_items
  persist operator input

Stage 1: Probe
  /v1/models
  capability probe
  completion smoke
  normalize aliases

Stage 2: Provision
  find/create channel
  patch model_mapping + model_pricing + restrict_models + billing_model_source
  create/update account
  persist managed resource link

Stage 3: Confirm
  background confirmer absorbs async probe race / warmup window
  writes confirmation_status

Stage 4: Validate
  host gateway real /v1/chat/completions
  writes final access_status

Stage 5: Project
  update run summary
  serve result API / pages

5.2 Ownership boundaries

  • Probe Layer 负责发现和分类,不决定最终 access_status
  • Provision Adapter 负责创建/更新宿主资源
  • Confirmation Engine 负责把瞬时 403/503 吸收到 pending/advisory/failed
  • Validation Engine 负责最终 access_status
  • Result Projection 负责把状态库转换成页面/API 视图

6. Capability Profile

6.1 为什么要分两层

真实场景里兼容能力不是“一个 key 一个总画像”就能表达清楚的。必须拆成:

  1. transport profile:这个 upstream 支不支持 /models/chat/completions/responses/messages
  2. model profiles:这个 upstream 下的具体模型,在 stream/tools/reasoning 字段上是否可用

6.2 Canonical schema

{
  "transport_profile": {
    "supports_openai_models": true,
    "supports_openai_chat_completions": true,
    "supports_openai_responses": false,
    "supports_anthropic_messages": false,
    "auth_style": "bearer",
    "model_id_style": "vendor_prefixed",
    "known_advisories": [
      "responses_unsupported_but_chat_ok",
      "initial_probe_race_expected"
    ]
  },
  "model_profiles": [
    {
      "raw_model_id": "deepseek-ai/DeepSeek-V4-Pro",
      "normalized_model_id": "deepseek-v4-pro",
      "supports_stream": true,
      "supports_tools": "unknown",
      "supports_reasoning_fields": "unknown",
      "smoke_chat_ok": true
    }
  ]
}

6.3 用途

  • 决定是否跳过 /responses
  • 决定是否直接走 raw /chat/completions
  • 决定 warning 文案
  • 决定推荐 smoke model
  • 决定后续快速匹配“哪个模型在哪种兼容层下靠谱”

7. Channel / Account Evolution Contract

V2 不再使用“薄 patch 接口”表达 channel 更新。宿主 patch 必须以完整 contract 表达:

ChannelPatchContract
  - model_mapping: map[string]string
  - model_pricing: map[string]PriceSpec
  - restrict_models: true
  - billing_model_source: "channel_mapped"

约束:

  • model_mapping 同时记录 raw → canonical
  • model_pricing 默认可填零值,但字段必须完整存在
  • patch 不得破坏旧模型
  • PatchChannel(addModels []string) 这类接口不再作为 V2 canonical contract

8. Async Confirmation Mechanism

8.1 为什么 V2 必须有后台 confirmer

V2 的稳定性目标不能建立在“请求线程里顺序 sleep + retry”。必须有独立后台机制推进

  • confirming item
  • 因 probe race 暂时 advisory 的 item
  • 503 no available accounts 等待预热的 item

8.2 Canonical executor

V2 必须实现 ConfirmationWorker

ConfirmationWorker
  - poll import_run_items where current_stage='confirm'
  - condition: next_retry_at <= now
  - acquire lease
  - run confirm logic
  - update item state
  - release lease

8.3 必需字段

import_run_items 至少要有:

  • confirmation_attempts
  • retry_count
  • last_retry_at
  • next_retry_at
  • lease_owner
  • lease_until

8.4 Restart safety

V2 第一版即要求:

  • 进程重启后 unfinished confirm item 会被 worker 重新拾取
  • 页面能看到 item 停在哪个阶段
  • CLI --confirm-wait-timeout 只是“等待窗口”,不是确认机制本身

9. Single Source of Truth

9.1 Canonical runtime tables

V2 运行态只认三类表:

  • import_runs
  • import_run_items
  • import_run_item_events

9.2 Legacy linkage

若某个 V2 item 调用了现有 v1 provision 流程,可在 item 上保留:

  • legacy_batch_id
  • legacy_provider_id

但这些字段仅作为追溯链接,不能替代 V2 状态源。

9.3 Result page data source

结果页/API 只读 V2 canonical tables不直接拼接

  • import_batches
  • probe_results
  • access_closure_records
  • 宿主数据库

10. Result API and Pages

10.1 API

V2 标准 API

POST /api/batch-import/runs
GET  /api/batch-import/runs
GET  /api/batch-import/runs/{run_id}
GET  /api/batch-import/runs/{run_id}/items
GET  /api/batch-import/runs/{run_id}/items/{item_id}

Legacy API /api/import-batches/* 保留,但标为 v1/legacy。

10.2 Pages

/batch-import/runs
/batch-import/runs/{run_id}

结果页必须能直接回答:

  • 哪条 URL 导入成功
  • 哪条卡在 probe/provision/confirm/validate
  • 哪条发生模型纠错
  • 哪条是 advisory 而不是 broken
  • 重试过几次
  • 当前 warning 的原因是什么

11. CLI Contract

go run ./cmd/cli batch-import \
  --host-id "<host_id>" \
  --entry "https://example.com/v1,sk-xxx" \
  --batch-file "./keys.csv" \
  --mode "strict|partial" \
  --access-mode "subscription|self_service" \
  --subscription-users "u1,u2" \
  --subscription-days 30 \
  --probe-api-key "<user_gateway_key>" \
  --confirm-wait-timeout 15s

CLI 输出必须至少包含:

  • run_id
  • result_page
  • 每个 entry 的 resolved_smoke_model
  • capability 摘要
  • confirmation_status
  • access_status
  • 推荐模型名(若发生纠错)

12. Error Policy

Blocking

  • 401/403 unauthorized 且证据表明 key 无效
  • /v1/models 完全不可用且无替代路径
  • provision 明确失败

Advisory

  • 第三方 upstream /responses=403/chat/completions=200
  • 首次 /accounts/:id/test=403,但 probe race 已被识别
  • 首次 /v1/chat/completions=503 no available accounts,且重试后恢复
  • 429 rate_limit

Access status ownership

  • confirmation_status=advisory 不自动等于 access_status=degraded
  • 只有 Validation Engine 可以把 item 标成 active/degraded/broken

13. Success Criteria

  1. access_mode 输入契约完整,subscription / self_service 都可单独落地
  2. run / item 状态、重试、warning、错误阶段能持久化并在重启后恢复可见
  3. 结果页和 API 只读 V2 canonical tables
  4. 模型纠错结果、capability profile、推荐模型名可追溯
  5. 第三方兼容 upstream 的 /responses 误判和宿主异步窗口不会把可用链路直接打成最终失败
  6. 页面可以清楚地区分 confirmed/advisory/failedactive/degraded/broken
  7. OpenAPI、SPEC、TDD、Architecture 对同一字段和同一状态枚举保持一致

14. Non-goals for first implementation

  • 多 key 自动调度
  • 实时推送
  • 自动定价策略
  • 自动负载均衡

15. Final decisions

  1. provider_id 采用 normalized_host + url_hash_last8
  2. requested_models 仅作提示,不作为事实源
  3. Validation Engineaccess_status 唯一写入方
  4. V2 runtime canonical tables 为 import_runs/import_run_items/import_run_item_events
  5. ConfirmationWorker 是 V2 必备组件,不是可选增强