From 88ca844c87173fccabfbff9e44307c799706587a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:08:51 +0800 Subject: [PATCH] Add files via upload --- internal/openai/claude_bridge.go | 7 ++++-- .../openai/claude_reasoning_roundtrip_test.go | 17 ++++++++++++++ internal/openai/summarization_diag.go | 22 ++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/internal/openai/claude_bridge.go b/internal/openai/claude_bridge.go index 6ab3707a..195d0e4a 100644 --- a/internal/openai/claude_bridge.go +++ b/internal/openai/claude_bridge.go @@ -152,8 +152,11 @@ func convertOpenAIToClaude(payload interface{}) (*claudeRequest, error) { req.Model = m } - // max_tokens (Claude 必需) - if mt, ok := oai["max_tokens"].(float64); ok && mt > 0 { + // Anthropic requires max_tokens. OpenAI-compatible clients prefer + // max_completion_tokens, so map it first and keep max_tokens as fallback. + if mt, ok := oai["max_completion_tokens"].(float64); ok && mt > 0 { + req.MaxTokens = int(mt) + } else if mt, ok := oai["max_tokens"].(float64); ok && mt > 0 { req.MaxTokens = int(mt) } else { req.MaxTokens = 8192 // Claude 默认最大输出(兼容 Haiku/Sonnet/Opus) diff --git a/internal/openai/claude_reasoning_roundtrip_test.go b/internal/openai/claude_reasoning_roundtrip_test.go index 4ae176c5..67dc25ac 100644 --- a/internal/openai/claude_reasoning_roundtrip_test.go +++ b/internal/openai/claude_reasoning_roundtrip_test.go @@ -192,3 +192,20 @@ func TestClaudeToOpenAIResponseJSON_Thinking(t *testing.T) { t.Fatal() } } + +func TestConvertOpenAIToClaudeMapsMaxCompletionTokens(t *testing.T) { + req, err := convertOpenAIToClaude(map[string]interface{}{ + "model": "claude-test", + "max_completion_tokens": float64(16384), + "max_tokens": float64(1024), + "messages": []interface{}{ + map[string]interface{}{"role": "user", "content": "hello"}, + }, + }) + if err != nil { + t.Fatal(err) + } + if req.MaxTokens != 16384 { + t.Fatalf("max tokens=%d, want 16384", req.MaxTokens) + } +} diff --git a/internal/openai/summarization_diag.go b/internal/openai/summarization_diag.go index c3be41e5..44465145 100644 --- a/internal/openai/summarization_diag.go +++ b/internal/openai/summarization_diag.go @@ -53,9 +53,17 @@ func (rt *summarizationDiagRoundTripper) RoundTrip(req *http.Request) (*http.Res resp.Body = io.NopCloser(bytes.NewReader(body)) resp.ContentLength = int64(len(body)) - if rt.logger != nil && summarizationResponseEmptyChoices(body) { + if rt.logger != nil && resp.StatusCode >= http.StatusBadRequest { + rt.logger.Warn("eino summarization: API request rejected", + zap.Int("status", resp.StatusCode), + zap.String("request_id", responseRequestID(resp)), + zap.Int("response_bytes", len(body)), + zap.String("raw_body", truncateForLog(string(body), summarizationDiagBodyMaxBytes)), + ) + } else if rt.logger != nil && summarizationResponseEmptyChoices(body) { rt.logger.Warn("eino summarization: API returned empty choices", zap.Int("status", resp.StatusCode), + zap.String("request_id", responseRequestID(resp)), zap.Int("response_bytes", len(body)), zap.String("raw_body", truncateForLog(string(body), summarizationDiagBodyMaxBytes)), ) @@ -63,6 +71,18 @@ func (rt *summarizationDiagRoundTripper) RoundTrip(req *http.Request) (*http.Res return resp, err } +func responseRequestID(resp *http.Response) string { + if resp == nil { + return "" + } + for _, key := range []string{"x-request-id", "request-id", "x-trace-id"} { + if value := strings.TrimSpace(resp.Header.Get(key)); value != "" { + return value + } + } + return "" +} + func isSummarizationRequest(req *http.Request) bool { if req == nil { return false