Files
planet/docs/plans/ai-provider-openclaw-style-routing-plan.md
linkong fbca381512
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.62.0
2026-05-21 01:37:32 +08:00

8.2 KiB
Raw Blame History

AI Provider OpenClaw-Style Routing Plan

Last updated: 2026-05-20

Summary

Planet 的 AI Provider 路由要从“运行时识别特殊 provider / 特殊模型”收敛到 OpenClaw 风格的配置驱动模型:模型引用、协议、鉴权、轻量探测、真实调用和模型级例外都由 provider catalog / preset / runtime metadata 描述,运行时只解释这些元数据,不再散落 if provider == ... and model == ... 这类硬编码。

这份计划覆盖 Admin Next 的 Provider 配置体验、backend settings API、aiprovider 适配服务和未来模型目录同步方式。目标是让 OpenCode Go、MiniMax、DeepSeek、OpenAI-compatible、Anthropic-compatible、Ollama、OpenRouter / One API 类代理都能用同一套规则扩展。

Background

当前实现已经完成了两步临时修正:

  • OpenCode Go 模型目录不再使用普通 Zen free 列表,而是使用 https://opencode.ai/zen/go/v1/models
  • minimax-m2.7 / minimax-m2.5 的协议例外已从 aiprovider 运行逻辑移到 model_provider_apis 元数据中。

但整体还没有完全达到 OpenClaw 式结构。OpenClaw 的关键思想是:

  • 模型引用使用 provider/model,由 provider 前缀确定 runtime provider。
  • provider 插件或 catalog 拥有 normalizeModelIdnormalizeTransportnormalizeConfigprepareRuntimeAuthcreateStreamFn 等 provider 行为。
  • 主推理循环不认识具体模型名,只使用解析后的 provider config、transport 和 request adapter。
  • 上游网关能自己路由时,尽量透传 provider routing metadata不在本地复制上游逻辑。

Planet 不需要完整复制 OpenClaw 插件系统,但需要学习它的边界划分。

Design Principles

  • Provider catalog 是路由事实来源runtime 不是。
  • 模型级协议例外必须是 metadata例如 model_provider_apis,不能是 Python set / if 分支。
  • 轻量连通性测试只验证网络、鉴权和模型目录,不发真实 prompt。
  • 真实模型调用只发生在 Playground、AI brief、分析任务等明确需要生成的路径。
  • 保存配置不自动设为默认,不自动触发连接测试;保存、设默认、测试三种按钮职责分离。
  • 目录刷新使用增量合并语义:发现新模型,标记旧模型 stale不直接删除用户选择或自定义模型。
  • 如果 provider 不提供可靠 /models,可以用内置 preset 确认已知模型,但 UI 必须说清楚这是 preset confirmation不是假装 provider 返回了目录。

Target Data Model

Provider preset / runtime config 应逐步收敛为类似结构:

{
  "provider": "opencode-go",
  "label": "OpenCode Go",
  "default_transport": "openai-completions",
  "base_url": "https://opencode.ai/zen/go/v1",
  "auth": {
    "type": "bearer",
    "api_key_env": "OPENCODE_GO_API_KEY"
  },
  "models": [
    {
      "id": "glm-5.1",
      "label": "GLM 5.1",
      "transport": "openai-completions",
      "context_window": null,
      "capabilities": ["text"]
    },
    {
      "id": "minimax-m2.7",
      "label": "MiniMax M2.7",
      "transport": "anthropic-messages",
      "capabilities": ["text", "reasoning"]
    }
  ],
  "discovery": {
    "type": "openai-models",
    "url": "https://opencode.ai/zen/go/v1/models",
    "auth": "provider-api-key"
  }
}

Runtime 选择规则:

  1. 解析 provider。
  2. 解析 model。
  3. models[].transport 找模型级 transport。
  4. 若没有模型级 transport使用 provider default_transport
  5. 将解析结果传给 aiprovider
  6. aiprovider 只按 transport 组装请求,不认识 provider 专属模型名。

Implementation Plan

Phase 1: Stabilize Current Metadata Path

  • Keep model_provider_apis as the immediate compatibility bridge.
  • Ensure _provider_defaults() includes provider metadata such as model_provider_apis.
  • Ensure _runtime_config_from_ai_payload() sends the resolved metadata through AIProviderClient.
  • Ensure AIProviderClient forwards metadata to aiprovider with a structured header.
  • Ensure aiprovider.ProviderService reads model metadata and resolves provider_api = model_provider_apis[model] ?? provider_api.
  • Add tests proving aiprovider does not contain provider/model-specific literals for routing decisions.

Phase 2: Replace model_provider_apis With Structured Model Catalog

  • Extend backend/app/services/llm_provider_catalog.py preset shape with models_metadata.
  • Preserve old models as a compatibility list for the UI.
  • Add helpers:
    • get_provider_model_metadata(provider, model)
    • resolve_provider_transport(provider_config, model)
    • merge_discovered_models(existing, discovered)
  • Return both models and models_metadata from refresh endpoints.
  • Admin Next should render model labels, capabilities and transport hints from metadata.

Phase 3: Provider Discovery And Incremental Sync

  • Add provider discovery descriptors:
    • OpenAI-compatible /models
    • Anthropic-compatible no-models / preset-confirmed path
    • Ollama /api/tags
    • OpenCode Go /zen/go/v1/models
    • OpenRouter / One API passthrough model discovery
  • Add incremental merge behavior:
    • New discovered model: add.
    • Existing discovered model: update last_seen_at, metadata.
    • Missing discovered model: mark stale, do not delete.
    • User custom model: keep unless explicitly removed.
  • Surface discovery source in Admin Next: 实时发现 / 内置预设 / 用户自定义 / 已过期.

Phase 4: Transport Adapters

  • Replace provider-specific request decisions with adapter descriptors:
    • openai-completions
    • anthropic-messages
    • ollama-generate
    • future openai-responses
    • future gemini-generate-content
  • Each adapter owns:
    • path
    • auth header format
    • request body transform
    • response text extraction
    • reasoning/thinking block extraction
    • models endpoint strategy
  • ProviderService.analyze() should select adapter by resolved transport and call the adapter.

Phase 5: Admin Next UX

  • Model provider page should show:
    • provider status tag
    • default model tag
    • source tag: env / runtime / preset / discovered
    • model list with transport/capability hint
    • separate buttons for save, set default, refresh model catalog, lightweight test
  • The connect plug button remains lightweight.
  • Full generation test lives only in Playground or a clearly named “试运行” action.
  • If lightweight test falls back to preset confirmation, toast must say so explicitly.

TODO

  • Add models_metadata to provider presets and refresh responses.
  • Add runtime resolver helper for provider/model transport selection.
  • Remove any remaining provider/model-specific literals from aiprovider runtime routing.
  • Add tests that opencode-go/minimax-m2.7 resolves through metadata, not through runtime hardcode.
  • Add tests for lightweight connectivity:
    • 401 / 403 fail as auth error.
    • 404 with known preset model passes as preset-confirmed.
    • /models missing alias passes only when preset contains the alias.
    • unknown model fails.
  • Add discovery descriptors for OpenCode Go, OpenAI-compatible, Anthropic-compatible, Ollama, OpenRouter / One API.
  • Add incremental model catalog merge semantics with stale marking.
  • Update Admin Next model list to show model source, transport and capability.
  • Keep save / set default / lightweight test / full test as separate actions.
  • Document the final provider catalog schema in technical docs after implementation.

Current Acceptance Criteria

  • No runtime routing branch may depend on concrete model names like minimax-m2.7.
  • OpenCode Go model refresh must not use the ordinary Zen free-model endpoint.
  • Lightweight connect must not call analyze() or consume generation quota.
  • Saving a provider must not automatically set it as default.
  • Provider UI must distinguish configured key, fallback key, preset model and live-discovered model.
  • backend/app/services/llm_provider_catalog.py
  • backend/app/api/v1/settings.py
  • backend/app/services/ai_client.py
  • aiprovider/main.py
  • aiprovider/provider_service.py
  • frontend/src/admin-next/pages/PlainResourcePages.tsx
  • docs/plans/admin-next-parity-audit-closeout-plan.md