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

183 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 拥有 `normalizeModelId``normalizeTransport``normalizeConfig``prepareRuntimeAuth``createStreamFn` 等 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 应逐步收敛为类似结构:
```json
{
"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.
## Related Files
- `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`