优化代码

This commit is contained in:
hero920103 committed 2026-10-28 08:47:09 +08:00
1 parent 3553790475
commit eda7e8bc96
23 files changed
+3113 -342

No files matched your search

+41 -20
View File
@@ -9,7 +9,6 @@ import (
"io"
"net/http"
"strings"
"time"
)
// openAI 是 OpenAI 兼容协议(/chat/completions + SSE)的实现。
@@ -25,19 +24,7 @@ func newOpenAI(opts Options) *openAI {
if opts.Temperature <= 0 {
opts.Temperature = 0.3
}
return &openAI{
opts: opts,
http: &http.Client{
// 不设总超时:流式响应可能持续很久,由 ctx 控制。
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConns: 8,
IdleConnTimeout: 90 * time.Second,
ForceAttemptHTTP2: true,
TLSHandshakeTimeout: 15 * time.Second,
},
},
}
return &openAI{opts: opts, http: newHTTPClient()}
}
type reqMessage struct {
@@ -72,6 +59,10 @@ type streamChunk struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
// 思考内容:DeepSeek-R1 / Qwen3 等推理模型走 reasoning_content,
// 也有网关用 reasoning,两个字段都收。
Reasoning string `json:"reasoning_content"`
ReasoningAlt string `json:"reasoning"`
} `json:"delta"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
@@ -186,6 +177,7 @@ func (o *openAI) Chat(ctx context.Context, system string, msgs []Message, fn Del
var usage Usage
var got strings.Builder
var gotReasoning strings.Builder
err = readSSE(resp.Body, func(ev sseEvent) error {
if len(ev.Data) == 0 || bytes.Equal(ev.Data, []byte("[DONE]")) {
return nil
@@ -205,12 +197,24 @@ func (o *openAI) Chat(ctx context.Context, system string, msgs []Message, fn Del
}
}
for _, c := range chunk.Choices {
reasoning := c.Delta.Reasoning
if reasoning == "" {
reasoning = c.Delta.ReasoningAlt
}
if reasoning != "" {
gotReasoning.WriteString(reasoning)
if fn != nil {
if derr := fn(Delta{Reasoning: reasoning}); derr != nil {
return derr
}
}
}
if c.Delta.Content == "" {
continue
}
got.WriteString(c.Delta.Content)
if fn != nil {
if derr := fn(c.Delta.Content); derr != nil {
if derr := fn(Delta{Text: c.Delta.Content}); derr != nil {
return derr
}
}
@@ -220,7 +224,7 @@ func (o *openAI) Chat(ctx context.Context, system string, msgs []Message, fn Del
if err != nil {
return usage, err
}
if got.Len() == 0 {
if got.Len() == 0 && gotReasoning.Len() == 0 {
return usage, fmt.Errorf("模型没有返回任何内容,请确认 %s 是支持图片输入的视觉模型", o.opts.Model)
}
return usage, nil
@@ -236,6 +240,9 @@ func (o *openAI) readWhole(r io.Reader, fn DeltaFunc) (Usage, error) {
Choices []struct {
Message struct {
Content string `json:"content"`
// 非流式响应里思考内容通常挂在 message 上。
ReasoningContent string `json:"reasoning_content"`
Reasoning string `json:"reasoning"`
} `json:"message"`
} `json:"choices"`
Usage *struct {
@@ -253,7 +260,11 @@ func (o *openAI) readWhole(r io.Reader, fn DeltaFunc) (Usage, error) {
if resp.Error != nil {
return Usage{}, fmt.Errorf("模型返回错误: %s", resp.Error.Message)
}
if len(resp.Choices) == 0 || resp.Choices[0].Message.Content == "" {
if len(resp.Choices) == 0 {
return Usage{}, fmt.Errorf("模型返回内容为空")
}
msg := resp.Choices[0].Message
if msg.Content == "" && msg.ReasoningContent == "" && msg.Reasoning == "" {
return Usage{}, fmt.Errorf("模型返回内容为空")
}
var usage Usage
@@ -264,10 +275,20 @@ func (o *openAI) readWhole(r io.Reader, fn DeltaFunc) (Usage, error) {
TotalTokens: resp.Usage.TotalTokens,
}
}
text := resp.Choices[0].Message.Content
if fn != nil {
if err := fn(text); err != nil {
return usage, err
reasoning := msg.ReasoningContent
if reasoning == "" {
reasoning = msg.Reasoning
}
if reasoning != "" {
if err := fn(Delta{Reasoning: reasoning}); err != nil {
return usage, err
}
}
if msg.Content != "" {
if err := fn(Delta{Text: msg.Content}); err != nil {
return usage, err
}
}
}
return usage, nil