fix: support Claude SSE format for ollama cloudflare xai cohere baidu tencent coze dify channels streaming responses - #2381
fix: support Claude SSE format for ollama cloudflare xai cohere baidu tencent coze dify channels streaming responses#2381longkeyy wants to merge 4857 commits into
Conversation
…space fix: tag splitting by whitespace
…odisable fix(channel): 当没有可用密钥时返回错误而不是第一个密钥
… new sections for partners, acknowledgments, and deployment instructions
- 更新中文README.md中的语言链接 - 完全重写英文README.en.md,包含所有详细功能说明 - 完全重写法文README.fr.md,确保内容一致性 - 完全重写日文README.ja.md,提供完整的项目说明 所有语言版本现在具有: - 相同的结构和格式 - 一致的语言导航 - 完整的功能特性和部署指南 - 统一的环境变量配置说明
…ence 修复viduq2不支持参考生视频的问题
…annel feat: replicate channel flux model
fix GetChannelKey AdminAuth -> RootAuth
fix GetChannelKey AdminAuth -> RootAuth
…future adjustments
fix: 未设置价格模型不会被拉取,除非设置自用模式
…-response-id fix: 模型设置增加针对Vertex渠道过滤content[].part[].functionResponse.id的选项,默认启用
- Replace legacy `docs.newapi.pro` paths with the new `/{lang}/docs/...` structure across all README translations
- Point key sections (installation, env vars, API, support, features) to their new locations
- Ensure language-specific links use the correct locale prefix (zh/en/ja) and keep FR aligned with English routes
Keep new-site links (/{lang}/docs/...) where matching pages exist in the current docs repo
Revert links that have no equivalent in the new docs to the legacy paths on doc.newapi.pro:
Google Gemini Chat
Midjourney-Proxy image docs
Suno music docs
Apply the same rule consistently across all README translations (zh/en/ja/fr)
…te-doc-links-new-routing 🔗 docs(readme): update documentation links to new site routing
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
relay/channel/coze/relay-coze.go (1)
225-225: Bug:fmt.Sprintfmissing format verbs.The format string lacks placeholders, so
errorData.CodeanderrorData.Messageare silently discarded and won't appear in the log output.🔎 Proposed fix
- common.SysLog(fmt.Sprintf("stream event error: ", errorData.Code, errorData.Message)) + common.SysLog(fmt.Sprintf("stream event error: code=%d, message=%s", errorData.Code, errorData.Message))
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
relay/channel/coze/relay-coze.gorelay/channel/dify/relay-dify.gorelay/channel/ollama/stream.gorelay/channel/tencent/relay-tencent.go
🚧 Files skipped from review as they are similar to previous changes (1)
- relay/channel/ollama/stream.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jiajunly
Repo: QuantumNous/new-api PR: 1629
File: relay/channel/openai/relay-openai.go:170-174
Timestamp: 2025-08-21T06:31:11.073Z
Learning: In relay/channel/openai/relay-openai.go, the streaming logic for the AddThinkFirst feature is designed so that only the first chunk of a stream gets the "<think>\n" prefix. The final flush in the streaming handler intentionally uses addThink=false because the last chunk should never receive the prefix, even in single-chunk streams where the prefix would have been applied during normal processing.
📚 Learning: 2025-08-21T06:31:11.073Z
Learnt from: jiajunly
Repo: QuantumNous/new-api PR: 1629
File: relay/channel/openai/relay-openai.go:170-174
Timestamp: 2025-08-21T06:31:11.073Z
Learning: In relay/channel/openai/relay-openai.go, the streaming logic for the AddThinkFirst feature is designed so that only the first chunk of a stream gets the "<think>\n" prefix. The final flush in the streaming handler intentionally uses addThink=false because the last chunk should never receive the prefix, even in single-chunk streams where the prefix would have been applied during normal processing.
Applied to files:
relay/channel/tencent/relay-tencent.gorelay/channel/dify/relay-dify.gorelay/channel/coze/relay-coze.go
🧬 Code graph analysis (3)
relay/channel/tencent/relay-tencent.go (1)
service/usage_helpr.go (1)
ResponseText2Usage(22-29)
relay/channel/dify/relay-dify.go (1)
service/usage_helpr.go (1)
ResponseText2Usage(22-29)
relay/channel/coze/relay-coze.go (2)
service/usage_helpr.go (1)
ResponseText2Usage(22-29)relay/common/relay_info.go (1)
RelayInfo(81-128)
🔇 Additional comments (12)
relay/channel/dify/relay-dify.go (4)
18-18: LGTM!Import of the openai package is necessary for the new streaming format helpers (
HandleStreamFormatandHandleFinalResponse).
156-160: Good fix for nil pointer dereference.Constructing a
DifyFilestruct literal instead of attempting to set fields on an uninitialized (nil) pointer correctly resolves the crash for remote images.
219-219: LGTM!The introduction of
lastStreamDataand the switch toopenai.HandleStreamFormataligns with the unified streaming pattern across other channels. Marshaling the response and storing it for later finalization is the correct approach.Also applies to: 245-248
255-258: LGTM!The finalization via
HandleFinalResponsecorrectly passes the accumulatedlastStreamData, timestamp, model name, and usage. This ensures Claude/Gemini format requests receive the appropriate SSE events.relay/channel/tencent/relay-tencent.go (4)
21-21: LGTM!Import of the openai package is required for the new streaming format helpers.
96-98: LGTM!Initializing
responseIdviahelper.GetResponseID(c)at the start ensures a consistent ID across all stream chunks, addressing the empty responseId issue mentioned in the commit summary.
119-130: LGTM!The streaming logic correctly:
- Assigns the pre-generated
responseIdto each response chunk for consistency.- Captures the
createdtimestamp.- Marshals and routes through
HandleStreamFormatfor format-aware streaming.
137-144: LGTM!The finalization logic correctly computes usage via
ResponseText2Usageand delegates toHandleFinalResponsefor proper Claude/Gemini SSE event emission.relay/channel/coze/relay-coze.go (4)
15-15: LGTM!Import of the openai package is required for the new streaming format helpers.
106-106: LGTM!The introduction of
lastStreamData, updated call sites to pass it by reference, and the finalization viaHandleFinalResponsecorrectly implement the unified streaming pattern for Claude/Gemini format support.Also applies to: 118-118, 138-138, 149-151
155-155: LGTM!The updated signature correctly accepts
infoandlastStreamDatapointer to enable format-aware streaming and state tracking.
172-177: Good addition of error logging for marshal failures.The error logging for JSON marshal failures improves debuggability, which aligns with the commit message for this PR.
Also applies to: 210-215
When using ollama channel with Claude format requests (/v1/messages), the response was incorrectly using OpenAI SSE format instead of Claude SSE format. Changed ollamaStreamHandler to use openai.HandleStreamFormat() and openai.HandleFinalResponse() which properly switch output format based on info.RelayFormat (OpenAI, Claude, or Gemini). Fixes #N/A
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
Use openai.HandleStreamFormat() and openai.HandleFinalResponse() to properly switch output format based on info.RelayFormat.
- Fix nil pointer dereference in Dify when handling remote images - Fix empty responseId in Tencent by using helper.GetResponseID() - Fix potential duplicate final events in Ollama for Claude/Gemini formats - Add error logging for marshal failures in Coze stream handler
bb73d37 to
dbee76a
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
relay/channel/dify/relay-dify.go (1)
256-257: EmptyresponseIdpassed toHandleFinalResponse.An empty string is passed as
responseId. For OpenAI format, this will result in an empty ID in the final usage response. For Claude format, the ID comes from thelastStreamDataunmarshalling. Consider usinghelper.GetResponseID(c)for consistency with other channel implementations.🔎 Suggested fix
// Handle final response based on format - openai.HandleFinalResponse(c, info, lastStreamData, "", common.GetTimestamp(), info.UpstreamModelName, "", usage, false) + openai.HandleFinalResponse(c, info, lastStreamData, helper.GetResponseID(c), common.GetTimestamp(), info.UpstreamModelName, "", usage, false)
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
relay/channel/baidu/relay-baidu.gorelay/channel/cloudflare/relay_cloudflare.gorelay/channel/cohere/relay-cohere.gorelay/channel/coze/relay-coze.gorelay/channel/dify/relay-dify.gorelay/channel/ollama/stream.gorelay/channel/tencent/relay-tencent.gorelay/channel/xai/text.go
🚧 Files skipped from review as they are similar to previous changes (2)
- relay/channel/xai/text.go
- relay/channel/cloudflare/relay_cloudflare.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jiajunly
Repo: QuantumNous/new-api PR: 1629
File: relay/channel/openai/relay-openai.go:170-174
Timestamp: 2025-08-21T06:31:11.073Z
Learning: In relay/channel/openai/relay-openai.go, the streaming logic for the AddThinkFirst feature is designed so that only the first chunk of a stream gets the "<think>\n" prefix. The final flush in the streaming handler intentionally uses addThink=false because the last chunk should never receive the prefix, even in single-chunk streams where the prefix would have been applied during normal processing.
📚 Learning: 2025-08-21T06:31:11.073Z
Learnt from: jiajunly
Repo: QuantumNous/new-api PR: 1629
File: relay/channel/openai/relay-openai.go:170-174
Timestamp: 2025-08-21T06:31:11.073Z
Learning: In relay/channel/openai/relay-openai.go, the streaming logic for the AddThinkFirst feature is designed so that only the first chunk of a stream gets the "<think>\n" prefix. The final flush in the streaming handler intentionally uses addThink=false because the last chunk should never receive the prefix, even in single-chunk streams where the prefix would have been applied during normal processing.
Applied to files:
relay/channel/tencent/relay-tencent.gorelay/channel/ollama/stream.gorelay/channel/cohere/relay-cohere.gorelay/channel/dify/relay-dify.gorelay/channel/baidu/relay-baidu.gorelay/channel/coze/relay-coze.go
🧬 Code graph analysis (5)
relay/channel/tencent/relay-tencent.go (6)
relay/helper/common.go (1)
GetResponseID(158-161)common/json.go (1)
Marshal(21-23)relay/channel/openai/helper.go (2)
HandleStreamFormat(22-34)HandleFinalResponse(197-254)common/sys_log.go (1)
SysLog(11-14)service/usage_helpr.go (1)
ResponseText2Usage(22-29)service/http.go (1)
CloseResponseBodyGracefully(15-23)
relay/channel/ollama/stream.go (4)
relay/helper/common.go (2)
GenerateStartEmptyResponse(168-184)GenerateStopResponse(186-199)common/json.go (1)
Marshal(21-23)relay/channel/openai/helper.go (2)
HandleStreamFormat(22-34)HandleFinalResponse(197-254)types/relay_format.go (2)
RelayFormat(3-3)RelayFormatOpenAI(6-6)
relay/channel/cohere/relay-cohere.go (3)
common/json.go (1)
Marshal(21-23)relay/channel/openai/helper.go (2)
HandleStreamFormat(22-34)HandleFinalResponse(197-254)service/usage_helpr.go (1)
ResponseText2Usage(22-29)
relay/channel/dify/relay-dify.go (4)
common/json.go (1)
Marshal(21-23)relay/channel/openai/helper.go (2)
HandleStreamFormat(22-34)HandleFinalResponse(197-254)service/usage_helpr.go (1)
ResponseText2Usage(22-29)common/utils.go (1)
GetTimestamp(261-263)
relay/channel/baidu/relay-baidu.go (3)
relay/helper/common.go (1)
SetEventStreamHeaders(41-55)common/json.go (1)
Marshal(21-23)relay/channel/openai/helper.go (2)
HandleStreamFormat(22-34)HandleFinalResponse(197-254)
🔇 Additional comments (21)
relay/channel/cohere/relay-cohere.go (3)
14-14: LGTM: Import added for centralized streaming helpers.The import of the
openaipackage enables use ofHandleStreamFormatandHandleFinalResponsefor format-aware streaming.
88-88: LGTM: Streaming format handling centralized correctly.The pattern of marshaling each chunk, storing in
lastStreamData, and routing throughHandleStreamFormatis consistent with the PR's objective to support Claude/Gemini SSE formats.Also applies to: 157-160
169-170: EmptylastStreamDataedge case is valid but behavior is intentional.If a stream ends without any valid chunks,
lastStreamDataremains an empty string. WhenHandleFinalResponseis called, the Claude and Gemini format branches will attempt to unmarshal this empty string, which fails and triggers an error log before returning early. While this prevents an error response from reaching the client, this behavior appears intentional: logging the failure and returning gracefully rather than crashing. If this graceful failure is unacceptable for your requirements, the handlers should validate thatlastStreamDatais non-empty before attempting unmarshaling, or handle the empty case explicitly.relay/channel/dify/relay-dify.go (2)
156-160: Good fix: Prevents nil pointer dereference for remote images.The previous code likely attempted to mutate a nil
DifyFilepointer. Creating a new struct literal with all required fields is the correct approach.
219-219: LGTM: Streaming format handling follows the established pattern.The implementation correctly captures each chunk in
lastStreamDataand routes throughHandleStreamFormat.Also applies to: 245-248
relay/channel/tencent/relay-tencent.go (3)
21-21: LGTM: Import added for centralized streaming helpers.
96-98: LGTM: Proper response ID and timestamp handling.Using
helper.GetResponseID(c)ensures a consistent response ID across the stream. The implementation correctly captures each chunk and routes throughHandleStreamFormat.Also applies to: 120-120, 125-130
137-144: LGTM: Finalization and cleanup are properly ordered.The sequence of computing usage, calling
HandleFinalResponse, and then closing the response body is correct.relay/channel/baidu/relay-baidu.go (4)
17-17: LGTM: Import added for centralized streaming helpers.
119-123: LGTM: Streaming state initialization.The variables for tracking stream state are properly initialized.
SetEventStreamHeadersis called before processing chunks.
136-143: LGTM: Response ID and created timestamp captured from upstream.The implementation correctly captures the response ID and created timestamp from Baidu's response for use in final response generation.
146-151: LGTM: Finalization follows the established pattern.The call to
HandleFinalResponsewith the captured metadata is correct.relay/channel/ollama/stream.go (4)
16-16: LGTM: Import added for centralized streaming helpers.
80-87: LGTM: Start response handling updated.The start response is correctly captured in
lastStreamDataand routed throughHandleStreamFormat.
152-155: LGTM: Delta chunks properly routed through format handler.
167-179: Good: Format-aware stop event handling prevents duplicate events.The conditional at lines 172-174 ensures the stop event is only sent via
HandleStreamFormatfor OpenAI format. For Claude/Gemini formats, the stop/finalization is handled byHandleFinalResponse, avoiding duplicate termination events. This aligns with the commit message mentioning "prevent potential duplicate final events in Ollama for Claude/Gemini formats."relay/channel/coze/relay-coze.go (5)
15-15: LGTM: Import added for centralized streaming helpers.
106-106: LGTM: Streaming state properly threaded through event handler.The
lastStreamDatavariable is passed as a pointer tohandleCozeEvent, allowing updates from within the event handler.Also applies to: 118-118, 138-138
149-152: LGTM: Finalization follows the established pattern.
155-155: LGTM: Function signature updated with required parameters.The updated signature correctly includes
info(needed forHandleStreamFormat) andlastStreamDatapointer.
172-177: Good: Error logging added for JSON marshal failures.The added error logging for marshal failures improves debuggability, as mentioned in the commit messages.
Also applies to: 210-215
Summary
Fix multiple channels to properly output Claude SSE format when request uses Claude format (
/v1/messages).Affected channels:
Each channel now uses
openai.HandleStreamFormat()andopenai.HandleFinalResponse()which properly switch output format based oninfo.RelayFormat.Problem
When using channels with model alias and sending Claude format requests (
/v1/messages), the streaming response was incorrectly using OpenAI SSE format:Instead of the expected Claude SSE format:
Root Cause
The stream handlers in these channels were using
helper.ObjectData()orhelper.StringData()directly without checkinginfo.RelayFormat, which always outputs OpenAI format.Solution
Use existing
openai.HandleStreamFormat()andopenai.HandleFinalResponse()functions which already implement proper format switching based oninfo.RelayFormat(supports OpenAI, Claude, and Gemini formats).Commits
Each channel has an independent commit for isolation:
fix(ollama): support Claude SSE format for streaming responsesfix(cloudflare): support Claude SSE format for streaming responsesfix(xai): support Claude SSE format for streaming responsesfix(cohere): support Claude SSE format for streaming responsesfix(baidu): support Claude SSE format for streaming responsesfix(tencent): support Claude SSE format for streaming responsesfix(coze): support Claude SSE format for streaming responsesfix(dify): support Claude SSE format for streaming responsesTest plan
/v1/messages)#1439
#1618
#1854
#2301
#2378
Summary by CodeRabbit
Refactor
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.