refactor: 实现 ConversionEngine 协议转换引擎,替代旧 protocol 包
引入 Canonical Model 和 ProtocolAdapter 架构,支持 OpenAI/Anthropic 协议间 无缝转换,统一 ProxyHandler 替代分散的 OpenAI/Anthropic Handler,简化 ProviderClient 为协议无关的 HTTP 发送器,Provider 新增 protocol 字段。
This commit is contained in:
71
backend/internal/conversion/canonical/extended.go
Normal file
71
backend/internal/conversion/canonical/extended.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package canonical
|
||||
|
||||
// CanonicalModel 规范模型
|
||||
type CanonicalModel struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Created int64 `json:"created,omitempty"`
|
||||
OwnedBy string `json:"owned_by,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalModelList 规范模型列表
|
||||
type CanonicalModelList struct {
|
||||
Models []CanonicalModel `json:"models"`
|
||||
}
|
||||
|
||||
// CanonicalModelInfo 规范模型详情
|
||||
type CanonicalModelInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Created int64 `json:"created,omitempty"`
|
||||
OwnedBy string `json:"owned_by,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalEmbeddingRequest 规范嵌入请求
|
||||
type CanonicalEmbeddingRequest struct {
|
||||
Model string `json:"model"`
|
||||
Input any `json:"input"` // string 或 []string
|
||||
EncodingFormat string `json:"encoding_format,omitempty"`
|
||||
Dimensions *int `json:"dimensions,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalEmbeddingResponse 规范嵌入响应
|
||||
type CanonicalEmbeddingResponse struct {
|
||||
Data []EmbeddingData `json:"data"`
|
||||
Model string `json:"model"`
|
||||
Usage EmbeddingUsage `json:"usage"`
|
||||
}
|
||||
|
||||
// EmbeddingData 嵌入数据项
|
||||
type EmbeddingData struct {
|
||||
Index int `json:"index"`
|
||||
Embedding any `json:"embedding"` // 根据格式不同可能是 []float64 或 base64 字符串
|
||||
}
|
||||
|
||||
// EmbeddingUsage 嵌入用量
|
||||
type EmbeddingUsage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
}
|
||||
|
||||
// CanonicalRerankRequest 规范重排序请求
|
||||
type CanonicalRerankRequest struct {
|
||||
Model string `json:"model"`
|
||||
Query string `json:"query"`
|
||||
Documents []string `json:"documents"`
|
||||
TopN *int `json:"top_n,omitempty"`
|
||||
ReturnDocuments *bool `json:"return_documents,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalRerankResponse 规范重排序响应
|
||||
type CanonicalRerankResponse struct {
|
||||
Results []RerankResult `json:"results"`
|
||||
Model string `json:"model"`
|
||||
}
|
||||
|
||||
// RerankResult 重排序结果项
|
||||
type RerankResult struct {
|
||||
Index int `json:"index"`
|
||||
RelevanceScore float64 `json:"relevance_score"`
|
||||
Document *string `json:"document,omitempty"`
|
||||
}
|
||||
156
backend/internal/conversion/canonical/stream.go
Normal file
156
backend/internal/conversion/canonical/stream.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package canonical
|
||||
|
||||
// StreamEventType 流式事件类型枚举
|
||||
type StreamEventType string
|
||||
|
||||
const (
|
||||
EventMessageStart StreamEventType = "message_start"
|
||||
EventContentBlockStart StreamEventType = "content_block_start"
|
||||
EventContentBlockDelta StreamEventType = "content_block_delta"
|
||||
EventContentBlockStop StreamEventType = "content_block_stop"
|
||||
EventMessageDelta StreamEventType = "message_delta"
|
||||
EventMessageStop StreamEventType = "message_stop"
|
||||
EventError StreamEventType = "error"
|
||||
EventPing StreamEventType = "ping"
|
||||
)
|
||||
|
||||
// DeltaType 增量类型枚举
|
||||
type DeltaType string
|
||||
|
||||
const (
|
||||
DeltaTypeText DeltaType = "text_delta"
|
||||
DeltaTypeInputJSON DeltaType = "input_json_delta"
|
||||
DeltaTypeThinking DeltaType = "thinking_delta"
|
||||
)
|
||||
|
||||
// StreamDelta 流式增量联合体
|
||||
type StreamDelta struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
PartialJSON string `json:"partial_json,omitempty"`
|
||||
Thinking string `json:"thinking,omitempty"`
|
||||
}
|
||||
|
||||
// StreamContentBlock 流式内容块联合体
|
||||
type StreamContentBlock struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Thinking string `json:"thinking,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalStreamEvent 规范流式事件联合体
|
||||
type CanonicalStreamEvent struct {
|
||||
Type StreamEventType `json:"type"`
|
||||
|
||||
// MessageStartEvent
|
||||
Message *StreamMessage `json:"message,omitempty"`
|
||||
|
||||
// ContentBlockStartEvent / ContentBlockDeltaEvent / ContentBlockStopEvent
|
||||
Index *int `json:"index,omitempty"`
|
||||
ContentBlock *StreamContentBlock `json:"content_block,omitempty"`
|
||||
Delta *StreamDelta `json:"delta,omitempty"`
|
||||
|
||||
// MessageDeltaEvent
|
||||
StopReason *StopReason `json:"stop_reason,omitempty"`
|
||||
Usage *CanonicalUsage `json:"usage,omitempty"`
|
||||
|
||||
// ErrorEvent
|
||||
Error *StreamError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// StreamMessage 流式消息摘要
|
||||
type StreamMessage struct {
|
||||
ID string `json:"id"`
|
||||
Model string `json:"model"`
|
||||
Usage *CanonicalUsage `json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
// StreamError 流式错误
|
||||
type StreamError struct {
|
||||
Type string `json:"type"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewMessageStartEvent 创建消息开始事件
|
||||
func NewMessageStartEvent(id, model string) CanonicalStreamEvent {
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventMessageStart,
|
||||
Message: &StreamMessage{ID: id, Model: model},
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageStartEventWithUsage 创建带用量的消息开始事件
|
||||
func NewMessageStartEventWithUsage(id, model string, usage *CanonicalUsage) CanonicalStreamEvent {
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventMessageStart,
|
||||
Message: &StreamMessage{ID: id, Model: model, Usage: usage},
|
||||
}
|
||||
}
|
||||
|
||||
// NewContentBlockStartEvent 创建内容块开始事件
|
||||
func NewContentBlockStartEvent(index int, block StreamContentBlock) CanonicalStreamEvent {
|
||||
idx := index
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventContentBlockStart,
|
||||
Index: &idx,
|
||||
ContentBlock: &block,
|
||||
}
|
||||
}
|
||||
|
||||
// NewContentBlockDeltaEvent 创建内容块增量事件
|
||||
func NewContentBlockDeltaEvent(index int, delta StreamDelta) CanonicalStreamEvent {
|
||||
idx := index
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventContentBlockDelta,
|
||||
Index: &idx,
|
||||
Delta: &delta,
|
||||
}
|
||||
}
|
||||
|
||||
// NewContentBlockStopEvent 创建内容块结束事件
|
||||
func NewContentBlockStopEvent(index int) CanonicalStreamEvent {
|
||||
idx := index
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventContentBlockStop,
|
||||
Index: &idx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageDeltaEvent 创建消息增量事件
|
||||
func NewMessageDeltaEvent(stopReason StopReason) CanonicalStreamEvent {
|
||||
sr := stopReason
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventMessageDelta,
|
||||
StopReason: &sr,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageDeltaEventWithUsage 创建带用量的消息增量事件
|
||||
func NewMessageDeltaEventWithUsage(stopReason StopReason, usage *CanonicalUsage) CanonicalStreamEvent {
|
||||
sr := stopReason
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventMessageDelta,
|
||||
StopReason: &sr,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageStopEvent 创建消息结束事件
|
||||
func NewMessageStopEvent() CanonicalStreamEvent {
|
||||
return CanonicalStreamEvent{Type: EventMessageStop}
|
||||
}
|
||||
|
||||
// NewErrorEvent 创建错误事件
|
||||
func NewErrorEvent(errType, message string) CanonicalStreamEvent {
|
||||
return CanonicalStreamEvent{
|
||||
Type: EventError,
|
||||
Error: &StreamError{Type: errType, Message: message},
|
||||
}
|
||||
}
|
||||
|
||||
// NewPingEvent 创建心跳事件
|
||||
func NewPingEvent() CanonicalStreamEvent {
|
||||
return CanonicalStreamEvent{Type: EventPing}
|
||||
}
|
||||
208
backend/internal/conversion/canonical/types.go
Normal file
208
backend/internal/conversion/canonical/types.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package canonical
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MessageRole 消息角色枚举
|
||||
type MessageRole string
|
||||
|
||||
const (
|
||||
RoleSystem MessageRole = "system"
|
||||
RoleUser MessageRole = "user"
|
||||
RoleAssistant MessageRole = "assistant"
|
||||
RoleTool MessageRole = "tool"
|
||||
)
|
||||
|
||||
// StopReason 停止原因枚举
|
||||
type StopReason string
|
||||
|
||||
const (
|
||||
StopReasonEndTurn StopReason = "end_turn"
|
||||
StopReasonMaxTokens StopReason = "max_tokens"
|
||||
StopReasonToolUse StopReason = "tool_use"
|
||||
StopReasonStopSequence StopReason = "stop_sequence"
|
||||
StopReasonContentFilter StopReason = "content_filter"
|
||||
StopReasonRefusal StopReason = "refusal"
|
||||
)
|
||||
|
||||
// SystemBlock 系统消息块
|
||||
type SystemBlock struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// ContentBlock 使用 type 字段的 discriminated union
|
||||
type ContentBlock struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
// TextBlock
|
||||
Text string `json:"text,omitempty"`
|
||||
|
||||
// ToolUseBlock
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Input json.RawMessage `json:"input,omitempty"`
|
||||
|
||||
// ToolResultBlock
|
||||
ToolUseID string `json:"tool_use_id,omitempty"`
|
||||
Content json.RawMessage `json:"content,omitempty"`
|
||||
IsError *bool `json:"is_error,omitempty"`
|
||||
|
||||
// ThinkingBlock
|
||||
Thinking string `json:"thinking,omitempty"`
|
||||
}
|
||||
|
||||
// NewTextBlock 创建文本块
|
||||
func NewTextBlock(text string) ContentBlock {
|
||||
return ContentBlock{Type: "text", Text: text}
|
||||
}
|
||||
|
||||
// NewToolUseBlock 创建工具调用块
|
||||
func NewToolUseBlock(id, name string, input json.RawMessage) ContentBlock {
|
||||
return ContentBlock{Type: "tool_use", ID: id, Name: name, Input: input}
|
||||
}
|
||||
|
||||
// NewToolResultBlock 创建工具结果块
|
||||
func NewToolResultBlock(toolUseID string, content string, isError bool) ContentBlock {
|
||||
errFlag := &isError
|
||||
return ContentBlock{
|
||||
Type: "tool_result",
|
||||
ToolUseID: toolUseID,
|
||||
Content: json.RawMessage(fmt.Sprintf("%q", content)),
|
||||
IsError: errFlag,
|
||||
}
|
||||
}
|
||||
|
||||
// NewThinkingBlock 创建思考块
|
||||
func NewThinkingBlock(thinking string) ContentBlock {
|
||||
return ContentBlock{Type: "thinking", Thinking: thinking}
|
||||
}
|
||||
|
||||
// CanonicalMessage 规范消息
|
||||
type CanonicalMessage struct {
|
||||
Role MessageRole `json:"role"`
|
||||
Content []ContentBlock `json:"content"`
|
||||
}
|
||||
|
||||
// CanonicalTool 规范工具定义
|
||||
type CanonicalTool struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
InputSchema json.RawMessage `json:"input_schema"`
|
||||
}
|
||||
|
||||
// ToolChoice 工具选择联合体
|
||||
type ToolChoice struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// NewToolChoiceAuto 创建自动工具选择
|
||||
func NewToolChoiceAuto() *ToolChoice {
|
||||
return &ToolChoice{Type: "auto"}
|
||||
}
|
||||
|
||||
// NewToolChoiceNone 创建无工具选择
|
||||
func NewToolChoiceNone() *ToolChoice {
|
||||
return &ToolChoice{Type: "none"}
|
||||
}
|
||||
|
||||
// NewToolChoiceAny 创建任意工具选择
|
||||
func NewToolChoiceAny() *ToolChoice {
|
||||
return &ToolChoice{Type: "any"}
|
||||
}
|
||||
|
||||
// NewToolChoiceNamed 创建指定工具选择
|
||||
func NewToolChoiceNamed(name string) *ToolChoice {
|
||||
return &ToolChoice{Type: "tool", Name: name}
|
||||
}
|
||||
|
||||
// RequestParameters 请求参数
|
||||
type RequestParameters struct {
|
||||
MaxTokens *int `json:"max_tokens,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
TopP *float64 `json:"top_p,omitempty"`
|
||||
TopK *int `json:"top_k,omitempty"`
|
||||
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
||||
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
|
||||
StopSequences []string `json:"stop_sequences,omitempty"`
|
||||
}
|
||||
|
||||
// ThinkingConfig 思考配置
|
||||
type ThinkingConfig struct {
|
||||
Type string `json:"type"`
|
||||
BudgetTokens *int `json:"budget_tokens,omitempty"`
|
||||
Effort string `json:"effort,omitempty"`
|
||||
}
|
||||
|
||||
// OutputFormat 输出格式联合体
|
||||
type OutputFormat struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Schema json.RawMessage `json:"schema,omitempty"`
|
||||
Strict *bool `json:"strict,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalRequest 规范请求
|
||||
type CanonicalRequest struct {
|
||||
Model string `json:"model"`
|
||||
System any `json:"system,omitempty"` // nil, string, or []SystemBlock
|
||||
Messages []CanonicalMessage `json:"messages"`
|
||||
Tools []CanonicalTool `json:"tools,omitempty"`
|
||||
ToolChoice *ToolChoice `json:"tool_choice,omitempty"`
|
||||
Parameters RequestParameters `json:"parameters"`
|
||||
Thinking *ThinkingConfig `json:"thinking,omitempty"`
|
||||
Stream bool `json:"stream"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
OutputFormat *OutputFormat `json:"output_format,omitempty"`
|
||||
ParallelToolUse *bool `json:"parallel_tool_use,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalUsage 规范用量
|
||||
type CanonicalUsage struct {
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
CacheReadTokens *int `json:"cache_read_tokens,omitempty"`
|
||||
CacheCreationTokens *int `json:"cache_creation_tokens,omitempty"`
|
||||
ReasoningTokens *int `json:"reasoning_tokens,omitempty"`
|
||||
}
|
||||
|
||||
// CanonicalResponse 规范响应
|
||||
type CanonicalResponse struct {
|
||||
ID string `json:"id"`
|
||||
Model string `json:"model"`
|
||||
Content []ContentBlock `json:"content"`
|
||||
StopReason *StopReason `json:"stop_reason,omitempty"`
|
||||
Usage CanonicalUsage `json:"usage"`
|
||||
}
|
||||
|
||||
// GetSystemString 获取系统消息字符串
|
||||
func (r *CanonicalRequest) GetSystemString() string {
|
||||
switch v := r.System.(type) {
|
||||
case string:
|
||||
return v
|
||||
case []SystemBlock:
|
||||
var result string
|
||||
for i, b := range v {
|
||||
if i > 0 {
|
||||
result += "\n\n"
|
||||
}
|
||||
result += b.Text
|
||||
}
|
||||
return result
|
||||
case nil:
|
||||
return ""
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSystemString 设置系统消息字符串
|
||||
func (r *CanonicalRequest) SetSystemString(s string) {
|
||||
if s == "" {
|
||||
r.System = nil
|
||||
} else {
|
||||
r.System = s
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user