1
0

Merge branch 'dev-conversion-docs'

This commit is contained in:
2026-04-22 13:23:15 +08:00

View File

@@ -126,24 +126,54 @@
### 2.3 请求处理流程
每个 HTTP 请求的转换流程:
#### 2.3.1 三车道数据流模型
引擎根据协议匹配情况和参数条件,选择三条数据流通道之一:
```
客户端入站 调用方(协议识别+前缀剥离) SDK 内部处理 上游出站
┌──────────────────┐ ┌──────────────────┐
│ URL: │ 调用方完成: 1. 接口识别: CHAT │ URL: │
/<protocol>/ │ · clientProtocol 2. 同协议? ──yes──▶ 直接转发│ 目标协议
│ v1/... │ · nativePath └──no──▶ 继续转换 │ 原生路径 │
Headers: │ · providerProtocol 3. URL 映射: 目标路径 │ Headers:
协议原生格式 4. Header 构建: 目标格式 │ 目标协议格式
Body:5. Body 转换: D→C→E │ Body:
协议原生格式 │ 目标协议格式
└──────────────────┘ └──────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
数据流三车道模型
├────────────────┬─────────────────────────┬────────────────────────────────────────┤
透传车道 │ 智能透传车道 完整转换车道
├────────────────┼─────────────────────────┼────────────────────────────────────────┤
触发条件 │ 同协议 │ 同协议 + 接口∈{Chat,Embed,Rerank} │ 不同协议
│ + Body非空 + ModelName非空 │
请求处理 │ 重建Headers │ 重建HeadersDecode→Middleware→Encode
│ Body原样转发 │ RewriteRequestModelName(body) │
│ │ │ 最小化JSON字段手术 │ │
│ │ │ │ │
│ 响应处理 │ 原样返回 │ modelOverride非空时 │ Decode→modelOverride │
│ │ │ RewriteResponseModelName(body) │ →Encode │
│ │ │ │ │
│ 流式处理 │ chunk→[chunk] │ chunk→RewriteResponseModelName │ Decode→Middleware │
│ │ │ →[rewritten] │ →modelOverride→Encode │
│ │ │ │ │
│ 性能开销 │ 最低 │ 低仅JSON字段改写 │ 高(完整序列化) │
└────────────────┴─────────────────────────┴────────────────────────────────────────┘
```
响应方向同理含流式。D=Decoder, C=Canonical, E=Encoder
**智能透传的设计动机**:同协议场景下,若仅需改写 `model` 字段(如客户端请求模型 "X",上游需要模型 "Y"),无需完整解码/编码。直接在 JSON 层面手术式改写该字段,既保留原始请求的所有细节,又避免序列化开销
#### 2.3.2 完整请求处理流程
```
客户端入站 调用方职责 SDK 内部处理 上游出站
┌──────────────┐ ┌──────────────┐
│ URL: │ 调用方完成: 1. 接口识别 │ URL: │
│ /<protocol>/ │ · clientProtocol 2. IsPassthrough? │ 目标协议 │
│ v1/... │ · nativePath ├─ yes ─┬─ 无 modelOverride → 透传车道 │ 原生路径 │
│ Headers: │ · providerProtocol │ └─ 有 modelOverride → 智能透传车道│ Headers: │
│ 协议原生格式 │ │ │ 目标协议格式 │
│ Body: │ └─ no → 完整转换车道 │ Body: │
│ 协议原生格式 │ │ 目标协议格式 │
└──────────────┘ └──────────────┘
```
响应方向同理(含流式)。
**同协议透传**client == provider 时,仅重建 Header 后原样转发到上游。
**智能透传**:同协议且需改写 model 字段时,最小化 JSON 改写后转发。
**未知接口透传**无法识别的路径URL+Header 适配后 Body 原样转发。
---
@@ -229,13 +259,15 @@ ContentBlock = Union<
content: Union<String, Array<ContentBlock>>,
is_error: Option<Boolean> }
ThinkingBlock, { type: "thinking", thinking: String }
ImageBlock, { type: "image", source: ... } // 多模态预留
AudioBlock, { type: "audio", source: ... } // 多模态预留
VideoBlock, { type: "video", source: ... } // 多模态预留
FileBlock { type: "file", source: ... } // 多模态预留
ImageBlock, { type: "image", source: ... } // Deferred多模态预留
AudioBlock, { type: "audio", source: ... } // Deferred多模态预留
VideoBlock, { type: "video", source: ... } // Deferred多模态预留
FileBlock { type: "file", source: ... } // Deferred多模态预留
>
```
**当前实现状态**:仅实现了 TextBlock、ToolUseBlock、ToolResultBlock、ThinkingBlock。多模态类型ImageBlock、AudioBlock、VideoBlock、FileBlock为预留扩展点。
### 4.4 CanonicalTool / ToolChoice
```
@@ -400,7 +432,7 @@ interface ProtocolAdapter {
createStreamEncoder(): StreamEncoder
// 错误编码
encodeError(error: ConversionError): RawResponse
encodeError(error: ConversionError): (body, statusCode)
// 扩展层
decodeModelsResponse(raw): CanonicalModelList
@@ -415,20 +447,40 @@ interface ProtocolAdapter {
encodeRerankRequest(canonical, provider): RawRequest
decodeRerankResponse(raw): CanonicalRerankResponse
encodeRerankResponse(canonical): RawResponse
// 智能透传支持
extractUnifiedModelID(nativePath: String): (modelID, error) // 从路径提取统一模型 ID
extractModelName(body: Raw, interfaceType: InterfaceType): (modelName, error) // 从请求体提取 model 字段值
rewriteRequestModelName(body: Raw, newModel: String, interfaceType: InterfaceType): RawRequest // 最小化改写请求体中的 model 字段
rewriteResponseModelName(body: Raw, newModel: String, interfaceType: InterfaceType): RawResponse // 最小化改写响应体中的 model 字段
}
```
**`buildHeaders` 的设计**Adapter 只需从 `provider` 中提取自己协议需要的认证和配置信息,构建自己的 Header 格式。不再需要理解其他协议的 Header。
**智能透传方法的契约**
- `rewriteRequestModelName` / `rewriteResponseModelName` 必须**幂等**(多次调用结果相同)
- Rewrite 方法必须**最小化**(仅修改 model 字段,不触碰其他字段)
- Rewrite 失败时,引擎使用宽容策略:记录警告日志,使用原始 body 继续处理
- `extractModelName` 支持的接口类型CHAT、EMBEDDINGS、RERANK这些接口的请求体包含 model 字段)
### 5.3 InterfaceType
```
InterfaceType = Enum<
CHAT, MODELS, MODEL_INFO, EMBEDDINGS, RERANK,
AUDIO, IMAGES // 预留:多模态扩展时启用
CHAT, MODELS, MODEL_INFO, EMBEDDINGS, RERANK, PASSTHROUGH
>
```
| 类型 | 说明 |
|------|------|
| CHAT | 核心对话接口(各协议的 Chat/Messages 接口) |
| MODELS | 模型列表接口 |
| MODEL_INFO | 模型详情接口 |
| EMBEDDINGS | 向量嵌入接口 |
| RERANK | 重排序接口 |
| PASSTHROUGH | 未知接口,透传处理 |
### 5.4 StreamDecoder / StreamEncoder
```
@@ -463,68 +515,138 @@ ConversionEngine 是无状态的格式转换工具,仅做协议间的编解码
**协议识别**`clientProtocol``providerProtocol` 由调用方确定并传入引擎(详见 §2.2)。
#### 6.1.1 HTTP 规格
```
HTTPRequestSpec {
url: String // 请求路径(不含 base_url
method: String // HTTP 方法
headers: Map<String, String>
body: ByteArray // 原始请求体
}
HTTPResponseSpec {
statusCode: Integer
headers: Map<String, String>
body: ByteArray // 原始响应体
}
```
#### 6.1.2 引擎接口
```
class ConversionEngine {
registry: AdapterRegistry
middlewareChain: MiddlewareChain
// 生命周期
registerAdapter(adapter): void
use(middleware): void
getRegistry(): AdapterRegistry
// 核心转换
isPassthrough(clientProtocol, providerProtocol): Boolean {
return clientProtocol == providerProtocol && registry.get(clientProtocol).supportsPassthrough()
}
// 非流式请求转换
convertHttpRequest(request, clientProtocol, providerProtocol, provider): HttpRequest {
nativePath = request.url
convertHttpRequest(request, clientProtocol, providerProtocol, provider): HTTPRequestSpec
convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride): HTTPResponseSpec
createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType): StreamConverter
if isPassthrough(clientProtocol, providerProtocol):
providerAdapter = registry.get(providerProtocol)
return {url: provider.base_url + nativePath, method: request.method,
headers: providerAdapter.buildHeaders(provider), body: request.body}
clientAdapter = registry.get(clientProtocol)
providerAdapter = registry.get(providerProtocol)
// 使用 clientAdapter 识别接口类型
interfaceType = clientAdapter.detectInterfaceType(nativePath)
providerUrl = providerAdapter.buildUrl(nativePath, interfaceType)
providerHeaders = providerAdapter.buildHeaders(provider)
providerBody = convertBody(interfaceType, clientAdapter, providerAdapter, provider, request.body)
return {url: provider.base_url + providerUrl, method: request.method,
headers: providerHeaders, body: providerBody}
}
// 非流式响应转换
convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType): HttpResponse {
if isPassthrough(clientProtocol, providerProtocol): return response
clientAdapter = registry.get(clientProtocol)
providerAdapter = registry.get(providerProtocol)
providerBody = convertResponseBody(interfaceType, clientAdapter, providerAdapter, response.body)
return {status: response.status, headers: response.headers, body: providerBody}
}
// 流式转换:从 provider 协议解码,编码为 client 协议
createStreamConverter(clientProtocol, providerProtocol, provider): StreamConverter {
if isPassthrough(clientProtocol, providerProtocol):
providerAdapter = registry.get(providerProtocol)
return new PassthroughStreamConverter(providerAdapter.buildHeaders(provider))
providerAdapter = registry.get(providerProtocol)
clientAdapter = registry.get(clientProtocol)
return new CanonicalStreamConverter(
providerAdapter.createStreamDecoder(), clientAdapter.createStreamEncoder(), middlewareChain)
}
// 辅助方法
detectInterfaceType(nativePath, clientProtocol): InterfaceType
encodeError(error, clientProtocol): (body, statusCode)
}
```
#### 6.1.3 请求转换流程
```
function convertHttpRequest(request, clientProtocol, providerProtocol, provider):
nativePath = request.url
if isPassthrough(clientProtocol, providerProtocol):
providerAdapter = registry.get(providerProtocol)
interfaceType = providerAdapter.detectInterfaceType(nativePath)
// 智能透传:对 Chat/Embeddings/Rerank 接口改写 model 字段
if interfaceType in {CHAT, EMBEDDINGS, RERANK} and request.body非空 and provider.modelName非空:
rewrittenBody = providerAdapter.rewriteRequestModelName(request.body, provider.modelName, interfaceType)
if rewrite失败:
log.warn("智能透传改写失败,使用原始请求体")
rewrittenBody = request.body
else:
rewrittenBody = request.body
return HTTPRequestSpec {
url: provider.base_url + nativePath,
method: request.method,
headers: providerAdapter.buildHeaders(provider),
body: rewrittenBody
}
// 完整转换车道
clientAdapter = registry.get(clientProtocol)
providerAdapter = registry.get(providerProtocol)
interfaceType = clientAdapter.detectInterfaceType(nativePath)
providerUrl = providerAdapter.buildUrl(nativePath, interfaceType)
providerHeaders = providerAdapter.buildHeaders(provider)
providerBody = convertBody(interfaceType, clientAdapter, providerAdapter, provider, request.body)
return HTTPRequestSpec {
url: provider.base_url + providerUrl,
method: request.method,
headers: providerHeaders,
body: providerBody
}
```
#### 6.1.4 响应转换流程
```
function convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride):
if isPassthrough(clientProtocol, providerProtocol):
// 智能透传:改写响应体中的 model 字段
if modelOverride非空 and response.body非空:
adapter = registry.get(clientProtocol)
rewrittenBody = adapter.rewriteResponseModelName(response.body, modelOverride, interfaceType)
if rewrite失败:
log.warn("智能透传改写失败,使用原始响应体")
return response
return HTTPResponseSpec { statusCode: response.statusCode, headers: response.headers, body: rewrittenBody }
return response
// 完整转换车道
clientAdapter = registry.get(clientProtocol)
providerAdapter = registry.get(providerProtocol)
convertedBody = convertResponseBody(interfaceType, clientAdapter, providerAdapter, response.body, modelOverride)
return HTTPResponseSpec { statusCode: response.statusCode, headers: response.headers, body: convertedBody }
```
**modelOverride 参数语义**:跨协议场景下,客户端期望看到的模型名。在响应转换时直接覆写 `canonicalResponse.model = modelOverride`,在流式转换时覆写 `event.message.model = modelOverride`
### 6.2 Body 转换分发
#### 6.2.1 接口类型分发策略
| 接口类型 | 请求转换 | 响应转换 | 中间件 | modelOverride |
|---------|---------|---------|--------|---------------|
| CHAT | Decode→**Middleware**→Encode | Decode→modelOverride→Encode | **应用** | 支持(响应) |
| MODELS | Body透传GET | Decode→Encode | 不应用 | 不支持 |
| MODEL_INFO | Body透传GET | Decode→Encode | 不应用 | 不支持 |
| EMBEDDINGS | Decode→Encode | Decode→modelOverride→Encode | 不应用 | 支持(响应) |
| RERANK | Decode→Encode | Decode→modelOverride→Encode | 不应用 | 支持(响应) |
| PASSTHROUGH | Body透传 | Body透传 | 不应用 | 不支持 |
**关键说明**
- **只有 CHAT 接口**走完整的 `decode → middleware → encode` 管道
- **扩展层接口**EMBEDDINGS、RERANK**跳过中间件**,直接 decode → encode
- **扩展层响应支持 modelOverride**,在 encode 前直接覆写 canonical 字段
#### 6.2.2 请求体转换
```
function convertBody(interfaceType, clientAdapter, providerAdapter, provider, body):
switch interfaceType:
@@ -546,41 +668,146 @@ function convertBody(interfaceType, clientAdapter, providerAdapter, provider, bo
// 同 EMBEDDINGS 模式
default:
return body // 透传层:原样转发
```
function convertResponseBody(interfaceType, clientAdapter, providerAdapter, body):
// 结构与 convertBody 对称CHAT 走 Canonical 深度转换,扩展层走轻量映射,默认透传
// 各接口的具体响应转换逻辑详见各协议适配文档(附录 E
#### 6.2.3 响应体转换
```
function convertResponseBody(interfaceType, clientAdapter, providerAdapter, body, modelOverride):
switch interfaceType:
case CHAT:
canonical = providerAdapter.decodeResponse(body)
if modelOverride非空: canonical.model = modelOverride
return clientAdapter.encodeResponse(canonical)
case MODELS:
if !clientAdapter.supportsInterface(MODELS) || !providerAdapter.supportsInterface(MODELS):
return body
return clientAdapter.encodeModelsResponse(providerAdapter.decodeModelsResponse(body))
case MODEL_INFO:
// 同 MODELS 模式
case EMBEDDINGS:
if !clientAdapter.supportsInterface(EMBEDDINGS) || !providerAdapter.supportsInterface(EMBEDDINGS):
return body
canonical = providerAdapter.decodeEmbeddingResponse(body)
if modelOverride非空: canonical.model = modelOverride
return clientAdapter.encodeEmbeddingResponse(canonical)
case RERANK:
// 同 EMBEDDINGS 模式,支持 modelOverride
default:
return body // 透传层:原样转发
```
### 6.3 StreamConverter
#### 6.3.1 接口定义
```
interface StreamConverter {
processChunk(rawChunk): Array<RawSSEChunk>
flush(): Array<RawSSEChunk>
}
```
#### 6.3.2 三种转换器变体
| 转换器 | 触发条件 | processChunk | flush |
|--------|---------|--------------|-------|
| `PassthroughStreamConverter` | 同协议 + 无 modelOverride | `[rawChunk]` | `[]` |
| `SmartPassthroughStreamConverter` | 同协议 + 有 modelOverride | `[rewriteResponseModelName(rawChunk)]` | `[]` |
| `CanonicalStreamConverter` | 不同协议 | Decode→Middleware→modelOverride→Encode | decoder.flush()→encoder.flush() |
#### 6.3.3 PassthroughStreamConverter
```
class PassthroughStreamConverter implements StreamConverter {
headers: Map<String, String>
constructor(headers) { this.headers = headers }
processChunk(rawChunk): Array<RawSSEChunk> { return [rawChunk] }
flush(): Array<RawSSEChunk> { return [] }
}
```
#### 6.3.4 SmartPassthroughStreamConverter
```
class SmartPassthroughStreamConverter implements StreamConverter {
adapter: ProtocolAdapter
modelOverride: String
interfaceType: InterfaceType
processChunk(rawChunk): Array<RawSSEChunk> {
if rawChunk为空: return []
rewrittenChunk = adapter.rewriteResponseModelName(rawChunk, modelOverride, interfaceType)
if rewrite失败:
log.warn("智能透传改写失败,使用原始 chunk")
return [rawChunk]
return [rewrittenChunk]
}
flush(): Array<RawSSEChunk> { return [] }
}
```
#### 6.3.5 CanonicalStreamConverter
```
class CanonicalStreamConverter implements StreamConverter {
decoder: StreamDecoder
encoder: StreamEncoder
middleware: MiddlewareChain
context: ConversionContext
clientProtocol: String
providerProtocol: String
modelOverride: String
processChunk(rawChunk):
events = decoder.processChunk(rawChunk).map(e => middleware.applyStreamEvent(e))
return events.flatMap(e => encoder.encodeEvent(e))
events = decoder.processChunk(rawChunk)
result = []
for each event in events:
// 中间件:转换 canonical 事件
if middleware != null:
processed, err = middleware.applyStreamEvent(event, clientProtocol, providerProtocol, context)
if err != null:
continue // 宽容策略:跳过错误事件,继续处理
event = processed
// modelOverride覆写 model 字段
if modelOverride非空 and event.message != null:
event.message.model = modelOverride
// 编码为目标协议 SSE
chunks = encoder.encodeEvent(event)
result.append(chunks)
return result
flush():
return decoder.flush().flatMap(e => encoder.encodeEvent(e)) + encoder.flush()
events = decoder.flush()
// 同 processChunk 的中间件 + modelOverride + encode 管道
result = [经过管道处理的所有事件]
encoderChunks = encoder.flush()
result.append(encoderChunks)
return result
}
```
#### 6.3.6 流式转换器创建
```
function createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType):
if isPassthrough(clientProtocol, providerProtocol):
if modelOverride非空:
adapter = registry.get(clientProtocol)
return new SmartPassthroughStreamConverter(adapter, modelOverride, interfaceType)
return new PassthroughStreamConverter()
providerAdapter = registry.get(providerProtocol)
clientAdapter = registry.get(clientProtocol)
return new CanonicalStreamConverter(
decoder: providerAdapter.createStreamDecoder(),
encoder: clientAdapter.createStreamEncoder(),
middleware: middlewareChain,
modelOverride: modelOverride
)
```
### 6.4 Middleware
引擎内部的拦截钩子,在 decode → encode 之间对 Canonical 进行变换。
@@ -588,48 +815,73 @@ class CanonicalStreamConverter implements StreamConverter {
```
interface ConversionMiddleware {
intercept(canonical, clientProtocol, providerProtocol, context): canonical | error
interceptStreamEvent?(event, clientProtocol, providerProtocol, context): event | error
interceptStreamEvent(event, clientProtocol, providerProtocol, context): event | error
}
ConversionContext { conversionId, interfaceType, timestamp, metadata }
ConversionContext {
conversionId: String // 唯一转换 IDUUID
interfaceType: InterfaceType
timestamp: DateTime
metadata: Map<String, Any>
}
```
#### 6.4.1 中间件执行规则
- `intercept` 返回修改后的 canonical或返回 ConversionError 以**中断转换**
- `interceptStreamEvent` 同理,返回错误可中断流式转换
- `interceptStreamEvent` 返回修改后的 event或返回 error
- 多个 Middleware 按注册顺序链式执行,任一中断则后续不再执行
#### 6.4.2 错误处理差异
| 场景 | 错误处理策略 |
|------|-------------|
| 请求中间件 `intercept` 返回 error | **严格模式**:中断整个转换,返回错误 |
| 流式中间件 `interceptStreamEvent` 返回 error | **宽容模式**:跳过该事件,继续处理后续事件 |
**设计动机**:流式场景下,单个事件的错误不应中断整个流。请求场景下,错误请求应被明确拒绝。
### 6.5 使用示例
```
engine = new ConversionEngine()
engine.registerAdapter(new ProtocolAAdapter())
engine.registerAdapter(new ProtocolBAdapter())
engine.registerAdapter(new OpenAIAdapter())
engine.registerAdapter(new AnthropicAdapter())
// 场景1: 跨协议 Chat 转换
// 入站: /protocol_a/v1/chat/completions
// 入站: /openai/v1/chat/completions
provider = TargetProvider {
base_url: "https://api.protocol-b.com",
base_url: "https://api.anthropic.com",
api_key: "xxx",
model_name: "model-b",
adapter_config: { ... }
model_name: "claude-3-opus",
adapter_config: { anthropic_version: "2023-06-01" }
}
out = engine.convertHttpRequest(inRequest, "protocol_a", "protocol_b", provider)
// 出站: 目标协议路径 + 目标协议 headers + 转换后的 body
out = engine.convertHttpRequest(inRequest, "openai", "anthropic", provider)
// 出站: /v1/messages + Anthropic headers + 转换后的 body
// 场景2: /models 跨协议
out = engine.convertHttpRequest(inRequest, "protocol_a", "protocol_b", provider)
// URL: /v1/models(通常不变), headers 按目标协议格式重建
out = engine.convertHttpRequest(inRequest, "openai", "anthropic", provider)
// URL: /v1/models, headers 按 Anthropic 格式重建
// 场景3: 同协议透传
out = engine.convertHttpRequest(inRequest, "protocol_a", "protocol_a", provider)
// client == provider → 剥离前缀, 用 provider 重建 headers 后原样转发
out = engine.convertHttpRequest(inRequest, "openai", "openai", provider)
// client == provider → 透传车道:重建 headers 后原样转发
// 场景4: 流式转换(从 provider 协议解码,编码为 client 协议
converter = engine.createStreamConverter("protocol_a", "protocol_b", provider)
// 场景4: 同协议智能透传(改写 model 字段
provider = TargetProvider { model_name: "gpt-4-turbo", ... }
out = engine.convertHttpRequest(inRequest, "openai", "openai", provider)
// 智能透传车道:请求体中的 model 字段改写为 "gpt-4-turbo"
// 场景5: 流式转换(从 provider 协议解码,编码为 client 协议)
converter = engine.createStreamConverter("openai", "anthropic", "claude-3-opus", CHAT)
for chunk in upstreamSSE {
for out in converter.processChunk(chunk) { sendToClient(out) }
}
converter.flush()
// 场景6: 同协议流式智能透传
converter = engine.createStreamConverter("openai", "openai", "gpt-4-turbo", CHAT)
// 使用 SmartPassthroughStreamConverter逐 chunk 改写 model 字段
```
---
@@ -641,9 +893,13 @@ converter.flush()
```
上游 SSE 流
├─ 同协议: PassthroughStreamConverter(用 provider 重建 Headers 后逐块转发)
├─ 同协议 + 无 modelOverride: PassthroughStreamConverter
│ chunk → [chunk]
└─协议: CanonicalStreamConverter
协议 + 有 modelOverride: SmartPassthroughStreamConverter
│ chunk → [rewriteResponseModelName(chunk)]
└─ 不同协议: CanonicalStreamConverter
StreamDecoder StreamEncoder
┌───────────┐ ┌───────────┐
│ SSE Parser│ │SSE Writer │
@@ -653,9 +909,17 @@ converter.flush()
│ Event │──────────────────────▶│ Event │
│ Translator │ ┌──────────┐ │ Translator │
│ (状态机) │ │Middleware│ │ │
└───────────┘ └──────────┘ └───────────┘
└───────────┘ │(宽容模式) │ └───────────┘
└──────────┘
┌─────▼─────┐
│modelOverride│
│(覆写 model) │
└───────────┘
```
**流式中间件错误处理**`interceptStreamEvent` 返回 error 时,跳过该事件继续处理后续事件(宽容模式),而非中断整个流。
### 7.2 StreamDecoder 通用状态
StreamDecoder 需要跟踪以下通用状态。具体协议的 Decoder 可根据需要扩展:
@@ -697,12 +961,16 @@ StreamDecoder 将协议原生 SSE 事件翻译为 CanonicalStreamEventStreamE
### 8.2 多模态扩展
**状态**Deferred未实现
Canonical Model 已预留 ImageBlock / AudioBlock / VideoBlock / FileBlock。实现路径
1. 在各 ProtocolAdapter 中实现多模态块的编解码
2. 在 StreamDecoder/StreamEncoder 中处理多模态增量数据
### 8.3 有状态特性扩展
**状态**Deferred未实现
```
interface StatefulMiddleware extends ConversionMiddleware {
stateStore: SessionStateStore
@@ -722,6 +990,8 @@ interface StatefulMiddleware extends ConversionMiddleware {
### 8.5 自定义接口支持
**状态**Deferred未实现
```
interface CustomInterfaceHandler {
interfaceType(): InterfaceType
@@ -732,6 +1002,8 @@ interface CustomInterfaceHandler {
engine.registerCustomHandler(handler)
```
当前实现中,未知接口直接走 PASSTHROUGH 透传。
---
## 9. 错误处理
@@ -759,16 +1031,36 @@ ErrorCode = Enum<
### 9.2 错误处理策略
```
ErrorHandler { mode: "strict" | "lenient" }
引擎采用**分层宽容策略**,根据接口层级和场景选择不同的错误处理方式:
strict: 任何错误抛出异常
lenient: 尽力继续
INCOMPATIBLE_FEATURE → 降级继续
INTERFACE_NOT_SUPPORTED → 透传或返回空响应
TOOL_CALL_PARSE_ERROR → 保留原始内容继续
PROTOCOL_CONSTRAINT_VIOLATION → 自动修复
```
┌─────────────────────────────────────────────────────────────────────┐
│ 错误处理分层策略 │
├─────────────────┬───────────────────────────────────────────────────┤
│ 核心层CHAT │ 严格模式:任何错误返回 ConversionError中断转换 │
│ │ - Decode 失败 → 返回 JSON_PARSE_ERROR │
│ │ - Middleware 失败 → 返回错误 │
│ │ - Encode 失败 → 返回 ENCODING_FAILURE │
├─────────────────┼───────────────────────────────────────────────────┤
│ 扩展层 │ 宽容模式:记录警告日志,返回原始 body 透传 │
│ (Models/Embed/ │ - Decode 失败 → log.warn + 返回原始 body │
│ Rerank) │ - Encode 失败 → log.warn + 返回原始 body │
├─────────────────┼───────────────────────────────────────────────────┤
│ 流式中间件 │ 宽容模式:跳过错误事件,继续处理后续事件 │
│ │ - interceptStreamEvent 返回 error → continue │
├─────────────────┼───────────────────────────────────────────────────┤
│ 智能透传 │ 宽容模式:重写失败则使用原始 body/chunk │
│ │ - Rewrite 失败 → log.warn + 返回原始 body/chunk │
├─────────────────┼───────────────────────────────────────────────────┤
│ 请求中间件 │ 严格模式:返回错误则中断整个转换 │
│ │ - intercept 返回 error → 返回 error │
└─────────────────┴───────────────────────────────────────────────────┘
```
**设计动机**
- 核心接口CHAT必须保证语义正确性错误应明确暴露
- 扩展层接口优先保证可用性,错误应降级处理
- 流式场景不应因单个事件错误中断整个流
**不支持接口的处理**`INTERFACE_NOT_SUPPORTED`
@@ -778,7 +1070,7 @@ lenient: 尽力继续
| 返回空响应 | 不影响核心功能 | 返回空列表 `{data: []}` |
| 返回错误 | 客户端明确需要此功能 | 返回 501 或协议格式错误 |
具体策略通过配置或 Middleware 决定
具体策略`supportsInterface` 返回值决定:返回 false 时引擎直接透传 body
### 9.3 错误响应格式
@@ -786,6 +1078,24 @@ lenient: 尽力继续
Middleware 中断转换时同理,引擎调用 clientAdapter.encodeError 将 ConversionError 编码为客户端可理解的格式。
#### 9.3.1 EncodeError Fallback 行为
当客户端适配器不可用时,引擎使用通用 JSON 错误作为 fallback
```
function encodeError(error, clientProtocol):
adapter = registry.get(clientProtocol)
if adapter不存在:
// Fallback: 通用 JSON 错误
return {
"error": {
"message": error.message,
"type": "internal_error"
}
}, 500
return adapter.encodeError(error)
```
---
## 附录 A模块依赖
@@ -796,21 +1106,25 @@ Middleware 中断转换时同理,引擎调用 clientAdapter.encodeError 将 Co
│ 门面HTTP 转换 / 透传判断 / 流式转换 │
│ 无状态;协议识别见 §2.2 │
├──────────────────────────────────────────────────┤
│ HTTPRequestSpec / HTTPResponseSpec │
│ url, method, headers, body / statusCode, ... │
├──────────────────────────────────────────────────┤
│ TargetProvider │
│ base_url / api_key / model_name / adapter_config │
├──────────────────┬───────────────────────────────┤
│ AdapterRegistry │ MiddlewareChain │
├──────────────────┴───────────────────────────────┤
│ StreamConverter: Passthrough | Canonical
│ StreamConverter: Passthrough | SmartPassthrough | Canonical │
├──────────────────────────────────────────────────┤
│ ProtocolAdapter: 各协议实现 │
│ · buildHeaders(provider) · URL 映射 │
│ · Chat/Models/ModelInfo/Embeddings/Rerank/... 编解码 │
│ · encodeError · StreamDecoder / StreamEncoder │
│ · rewriteRequestModelName / rewriteResponseModelName (智能透传) │
├──────────────────────────────────────────────────┤
│ Canonical Model (Core + Extended) │
├──────────────────────────────────────────────────┤
│ Error Handling
│ Error Handling (分层宽容策略)
├──────────────────────────────────────────────────┤
│ Utility: UTF-8 Buffer / SSE Parser / Detector │
└──────────────────────────────────────────────────┘
@@ -823,22 +1137,25 @@ Middleware 中断转换时同理,引擎调用 clientAdapter.encodeError 将 Co
```
// ─── 核心入口 ───
ConversionEngine
.registerAdapter(adapter)
.use(middleware)
.registerAdapter(adapter): void
.use(middleware): void
.getRegistry(): AdapterRegistry
.isPassthrough(clientProtocol, providerProtocol): Boolean
.convertHttpRequest(request, clientProtocol, providerProtocol, provider): HttpRequest
.convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType): HttpResponse
.createStreamConverter(clientProtocol, providerProtocol, provider): StreamConverter
.convertHttpRequest(request, clientProtocol, providerProtocol, provider): HTTPRequestSpec
.convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride): HTTPResponseSpec
.createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType): StreamConverter
.detectInterfaceType(nativePath, clientProtocol): InterfaceType
.encodeError(error, clientProtocol): (body, statusCode)
// ─── HTTP 规格 ───
HTTPRequestSpec { url, method, headers, body }
HTTPResponseSpec { statusCode, headers, body }
// ─── 目标上游信息 ───
TargetProvider
.base_url: String
.api_key: String
.model_name: String
.adapter_config: Map<String, Any>
TargetProvider { base_url, api_key, model_name, adapter_config }
// ─── URL 路由 ───
// 协议识别见 §2.2;出站: provider.base_url + 目标协议原生路径
// ─── 接口类型 ───
InterfaceType = CHAT | MODELS | MODEL_INFO | EMBEDDINGS | RERANK | PASSTHROUGH
// ─── 协议适配器 ───
ProtocolAdapter
@@ -847,19 +1164,30 @@ ProtocolAdapter
.decodeRequest(raw) / .encodeRequest(canonical, provider)
.decodeResponse(raw) / .encodeResponse(canonical)
.createStreamDecoder() / .createStreamEncoder()
.encodeError(error): RawResponse
.encodeError(error): (body, statusCode)
// 扩展层
.decodeModelsResponse / .encodeModelsResponse
.decodeModelInfoResponse / .encodeModelInfoResponse
.decodeEmbeddingRequest / .encodeEmbeddingRequest(canonical, provider) / ...Response
.decodeRerankRequest / .encodeRerankRequest(canonical, provider) / ...Response
.decodeEmbeddingRequest / .encodeEmbeddingRequest / ...Response
.decodeRerankRequest / .encodeRerankRequest / ...Response
// 智能透传支持
.extractUnifiedModelID(nativePath)
.extractModelName(body, interfaceType)
.rewriteRequestModelName(body, newModel, interfaceType)
.rewriteResponseModelName(body, newModel, interfaceType)
// ─── 流式处理 ───
StreamConverter: .processChunk(raw) / .flush()
├─ PassthroughStreamConverter [raw] → [raw](用 provider 重建 Headers
CanonicalStreamConverter decode → middleware → encode
├─ PassthroughStreamConverter [raw] → [raw]
SmartPassthroughStreamConverter [raw] → [rewrite(raw)]
└─ CanonicalStreamConverter decode → middleware → modelOverride → encode
// ─── 接口类型 ───
InterfaceType = CHAT | MODELS | MODEL_INFO | EMBEDDINGS | RERANK | AUDIO | IMAGES
// ─── 中间件 ───
ConversionMiddleware
.intercept(req, clientProtocol, providerProtocol, ctx): (req, error)
.interceptStreamEvent(event, clientProtocol, providerProtocol, ctx): (event, error)
ConversionContext { conversionId, interfaceType, timestamp, metadata }
```
---
@@ -1070,6 +1398,28 @@ Canonical Model 是**活的公共契约**,不是固定不变的。其字段集
| D.6 | [ ] 流式 StreamDecoder 和 StreamEncoder 已实现(对照 §4.8 |
| D.7 | [ ] 扩展层接口的编解码已实现(支持的接口) |
| D.8 | [ ] `encodeError` 已实现 |
| D.10 | [ ] `extractUnifiedModelID(nativePath)` 已实现 |
| D.10 | [ ] `extractModelName(body, interfaceType)` 已实现(覆盖 Chat/Embeddings/Rerank |
| D.10 | [ ] `rewriteRequestModelName` 已实现(幂等、最小化) |
| D.10 | [ ] `rewriteResponseModelName` 已实现(按接口类型处理 model 字段存在性) |
### D.10 智能透传支持
| 项目 | 说明 |
|------|------|
| extractUnifiedModelID | 从路径提取统一模型 ID 的规则(如 `/models/{provider_id}/{model_name}` |
| extractModelName | 从请求体提取 model 字段值的规则按接口类型Chat/Embeddings/Rerank |
| rewriteRequestModelName | 请求体 model 字段改写规则(最小化 JSON 手术,仅修改 model 字段) |
| rewriteResponseModelName | 响应体 model 字段改写规则(按接口类型处理 model 字段存在性) |
**rewriteResponseModelName 的接口类型差异**
| 接口类型 | model 字段处理 |
|---------|---------------|
| CHAT | 存在则改写,不存在则添加(协议要求必须有 model 字段) |
| EMBEDDINGS | 存在则改写,不存在则添加(协议要求必须有 model 字段) |
| RERANK | 存在则改写不存在则不添加model 字段可选) |
| 其他 | 直接返回原始 body |
---