- openai.go 216(适配器+Chat/ChatStream+共享助手); openai_encode.go 202(请求wire+build*); openai_response.go 79(响应wire+responseFromWire); openai_stream.go 174(scanSSE) - 字节级比对 body identical; 导出API不变(go doc diff 空); gofmt 干净 - 纯净基线(HEAD+本改)全仓 go build/vet/test 绿 + win/darwin 交叉编译绿 - 遗留: scanSSE 138行(>80, 拆分前既有,零逻辑改动未动), 另开步骤重构
80 lines
2.8 KiB
Go
80 lines
2.8 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
)
|
|
|
|
// openAIChatResponse is the JSON body returned by /chat/completions, for both
|
|
// the non-streaming payload and each SSE stream chunk.
|
|
type openAIChatResponse struct {
|
|
ID string `json:"id"`
|
|
Model string `json:"model"`
|
|
Choices []openAIChoice `json:"choices"`
|
|
Usage *openAIUsage `json:"usage,omitempty"`
|
|
}
|
|
|
|
// openAIChoice is one candidate completion.
|
|
type openAIChoice struct {
|
|
Index int `json:"index"`
|
|
Message openAIResponseMessage `json:"message"`
|
|
Delta *openAIDelta `json:"delta,omitempty"`
|
|
FinishReason string `json:"finish_reason,omitempty"`
|
|
}
|
|
|
|
// openAIResponseMessage is the assistant message inside a non-streaming choice.
|
|
type openAIResponseMessage struct {
|
|
Role string `json:"role"`
|
|
Content string `json:"content"`
|
|
ToolCalls []openAIToolCall `json:"tool_calls,omitempty"`
|
|
}
|
|
|
|
// openAIDelta is the incremental payload inside a stream chunk. Reasoning content
|
|
// (OpenAI o-series / DeepSeek / DashScope) arrives in ReasoningContent; streaming
|
|
// function calls arrive in ToolCalls, accumulated per fragment index.
|
|
type openAIDelta struct {
|
|
Role string `json:"role"`
|
|
Content string `json:"content"`
|
|
ReasoningContent string `json:"reasoning_content"`
|
|
ToolCalls []openAIToolCallDelta `json:"tool_calls"`
|
|
}
|
|
|
|
// openAIToolCallDelta is one streaming function-call fragment. OpenAI emits a
|
|
// sequence of these sharing the same Index: the ID and Function.Name usually
|
|
// arrive once (in the first fragment) while Function.Arguments streams across
|
|
// fragments.
|
|
type openAIToolCallDelta struct {
|
|
Index int `json:"index"`
|
|
ID string `json:"id"`
|
|
Function openAIToolCallFn `json:"function"`
|
|
}
|
|
|
|
// openAIUsage reports token consumption.
|
|
type openAIUsage struct {
|
|
PromptTokens int `json:"prompt_tokens"`
|
|
CompletionTokens int `json:"completion_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
}
|
|
|
|
func responseFromWire(wire *openAIChatResponse) Response {
|
|
out := Response{Model: wire.Model}
|
|
if len(wire.Choices) > 0 {
|
|
text := wire.Choices[0].Message.Content
|
|
out.Content = text
|
|
out.Message = Message{Role: RoleAssistant, Content: []Content{{Type: ContentTypeText, Text: text}}}
|
|
out.StopReason = wire.Choices[0].FinishReason
|
|
for _, tc := range wire.Choices[0].Message.ToolCalls {
|
|
args := map[string]any{}
|
|
if tc.Function.Arguments != "" {
|
|
_ = json.Unmarshal([]byte(tc.Function.Arguments), &args)
|
|
}
|
|
out.ToolCalls = append(out.ToolCalls, ToolCall{Name: tc.Function.Name, ID: tc.ID, Args: args})
|
|
}
|
|
}
|
|
if u := wire.Usage; u != nil {
|
|
out.PromptTokens = u.PromptTokens
|
|
out.CompletionTokens = u.CompletionTokens
|
|
out.TotalTokens = u.TotalTokens
|
|
}
|
|
return out
|
|
}
|