refactor: 实现 ConversionEngine 协议转换引擎,替代旧 protocol 包
- 新增 ConversionEngine 核心引擎,支持 OpenAI 和 Anthropic 协议转换 - 添加 stream decoder/encoder 实现 - 更新 provider client 支持新引擎 - 补充单元测试和集成测试 - 更新 specs 文档
This commit is contained in:
@@ -170,3 +170,116 @@ func TestStreamEncoder_MessageDelta_WithUsage(t *testing.T) {
|
||||
assert.Contains(t, s, "usage")
|
||||
assert.Contains(t, s, "prompt_tokens")
|
||||
}
|
||||
|
||||
func TestStreamEncoder_InputJSONDelta_SubsequentDelta(t *testing.T) {
|
||||
e := NewStreamEncoder()
|
||||
|
||||
e.EncodeEvent(canonical.NewContentBlockStartEvent(0, canonical.StreamContentBlock{
|
||||
Type: "tool_use",
|
||||
ID: "call_1",
|
||||
Name: "get_weather",
|
||||
}))
|
||||
|
||||
e.EncodeEvent(canonical.NewContentBlockDeltaEvent(0, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: "{\"city\":",
|
||||
}))
|
||||
|
||||
event := canonical.NewContentBlockDeltaEvent(0, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: "\"Beijing\"}",
|
||||
})
|
||||
|
||||
chunks := e.EncodeEvent(event)
|
||||
require.NotEmpty(t, chunks)
|
||||
|
||||
s := string(chunks[0])
|
||||
assert.Contains(t, s, "tool_calls")
|
||||
assert.Contains(t, s, "Beijing")
|
||||
}
|
||||
|
||||
func TestStreamEncoder_MessageStart_NilMessage(t *testing.T) {
|
||||
e := NewStreamEncoder()
|
||||
event := canonical.CanonicalStreamEvent{Type: canonical.EventMessageStart}
|
||||
|
||||
chunks := e.EncodeEvent(event)
|
||||
require.Len(t, chunks, 1)
|
||||
|
||||
s := string(chunks[0])
|
||||
assert.Contains(t, s, "chat.completion.chunk")
|
||||
}
|
||||
|
||||
func TestStreamEncoder_UnknownEvent_ReturnsNil(t *testing.T) {
|
||||
e := NewStreamEncoder()
|
||||
event := canonical.CanonicalStreamEvent{Type: "unknown_type"}
|
||||
chunks := e.EncodeEvent(event)
|
||||
assert.Nil(t, chunks)
|
||||
}
|
||||
|
||||
func TestStreamEncoder_ContentBlockDelta_NilDelta(t *testing.T) {
|
||||
e := NewStreamEncoder()
|
||||
event := canonical.CanonicalStreamEvent{Type: canonical.EventContentBlockDelta}
|
||||
chunks := e.EncodeEvent(event)
|
||||
assert.Nil(t, chunks)
|
||||
}
|
||||
|
||||
func TestStreamEncoder_MultiToolCall_IndexMapping(t *testing.T) {
|
||||
e := NewStreamEncoder()
|
||||
|
||||
e.EncodeEvent(canonical.NewContentBlockStartEvent(0, canonical.StreamContentBlock{
|
||||
Type: "tool_use",
|
||||
ID: "call_1",
|
||||
Name: "get_weather",
|
||||
}))
|
||||
|
||||
firstDelta := canonical.NewContentBlockDeltaEvent(0, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: `{"city":"北京"}`,
|
||||
})
|
||||
chunks := e.EncodeEvent(firstDelta)
|
||||
require.NotEmpty(t, chunks)
|
||||
s := string(chunks[0])
|
||||
assert.Contains(t, s, `"index":0`)
|
||||
assert.Contains(t, s, "get_weather")
|
||||
assert.Contains(t, s, "北京")
|
||||
|
||||
e.EncodeEvent(canonical.NewContentBlockStopEvent(0))
|
||||
|
||||
e.EncodeEvent(canonical.NewContentBlockStartEvent(1, canonical.StreamContentBlock{
|
||||
Type: "tool_use",
|
||||
ID: "call_2",
|
||||
Name: "get_time",
|
||||
}))
|
||||
|
||||
secondDelta := canonical.NewContentBlockDeltaEvent(1, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: `{"tz":"Asia/Shanghai"}`,
|
||||
})
|
||||
chunks = e.EncodeEvent(secondDelta)
|
||||
require.NotEmpty(t, chunks)
|
||||
s = string(chunks[0])
|
||||
assert.Contains(t, s, `"index":1`)
|
||||
assert.Contains(t, s, "get_time")
|
||||
assert.Contains(t, s, "Asia/Shanghai")
|
||||
|
||||
subsequentDelta0 := canonical.NewContentBlockDeltaEvent(0, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: `"more_data"`,
|
||||
})
|
||||
chunks = e.EncodeEvent(subsequentDelta0)
|
||||
require.NotEmpty(t, chunks)
|
||||
s = string(chunks[0])
|
||||
assert.Contains(t, s, `"index":0`)
|
||||
assert.NotContains(t, s, "get_weather")
|
||||
assert.Contains(t, s, "more_data")
|
||||
|
||||
subsequentDelta1 := canonical.NewContentBlockDeltaEvent(1, canonical.StreamDelta{
|
||||
Type: string(canonical.DeltaTypeInputJSON),
|
||||
PartialJSON: `"more_time"`,
|
||||
})
|
||||
chunks = e.EncodeEvent(subsequentDelta1)
|
||||
require.NotEmpty(t, chunks)
|
||||
s = string(chunks[0])
|
||||
assert.Contains(t, s, `"index":1`)
|
||||
assert.Contains(t, s, "more_time")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user