优化代码
This commit is contained in:
1 parent
3553790475
commit
eda7e8bc96
23 files changed
+3113
-342
No files matched your search
+41
-20
@@ -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
|
||||
|
||||
Reference in new issue
Block a user