1
0
Files
nex/openspec/specs/conversion-engine/spec.md
lanyuanxiaoyao 56ecc73d1b docs: 整合 openspec 规范,合并配置和前端相关独立 spec
将 cli-config、config-priority、env-config 合并入 config-management;
将 tdesign-integration、recharts-integration、frontend-config-ui、
frontend-testing、stats-dashboard 合并入新的 frontend/spec.md;
清理其余 spec 中的冗余标记,补充缺失场景。
2026-04-20 19:55:56 +08:00

280 lines
12 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.
# Conversion Engine
## Purpose
定义协议无关的 Canonical Model 和 ConversionEngine 转换引擎,作为所有协议间请求/响应转换的统一枢纽。
### Requirement: 定义 CanonicalRequest 规范模型
系统 SHALL 定义协议无关的 `CanonicalRequest` 结构体,作为所有协议间请求转换的统一枢纽。
CanonicalRequest SHALL 包含以下字段:
- `model`: 字符串,模型名称
- `system`: 可选,字符串或 SystemBlock 数组
- `messages`: CanonicalMessage 数组
- `tools`: 可选CanonicalTool 数组
- `tool_choice`: 可选ToolChoice 联合体
- `parameters`: RequestParameters
- `thinking`: 可选ThinkingConfig
- `stream`: 布尔值
- `user_id`: 可选字符串
- `output_format`: 可选OutputFormat
- `parallel_tool_use`: 可选布尔值
#### Scenario: CanonicalRequest 包含所有公共字段
- **WHEN** 从任意协议解码请求为 CanonicalRequest
- **THEN** CanonicalRequest SHALL 包含协议间可映射的所有公共语义字段
- **THEN** 协议特有字段 SHALL NOT 出现在 CanonicalRequest 中
#### Scenario: System 消息独立存储
- **WHEN** 协议使用顶层 system 字段(如 Anthropic
- **THEN** SHALL 提取为 `canonical.system`
- **WHEN** 协议使用 messages 数组中的 system 角色(如 OpenAI
- **THEN** SHALL 从 messages 中提取为 `canonical.system`
### Requirement: 定义 CanonicalMessage 和 ContentBlock
系统 SHALL 定义 `CanonicalMessage` 结构体和 `ContentBlock` 联合体。
CanonicalMessage SHALL 包含 `role`枚举system/user/assistant/tool`content`ContentBlock 数组)。
ContentBlock SHALL 支持以下类型:
- `text`: 文本内容块
- `tool_use`: 工具调用块id, name, input
- `tool_result`: 工具结果块tool_use_id, content, is_error
- `thinking`: 思考内容块thinking
#### Scenario: ContentBlock 类型化表示
- **WHEN** 编码 ContentBlock
- **THEN** SHALL 使用 `type` 字段标识块类型
- **THEN** 不同类型 SHALL 携带各自特有的字段
#### Scenario: Tool 输入保留原始 JSON
- **WHEN** 解码工具调用的 input 字段
- **THEN** SHALL 使用 `json.RawMessage` 保留原始 JSON 数据
- **THEN** SHALL NOT 强制解析为 map 或 struct
### Requirement: 定义 CanonicalTool 和 ToolChoice
系统 SHALL 定义 `CanonicalTool`name, description, input_schema`ToolChoice` 联合体auto/none/any/tool+name
#### Scenario: ToolChoice 覆盖所有语义
- **WHEN** 协议使用 `"required"` 表示必须调用工具(如 OpenAI
- **THEN** SHALL 映射为 `{type: "any"}`
- **WHEN** 协议使用 `{type: "tool", name}` 指定工具
- **THEN** SHALL 保持 `{type: "tool", name}` 映射
### Requirement: 定义 CanonicalResponse 规范模型
系统 SHALL 定义 `CanonicalResponse` 结构体,包含 `id``model``content`ContentBlock 数组)、`stop_reason`(可选枚举)、`usage`CanonicalUsage
CanonicalUsage SHALL 包含 `input_tokens``output_tokens`、可选的 `cache_read_tokens``cache_creation_tokens``reasoning_tokens`
stop_reason 枚举 SHALL 包含:`end_turn``max_tokens``tool_use``stop_sequence``content_filter``refusal``pause_turn`
#### Scenario: 响应内容块与请求共用类型
- **WHEN** 编码响应中的 content
- **THEN** SHALL 使用与请求相同的 ContentBlock 类型系统
- **THEN** TextBlock 和 ToolUseBlock SHALL 在请求和响应中通用
### Requirement: 定义 CanonicalStreamEvent 联合体
系统 SHALL 定义类型化的 CanonicalStreamEvent 联合体,包含显式的 start/stop 生命周期。
事件类型 SHALL 包含:
- `message_start`: 包含 messageid, model, usage
- `content_block_start`: 包含 index 和 content_block
- `content_block_delta`: 包含 index 和 deltatext_delta/input_json_delta/thinking_delta
- `content_block_stop`: 包含 index
- `message_delta`: 包含 deltastop_reason和 usage
- `message_stop`: 无额外数据
- `error`: 包含 errortype, message
- `ping`: 无额外数据
#### Scenario: 流式事件具有完整生命周期
- **WHEN** 解码协议的 SSE 流为 CanonicalStreamEvent
- **THEN** SHALL 包含 message_start → content_block_start/delta/stop → message_delta → message_stop 的完整生命周期
- **THEN** 每个事件 SHALL 包含足够的信息用于编码为任意目标协议的 SSE 格式
### Requirement: 定义 ProtocolAdapter 接口
系统 SHALL 定义 `ProtocolAdapter` 接口,每种协议实现完整适配。
ProtocolAdapter SHALL 包含以下方法:
- `protocolName()`: 返回协议唯一标识
- `protocolVersion()`: 返回协议版本
- `supportsPassthrough()`: 返回是否支持同协议透传
- `detectInterfaceType(nativePath)`: 根据协议的 URL 路径识别接口类型
- `buildUrl(nativePath, interfaceType)`: 映射 URL 路径
- `buildHeaders(provider)`: 构建认证和必要 Header
- `supportsInterface(interfaceType)`: 判断是否支持某接口类型
- `decodeRequest(raw)`: 协议格式 → CanonicalRequest
- `encodeRequest(canonical, provider)`: CanonicalRequest → 协议格式
- `decodeResponse(raw)`: 协议格式 → CanonicalResponse
- `encodeResponse(canonical)`: CanonicalResponse → 协议格式
- `createStreamDecoder()`: 创建 StreamDecoder 实例
- `createStreamEncoder()`: 创建 StreamEncoder 实例
- `encodeError(error)`: ConversionError → 协议错误格式
- 扩展层编解码方法Models/Embeddings/Rerank
#### Scenario: Adapter 注册到 Registry
- **WHEN** 应用启动
- **THEN** SHALL 将所有 Adapter 注册到 AdapterRegistry
- **THEN** 可通过 `registry.get(protocolName)` 获取 Adapter
#### Scenario: Adapter 自描述接口能力
- **WHEN** 调用 `supportsInterface(interfaceType)`
- **THEN** SHALL 返回布尔值表示是否支持该接口类型
- **THEN** 不支持的接口 SHALL 由引擎走透传逻辑
#### Scenario: Adapter 识别接口类型
- **WHEN** 调用 `detectInterfaceType(nativePath)`
- **THEN** SHALL 根据协议的 URL 路径模式识别接口类型
- **THEN** SHALL 返回对应的 InterfaceTypeCHAT/MODELS/MODEL_INFO/EMBEDDINGS/RERANK
- **THEN** 无法识别的路径 SHALL 返回 PASSTHROUGH 类型
### Requirement: 实现 AdapterRegistry
系统 SHALL 实现 `AdapterRegistry`,支持 Adapter 的注册和查询。
#### Scenario: 注册 Adapter
- **WHEN** 调用 `registry.register(adapter)`
- **THEN** SHALL 以 adapter 的 protocolName 为 key 存储
- **THEN** 重复注册同名协议 SHALL 返回错误
#### Scenario: 查询 Adapter
- **WHEN** 调用 `registry.get(protocolName)`
- **THEN** SHALL 返回已注册的 ProtocolAdapter
- **THEN** 未注册的协议 SHALL 返回错误
### Requirement: 实现 ConversionEngine 门面
系统 SHALL 实现 `ConversionEngine` 作为无状态的转换引擎门面,线程安全、可复用。
ConversionEngine SHALL 提供:
- `registerAdapter(adapter)`: 注册协议适配器
- `use(middleware)`: 注册转换中间件
- `isPassthrough(clientProtocol, providerProtocol)`: 判断是否同协议透传
- `convertHttpRequest(request, clientProtocol, providerProtocol, provider)`: 请求转换
- `convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType)`: 响应转换
- `createStreamConverter(clientProtocol, providerProtocol, provider)`: 创建流式转换器
#### Scenario: 跨协议 Chat 请求转换
- **WHEN** clientProtocol != providerProtocol 且 interfaceType == CHAT
- **THEN** SHALL 使用 clientAdapter.detectInterfaceType 识别接口类型
- **THEN** SHALL 使用 clientAdapter.decodeRequest 解码为 CanonicalRequest
- **THEN** SHALL 经 middlewareChain 处理
- **THEN** SHALL 使用 providerAdapter.encodeRequest 编码为目标协议格式
- **THEN** SHALL 使用 providerAdapter.buildUrl 映射目标 URL
- **THEN** SHALL 使用 providerAdapter.buildHeaders 构建目标 Header
- **THEN** SHALL 返回 HTTPRequestSpec{URL, Method, Headers, Body}
#### Scenario: 同协议透传
- **WHEN** clientProtocol == providerProtocol 且 supportsPassthrough == true
- **THEN** SHALL 使用 providerAdapter.buildHeaders 重建 Header
- **THEN** SHALL 原样传递请求 Body
- **THEN** SHALL NOT 执行 decode/encode 转换
#### Scenario: 未知接口透传
- **WHEN** clientAdapter.detectInterfaceType 返回 PASSTHROUGH 类型
- **THEN** SHALL 使用 providerAdapter.buildUrl 和 buildHeaders 适配 URL 和 Header
- **THEN** SHALL 原样传递请求 Body
### Requirement: 定义 StreamDecoder/StreamEncoder/StreamConverter 接口
系统 SHALL 定义流式转换的三层接口。
StreamDecoder SHALL 接口:
- `processChunk(rawChunk)`: 原始字节 → CanonicalStreamEvent 数组
- `flush()`: 刷新缓冲区 → CanonicalStreamEvent 数组
StreamEncoder SHALL 接口:
- `encodeEvent(event)`: CanonicalStreamEvent → SSE 字节数组
- `flush()`: 刷新缓冲区 → SSE 字节数组
StreamConverter SHALL 接口:
- `processChunk(rawChunk)`: 原始字节 → SSE 字节数组
- `flush()`: 刷新 → SSE 字节数组
#### Scenario: PassthroughStreamConverter
- **WHEN** 同协议透传时
- **THEN** SHALL 直接传递原始 SSE 字节,不做任何解析或转换
#### Scenario: CanonicalStreamConverter
- **WHEN** 跨协议转换时
- **THEN** SHALL 使用 providerAdapter 的 StreamDecoder 解码原始 SSE
- **THEN** SHALL 经 middlewareChain 处理事件
- **THEN** SHALL 使用 clientAdapter 的 StreamEncoder 编码为目标 SSE 格式
### Requirement: 定义 ConversionMiddleware 接口
系统 SHALL 定义 `ConversionMiddleware` 接口,用于在 decode→encode 之间拦截转换。
- `intercept(canonical, clientProtocol, providerProtocol, context)`: 修改或拒绝 Canonical
- `interceptStreamEvent(event, clientProtocol, providerProtocol, context)`: 修改或拒绝流式事件
#### Scenario: Middleware 链式执行
- **WHEN** 注册多个 Middleware
- **THEN** SHALL 按注册顺序链式执行
- **THEN** 任一 Middleware 返回错误 SHALL 中断后续执行
#### Scenario: Middleware 中断转换
- **WHEN** Middleware 返回 ConversionError
- **THEN** SHALL 停止转换流程
- **THEN** SHALL 使用 clientAdapter.encodeError 编码错误响应
### Requirement: 定义 ConversionError 错误体系
系统 SHALL 定义 `ConversionError``ErrorCode` 枚举。
ErrorCode SHALL 包含INVALID_INPUT、MISSING_REQUIRED_FIELD、INCOMPATIBLE_FEATURE、FIELD_MAPPING_FAILURE、TOOL_CALL_PARSE_ERROR、JSON_PARSE_ERROR、STREAM_STATE_ERROR、UTF8_DECODE_ERROR、PROTOCOL_CONSTRAINT_VIOLATION、ENCODING_FAILURE、INTERFACE_NOT_SUPPORTED。
#### Scenario: 转换错误包含上下文
- **WHEN** 转换过程中发生错误
- **THEN** ConversionError SHALL 包含 code、message、clientProtocol、providerProtocol、interfaceType 等上下文
- **THEN** SHALL 支持包装原始错误cause
### Requirement: 定义 InterfaceType 枚举和接口分层
系统 SHALL 定义 `InterfaceType` 枚举CHAT、MODELS、MODEL_INFO、EMBEDDINGS、RERANK和接口分层策略。
- 核心层CHAT使用 Canonical Model 深度转换
- 扩展层MODELS、MODEL_INFO、EMBEDDINGS、RERANK使用轻量 Canonical Models 做字段映射
- 透传层未知接口URL+Header 适配后 Body 原样转发
#### Scenario: 扩展层接口转换
- **WHEN** interfaceType 为 MODELS/MODEL_INFO/EMBEDDINGS/RERANK
- **THEN** SHALL 使用对应扩展层 Canonical Model 做轻量字段映射
- **THEN** 双方都不支持时 SHALL 走透传逻辑
### Requirement: <20><>义 TargetProvider 结构体
系统 SHALL 定义 `TargetProvider` 结构体,包含 `base_url``api_key``model_name``adapter_config`
#### Scenario: Adapter 从 TargetProvider 获取配置
- **WHEN** Adapter 调用 buildHeaders(provider)
- **THEN** SHALL 从 provider.api_key 提取认证信息
- **THEN** SHALL 从 provider.adapter_config 提取协议专属配置
- **THEN** SHALL 使用 provider.model_name 覆盖请求中的 model 字段