From 1ab7e98f5632b6de2196d6e0bf8b5ebff5d990c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Fri, 8 May 2026 22:42:31 +0800 Subject: [PATCH] Add files via upload --- internal/openai/claude_bridge.go | 20 +++++++++--- internal/openai/openai.go | 55 ++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/internal/openai/claude_bridge.go b/internal/openai/claude_bridge.go index 947ba70d..e2bf73a1 100644 --- a/internal/openai/claude_bridge.go +++ b/internal/openai/claude_bridge.go @@ -499,6 +499,7 @@ func (c *Client) claudeChatCompletionStream(ctx context.Context, payload interfa reader := bufio.NewReader(resp.Body) var full strings.Builder + fullText := "" for { line, readErr := reader.ReadString('\n') @@ -531,9 +532,14 @@ func (c *Client) claudeChatCompletionStream(ctx context.Context, payload interfa if deltaType == "text_delta" { text, _ := delta["text"].(string) if text != "" { - full.WriteString(text) + var textOut string + fullText, textOut = normalizeStreamingDelta(fullText, text) + if textOut == "" { + continue + } + full.WriteString(textOut) if onDelta != nil { - if err := onDelta(text); err != nil { + if err := onDelta(textOut); err != nil { return full.String(), err } } @@ -603,6 +609,7 @@ func (c *Client) claudeChatCompletionStreamWithToolCalls( reader := bufio.NewReader(resp.Body) var full strings.Builder + fullText := "" finishReason := "" // 追踪当前正在构建的 content blocks @@ -665,9 +672,14 @@ func (c *Client) claudeChatCompletionStreamWithToolCalls( if deltaType == "text_delta" { text, _ := delta["text"].(string) if text != "" { - full.WriteString(text) + var textOut string + fullText, textOut = normalizeStreamingDelta(fullText, text) + if textOut == "" { + continue + } + full.WriteString(textOut) if onContentDelta != nil { - if err := onContentDelta(text); err != nil { + if err := onContentDelta(textOut); err != nil { return full.String(), nil, finishReason, err } } diff --git a/internal/openai/openai.go b/internal/openai/openai.go index 10faf565..46e0ca9e 100644 --- a/internal/openai/openai.go +++ b/internal/openai/openai.go @@ -33,6 +33,38 @@ func (e *APIError) Error() string { return fmt.Sprintf("openai api error: status=%d body=%s", e.StatusCode, e.Body) } +// normalizeStreamingDelta 将可能是“累计片段/重发片段”的内容归一化为“纯增量”。 +// 部分兼容网关会返回累计 content;若直接 append 会出现重复文本(结巴)。 +func normalizeStreamingDelta(current, incoming string) (next, delta string) { + if incoming == "" { + return current, "" + } + if current == "" { + return incoming, incoming + } + if incoming == current { + return current, "" + } + if strings.HasPrefix(incoming, current) { + return incoming, incoming[len(current):] + } + if strings.HasSuffix(current, incoming) { + return current, "" + } + + // 边界重叠:current 后缀与 incoming 前缀重合,仅追加非重叠部分。 + max := len(current) + if len(incoming) < max { + max = len(incoming) + } + for overlap := max; overlap > 0; overlap-- { + if current[len(current)-overlap:] == incoming[:overlap] { + return current + incoming[overlap:], incoming[overlap:] + } + } + return current + incoming, incoming +} + // NewClient 创建一个新的OpenAI客户端。 func NewClient(cfg *config.OpenAIConfig, httpClient *http.Client, logger *zap.Logger) *Client { if httpClient == nil { @@ -219,6 +251,7 @@ func (c *Client) ChatCompletionStream(ctx context.Context, payload interface{}, reader := bufio.NewReader(resp.Body) var full strings.Builder + fullText := "" // 典型 SSE 结构: // data: {...}\n\n @@ -263,9 +296,14 @@ func (c *Client) ChatCompletionStream(ctx context.Context, payload interface{}, continue } - full.WriteString(delta) + var deltaOut string + fullText, deltaOut = normalizeStreamingDelta(fullText, delta) + if deltaOut == "" { + continue + } + full.WriteString(deltaOut) if onDelta != nil { - if err := onDelta(delta); err != nil { + if err := onDelta(deltaOut); err != nil { return full.String(), err } } @@ -380,6 +418,7 @@ func (c *Client) ChatCompletionStreamWithToolCalls( reader := bufio.NewReader(resp.Body) var full strings.Builder + fullText := "" finishReason := "" for { @@ -426,10 +465,14 @@ func (c *Client) ChatCompletionStreamWithToolCalls( content = delta.Text } if content != "" { - full.WriteString(content) - if onContentDelta != nil { - if err := onContentDelta(content); err != nil { - return full.String(), nil, finishReason, err + var contentOut string + fullText, contentOut = normalizeStreamingDelta(fullText, content) + if contentOut != "" { + full.WriteString(contentOut) + if onContentDelta != nil { + if err := onContentDelta(contentOut); err != nil { + return full.String(), nil, finishReason, err + } } } }