Skip to content

feat: support Responses to Chat#5787

Merged
Calcium-Ion merged 6 commits into
mainfrom
feat/responses-to-chat
Jun 28, 2026
Merged

feat: support Responses to Chat#5787
Calcium-Ion merged 6 commits into
mainfrom
feat/responses-to-chat

Conversation

@Calcium-Ion

@Calcium-Ion Calcium-Ion commented Jun 28, 2026

Copy link
Copy Markdown
Member

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

支持 Responses to Chat 兼容转换:新增 Responses request 转 Chat Completions request 的回退能力,并补齐 Chat Completions 响应回转为 Responses 的非流式与流式输出。Advanced Custom 新增 OpenAI Responses to OpenAI Chat 转换选项;Gemini 渠道在客户端请求 /v1/responses 时会将 Gemini 响应转换回 OpenAI Responses 格式,避免上游已有内容但 Responses 客户端无法渲染。

同时修复 Responses 流式工具参数归位中 output_index 与 item_id 双 pending 导致参数重复的问题,保持 output_index 作为主绑定键,item_id 作为兼容补充。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

已通过以下验证:

go test ./service/relayconvert
go test ./relay/channel/openai ./relay/channel/gemini ./relay/channel/advancedcustom
go test ./dto ./service/relayconvert ./relay/channel/openai ./relay/channel/advancedcustom ./relay/channel/gemini
go test ./relay/...
cd web/default && bun run i18n:sync
cd web/default && bun run build
git diff --check

Summary by CodeRabbit

  • New Features

    • Added support for converting OpenAI Responses into chat-completions format, including streaming and non-streaming output.
    • Added support for Gemini and OpenAI relay paths that can return Responses-format output.
    • Added a new advanced custom converter option in the UI, with matching translations in multiple languages.
  • Bug Fixes

    • Improved handling of streamed responses, tool calls, and usage totals.
    • Fixed converter validation so the new Responses-to-Chat option accepts the correct incoming path.
    • Improved compatibility when detecting event-stream responses with varied header formatting.

Add a shared Responses-to-Chat stream state machine and use it from the OpenAI relay path. Preserve assistant text alongside tool calls, bind tool argument deltas by output_index, map incomplete finish reasons, support reasoning/custom tool events, and buffer upstream SSE for non-stream Chat clients.

Add deterministic service tests and relay SSE tests for the conversion path.

Related to #5745.
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62afa6d8-d166-4f04-815f-75420a76cbb1

📥 Commits

Reviewing files that changed from the base of the PR and between 8020a18 and b59eb9e.

📒 Files selected for processing (8)
  • relay/channel/claude/relay_claude_test.go
  • relay/channel/gemini/relay_responses.go
  • relay/channel/gemini/relay_responses_test.go
  • relay/chat_completions_via_responses.go
  • relay/chat_completions_via_responses_test.go
  • service/convert.go
  • service/relayconvert/chat_responses_compat_test.go
  • service/relayconvert/responses_to_chat.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • relay/chat_completions_via_responses_test.go
  • service/convert.go
  • relay/chat_completions_via_responses.go
  • service/relayconvert/chat_responses_compat_test.go
  • service/relayconvert/responses_to_chat.go
  • relay/channel/gemini/relay_responses.go

Walkthrough

Adds a new openai_responses_to_openai_chat_completions advanced custom converter that translates OpenAI Responses API requests/responses to Chat Completions format. Implements bidirectional conversion in a renamed relayconvert package (from openaicompat), adds Gemini Responses relay handlers, refactors the OpenAI chat_via_responses stream handler to delegate to relayconvert, and adds frontend type/UI/i18n support.

Changes

Responses ↔ Chat Completions Bidirectional Conversion

Layer / File(s) Summary
DTO contracts and channel settings
dto/channel_settings.go, dto/channel_settings_test.go
Adds AdvancedCustomConverterOpenAIResponsesToOpenAIChatCompletions constant, extends IsAdvancedCustomConverterAllowed and validateAdvancedCustomConverterPath to accept /v1/responses, and tests the new path validation.
openaicompat → relayconvert package rename and service wiring
service/relayconvert/chat_to_responses.go, service/relayconvert/policy.go, service/relayconvert/regex.go, service/relayconvert/responses_to_chat.go, service/openai_chat_responses_compat.go, service/openai_chat_responses_mode.go, service/relayconvert/chat_responses_compat_test.go
Updates package declarations from openaicompat to relayconvert; rewires service wrapper functions to delegate to relayconvert; fixes the responseStreamEventItemID helper to prefer event.Item.ID over event.ItemID and updates pending-args buffering in toolArgumentsDelta, ensureToolForEvent, ensureFallbackToolForEvent, and ResponsesBufferedAccumulator.ProcessEvent; adds tests for non-duplication of pending args.
Responses→Chat request conversion (relayconvert)
service/relayconvert/responses_request_to_chat.go, service/relayconvert/responses_request_to_chat_test.go
Adds ResponsesRequestToChatCompletionsRequest converting Responses requests to chat messages/tools/tool-choice/response-format with stateful-field rejection; tests cover scalar fields, multimodal input, tool calls, custom tools, and stateful-field errors.
Chat→Responses response/stream conversion (relayconvert)
service/relayconvert/chat_to_responses_response.go, service/relayconvert/chat_responses_compat_test.go
Adds ChatCompletionsResponseToResponsesResponse, ResponsesStatusFromChatFinishReason, UsageFromChatUsage, ChatToResponsesStreamState, ChatCompletionsStreamChunkToResponsesEvents, and FinalizeChatCompletionsStreamToResponses; tests cover finish-reason mapping, tool-call preservation, streaming aggregation.
OpenAI relay handlers (responses↔chat, buffered stream)
relay/channel/openai/responses_via_chat.go, relay/channel/openai/chat_via_responses.go, relay/channel/openai/chat_via_responses_test.go, relay/chat_completions_via_responses.go, relay/chat_completions_via_responses_test.go
Adds OaiChatToResponsesHandler and OaiChatToResponsesStreamHandler; adds OaiResponsesToChatBufferedStreamHandler; refactors OaiResponsesToChatStreamHandler to delegate to relayconvert state machine; replaces strings.HasPrefix stream detection with case-insensitive isResponsesEventStreamContentType; adds handler and helper tests.
Gemini Responses adaptor and relay handlers
relay/channel/gemini/adaptor.go, relay/channel/gemini/adaptor_responses.go, relay/channel/gemini/adaptor_responses_test.go, relay/channel/gemini/relay_responses.go, relay/channel/gemini/relay_responses_test.go
Implements ConvertOpenAIResponsesRequest (preprocesses/filters custom tool calls and inputs, converts via relayconvert, delegates to ConvertOpenAIRequest); adds GeminiResponsesHandler and GeminiResponsesStreamHandler; dispatches RelayModeResponses in DoResponse; tests cover system instructions, tool/tool-choice conversion, function call turns, custom tool skipping, non-streaming/streaming response shape, and error handling.
Advanced-custom adaptor wiring and Claude tool-use fix
relay/channel/advancedcustom/adaptor.go, relay/channel/advancedcustom/adaptor_test.go, service/convert.go, relay/channel/claude/relay_claude_test.go
Adds OpenAIResponsesToOpenAIChatCompletions case in ConvertOpenAIResponsesRequest and DoResponse routing; fixes ResponseOpenAI2Claude so tool-use Input is always a map (empty map fallback instead of raw string); adds adaptor and Claude tool-use tests.
Frontend type, UI options, and i18n
web/default/src/features/channels/types.ts, web/default/src/features/channels/lib/advanced-custom.ts, web/default/src/i18n/locales/*.json, .gitignore
Extends AdvancedCustomConverter union type; adds converter option with upstream path placeholder and path validation; adds translations in all supported locales; adds gitignore entry for local live test file.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AdvancedCustomAdaptor
  participant relayconvert
  participant UpstreamChatAPI

  Client->>AdvancedCustomAdaptor: POST /v1/responses (OpenAIResponsesRequest)
  AdvancedCustomAdaptor->>relayconvert: ResponsesRequestToChatCompletionsRequest
  relayconvert-->>AdvancedCustomAdaptor: GeneralOpenAIRequest
  AdvancedCustomAdaptor->>UpstreamChatAPI: POST /v1/chat/completions
  UpstreamChatAPI-->>AdvancedCustomAdaptor: ChatCompletions response/stream
  AdvancedCustomAdaptor->>relayconvert: ChatCompletionsStreamChunkToResponsesEvents / ChatCompletionsResponseToResponsesResponse
  relayconvert-->>AdvancedCustomAdaptor: ResponsesStreamEvents / OpenAIResponsesResponse
  AdvancedCustomAdaptor-->>Client: Responses SSE stream or JSON response
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • QuantumNous/new-api#2629: Introduced the original OaiResponsesToChatStreamHandler in relay/channel/openai/chat_via_responses.go, which this PR refactors to delegate to the new relayconvert state machine.
  • QuantumNous/new-api#2837: Modified reasoning summary delta mapping in the same chat_via_responses.go streaming handler that this PR restructures.
  • QuantumNous/new-api#2889: Modified OaiResponsesToChat* streaming control flow in relay/channel/openai/chat_via_responses.go, overlapping directly with this PR's refactor of the same file.

Suggested reviewers

  • creamlike1024

Poem

🐇 Hoppy conversions, back and forth we go,
Responses to Chat, in a bidirectional flow!
Gemini and Claude, advanced custom too—
relayconvert handles what openaicompat once knew.
The rabbit stamped types and i18n strings,
then hopped away fast on its little white feet. 🌟

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning [#2941] The diff also includes unrelated Claude parsing changes, .gitignore tweaks, and locale updates beyond the requested conversion. Move unrelated fixes and housekeeping into separate PRs, and keep this one focused on Responses→Chat conversion.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding Responses-to-Chat support.
Linked Issues check ✅ Passed [#2941] The PR adds /v1/responses → /v1/chat/completions conversion and validates advanced custom routing.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/responses-to-chat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Calcium-Ion Calcium-Ion changed the title feat: 支持 Responses to Chat feat: support Responses to Chat Jun 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
relay/channel/gemini/relay_responses_test.go (1)

52-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert for the value checks in these tests.

After the fatal setup passes, the remaining equality/substring checks should use assert to match the repo’s Go test convention. As per coding guidelines, “New or substantially rewritten Go backend tests MUST use github.com/stretchr/testify/require for setup and fatal assertions, and github.com/stretchr/testify/assert for non-fatal value checks.”

Also applies to: 130-150

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relay/channel/gemini/relay_responses_test.go` around lines 52 - 65, The test
in relay_responses_test.go is using require for non-fatal value and substring
checks after setup has already passed; switch those assertions in the affected
test blocks to assert to match the Go test convention. Keep require only for
setup/fatal checks like nil validation, and update the remaining
equality/contains/not-contains checks in the relay response tests (including the
later similar block) to use assert, referencing the same test functions so the
intent stays consistent.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@relay/channel/gemini/relay_responses.go`:
- Around line 21-27: In GeminiResponsesHandler, the upstream response body is
only closed after io.ReadAll succeeds, so a read error can leak the connection.
Move the body cleanup to run before the read, ideally with a deferred call right
after entering GeminiResponsesHandler, and keep the rest of the response
parsing/logging logic unchanged.

In `@relay/chat_completions_via_responses.go`:
- Around line 148-150: The SSE detection in the
`chat_completions_via_responses.go` flow is doing a case-sensitive
`strings.HasPrefix` check on the upstream Content-Type, so valid
`text/event-stream` responses with different casing can be missed. Update the
logic around `info.IsStream` to normalize or parse the
`httpResp.Header.Get("Content-Type")` value before checking for SSE, using the
existing stream detection path in `chat_completions_via_responses.go` so
`upstreamStream` is set correctly regardless of media-type casing.

In `@service/convert.go`:
- Around line 631-636: The fallback in `convert.go` for `claudeContent.Input`
currently lets empty, invalid, or null `toolUse.Function.Arguments` become a
string, which breaks the expected object shape. Update the `tool_use` conversion
logic around `common.Unmarshal` so `claudeContent.Input` always remains an
object/map on fallback, and treat non-parseable or null arguments as an empty
object rather than copying the raw string.

In `@service/relayconvert/responses_request_to_chat.go`:
- Around line 169-172: The conversion logic in
responsesInputTypeFunctionCallOutput and the function-tool handling path should
fail fast when required identifiers are missing instead of emitting invalid Chat
Completions payloads. Add validation before appending dto.Message so
function_call_output without a non-empty call_id and type:"function" tools
without a non-empty name return a converter error immediately. Keep the check
close to the existing responseToolOutputToChatContent and tool-mapping logic so
malformed references are rejected before building the chat payload.

In `@service/relayconvert/responses_to_chat.go`:
- Around line 784-794: The buffered argument accumulator in ProcessEvent is
duplicating tool arguments because
responsesEventFunctionArgsDelta/responsesEventCustomToolInputDelta appends the
same delta to both pendingByOutputIndex and pendingByItemID when both
identifiers are present. Update the ProcessEvent path in responses_to_chat.go to
mirror toolArgumentsDelta’s behavior by preferring OutputIndex and only falling
back to ItemID when OutputIndex is absent, so ensureTool does not drain the same
delta twice into tool.Arguments.

---

Nitpick comments:
In `@relay/channel/gemini/relay_responses_test.go`:
- Around line 52-65: The test in relay_responses_test.go is using require for
non-fatal value and substring checks after setup has already passed; switch
those assertions in the affected test blocks to assert to match the Go test
convention. Keep require only for setup/fatal checks like nil validation, and
update the remaining equality/contains/not-contains checks in the relay response
tests (including the later similar block) to use assert, referencing the same
test functions so the intent stays consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d640591e-3bc7-4b1a-b537-d417ef4e723e

📥 Commits

Reviewing files that changed from the base of the PR and between 3a506f5 and 64c4ab6.

📒 Files selected for processing (35)
  • .gitignore
  • dto/channel_settings.go
  • dto/channel_settings_test.go
  • dto/openai_response.go
  • relay/channel/advancedcustom/adaptor.go
  • relay/channel/advancedcustom/adaptor_test.go
  • relay/channel/gemini/adaptor.go
  • relay/channel/gemini/adaptor_responses.go
  • relay/channel/gemini/adaptor_responses_test.go
  • relay/channel/gemini/relay_responses.go
  • relay/channel/gemini/relay_responses_test.go
  • relay/channel/openai/chat_via_responses.go
  • relay/channel/openai/chat_via_responses_test.go
  • relay/channel/openai/responses_via_chat.go
  • relay/chat_completions_via_responses.go
  • service/convert.go
  • service/openai_chat_responses_compat.go
  • service/openai_chat_responses_mode.go
  • service/openaicompat/responses_to_chat.go
  • service/relayconvert/chat_responses_compat_test.go
  • service/relayconvert/chat_to_responses.go
  • service/relayconvert/chat_to_responses_response.go
  • service/relayconvert/policy.go
  • service/relayconvert/regex.go
  • service/relayconvert/responses_request_to_chat.go
  • service/relayconvert/responses_request_to_chat_test.go
  • service/relayconvert/responses_to_chat.go
  • web/default/src/features/channels/lib/advanced-custom.ts
  • web/default/src/features/channels/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
💤 Files with no reviewable changes (1)
  • service/openaicompat/responses_to_chat.go

Comment thread relay/channel/gemini/relay_responses.go
Comment thread relay/chat_completions_via_responses.go
Comment thread service/convert.go Outdated
Comment on lines +169 to +172
case responsesInputTypeFunctionCallOutput:
callID := strings.TrimSpace(common.Interface2String(item["call_id"]))
content := responseToolOutputToChatContent(item["output"])
return append(messages, dto.Message{Role: "tool", ToolCallId: callID, Content: content}), nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject malformed tool references before building the chat payload.

function_call_output without call_id and type:"function" tools without name currently get converted into Chat Completions shapes the upstream API cannot validate. Failing fast here will return a precise converter error instead of leaking a later 400 from the chat endpoint.

Suggested fix
 	case responsesInputTypeFunctionCallOutput:
 		callID := strings.TrimSpace(common.Interface2String(item["call_id"]))
+		if callID == "" {
+			return nil, errors.New("function_call_output item is missing call_id")
+		}
 		content := responseToolOutputToChatContent(item["output"])
 		return append(messages, dto.Message{Role: "tool", ToolCallId: callID, Content: content}), nil
 	}
@@
 	for _, tool := range tools {
 		toolType := strings.TrimSpace(common.Interface2String(tool["type"]))
 		if toolType == "function" {
+			name := strings.TrimSpace(common.Interface2String(tool["name"]))
+			if name == "" {
+				return nil, errors.New("function tool is missing name")
+			}
 			out = append(out, dto.ToolCallRequest{
 				Type: "function",
 				Function: dto.FunctionRequest{
-					Name:        strings.TrimSpace(common.Interface2String(tool["name"])),
+					Name:        name,
 					Description: common.Interface2String(tool["description"]),
 					Parameters:  tool["parameters"],
 				},
 			})

Also applies to: 321-330

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/relayconvert/responses_request_to_chat.go` around lines 169 - 172,
The conversion logic in responsesInputTypeFunctionCallOutput and the
function-tool handling path should fail fast when required identifiers are
missing instead of emitting invalid Chat Completions payloads. Add validation
before appending dto.Message so function_call_output without a non-empty call_id
and type:"function" tools without a non-empty name return a converter error
immediately. Keep the check close to the existing
responseToolOutputToChatContent and tool-mapping logic so malformed references
are rejected before building the chat payload.

Comment thread service/relayconvert/responses_to_chat.go
@Calcium-Ion Calcium-Ion force-pushed the feat/responses-to-chat branch from 8020a18 to b59eb9e Compare June 28, 2026 06:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@relay/chat_completions_via_responses.go`:
- Around line 183-184: The content-type check in
isResponsesEventStreamContentType should not rely on substring matching, since
it can misclassify non-SSE responses that merely contain “text/event-stream” in
a parameter. Update the helper to parse the header with the MIME media type
logic and compare only the actual media type token case-insensitively, keeping
the existing call sites in chat_completions_via_responses.go unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb5a6772-fc5b-4d41-9413-abd27bf1f70c

📥 Commits

Reviewing files that changed from the base of the PR and between 05fbc6a and 8020a18.

📒 Files selected for processing (8)
  • relay/channel/gemini/relay_responses.go
  • relay/channel/gemini/relay_responses_test.go
  • relay/chat_completions_via_responses.go
  • relay/chat_completions_via_responses_test.go
  • service/convert.go
  • service/convert_test.go
  • service/relayconvert/chat_responses_compat_test.go
  • service/relayconvert/responses_to_chat.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • relay/channel/gemini/relay_responses.go
  • relay/channel/gemini/relay_responses_test.go
  • service/relayconvert/responses_to_chat.go
  • service/relayconvert/chat_responses_compat_test.go

Comment on lines +183 to +184
func isResponsesEventStreamContentType(contentType string) bool {
return strings.Contains(strings.ToLower(contentType), "text/event-stream")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Parse the media type instead of substring-matching it.

strings.Contains(strings.ToLower(contentType), "text/event-stream") will also return true for headers like application/json; note=text/event-stream, so a non-stream response can be routed through the SSE path. Compare the actual media type token case-insensitively (for example via mime.ParseMediaType) rather than searching the whole header string.

Suggested fix
+import "mime"
+
 func isResponsesEventStreamContentType(contentType string) bool {
-	return strings.Contains(strings.ToLower(contentType), "text/event-stream")
+	mediaType, _, err := mime.ParseMediaType(contentType)
+	return err == nil && strings.EqualFold(mediaType, "text/event-stream")
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func isResponsesEventStreamContentType(contentType string) bool {
return strings.Contains(strings.ToLower(contentType), "text/event-stream")
import "mime"
func isResponsesEventStreamContentType(contentType string) bool {
mediaType, _, err := mime.ParseMediaType(contentType)
return err == nil && strings.EqualFold(mediaType, "text/event-stream")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relay/chat_completions_via_responses.go` around lines 183 - 184, The
content-type check in isResponsesEventStreamContentType should not rely on
substring matching, since it can misclassify non-SSE responses that merely
contain “text/event-stream” in a parameter. Update the helper to parse the
header with the MIME media type logic and compare only the actual media type
token case-insensitively, keeping the existing call sites in
chat_completions_via_responses.go unchanged.

@Calcium-Ion Calcium-Ion merged commit 2d5a041 into main Jun 28, 2026
1 check passed
jahnli added a commit to jahnli/new-api that referenced this pull request Jun 28, 2026
…finement

Upstream commits merged:
- feat: support Responses to Chat (QuantumNous#5787)
- fix: update section titles and improve layout in channel components
- feat: refine channel management UI

Changes applied:
- Gemini Responses API adaptor and relay handlers
- OpenAI Responses↔Chat conversion (responses_via_chat.go)
- Package rename: service/openaicompat → service/relayconvert
- Channel management UI refactor (batch mode, drawer layout)
- Advanced custom converter: openai_responses_to_openai_chat_completions
- Brand rename (NewAPIError → AIHubError) applied to all new files

Conflicts resolved (8 files):
- relay/channel/gemini/adaptor.go: manual merge (keep AIHubError + add Responses mode)
- channel-mutate-drawer.tsx: adopt upstream + reapply brand rename
- 6 i18n locale files: both sides' new keys retained

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

格式转换 /v1/responses -> /v1/chat/completions

1 participant