Skip to content

[codex] fix MiniMax OpenAI response formatting - #5896

Open
zhukangfeng wants to merge 4 commits into
QuantumNous:mainfrom
zhukangfeng:codex/minimax-openai-response-sanitize
Open

[codex] fix MiniMax OpenAI response formatting#5896
zhukangfeng wants to merge 4 commits into
QuantumNous:mainfrom
zhukangfeng:codex/minimax-openai-response-sanitize

Conversation

@zhukangfeng

@zhukangfeng zhukangfeng commented Jul 4, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 本 PR 描述已按变更内容整理,没有直接粘贴未经整理的长篇输出。
  • 当前提交由 Codex AI-assisted 完成;提交作者不是仓库历史核心开发者。

📝 变更描述 / Description

修复 MiniMax 以 OpenAI Chat Completions 格式转发时的响应字段污染问题。

MiniMax 的 OpenAI 兼容响应会返回一些非 OpenAI Chat Completions 字段,例如非流式响应里的 input_sensitiveoutput_sensitiveservice_tierbase_resp,以及响应消息中的 nameaudio_content;流式响应最后一个 chunk 还会额外带 message 块。该 PR 在 MiniMax OpenAI Chat Completions 响应路径启用 OpenAI force format,使响应重新经过 OpenAI DTO 序列化,只保留兼容字段;同时在 force format 的非流式路径清理复用请求 DTO 带来的 message.name

这能让 MiniMax 对外返回更接近标准 OpenAI Chat Completions 的非流式 JSON 与 SSE chunk,避免客户端因额外字段或最后一个流式 chunk 的 message 块出现兼容性问题。

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

本地验证通过:

go test ./relay/channel/minimax -run 'TestDoResponseForOpenAI(TextStripsMiniMaxFields|StreamStripsMiniMaxFieldsAndMessage)' -count=1
go test ./relay/channel/minimax -count=1
go test ./relay/channel/openai -count=1
go test ./relay/channel/minimax ./relay/channel/openai -count=1
go test ./relay/channel/... -count=1
go test ./...

另外使用本地 MiniMax mock upstream 启动真实 new-api 服务完成端到端验证:

  • 非流式 /v1/chat/completions 返回 HTTP 200,保留 objectmessage.contentusage,不再包含 input_sensitiveoutput_sensitiveservice_tierbase_respaudio_contentmessage.name
  • 流式 /v1/chat/completions 返回 HTTP 200 SSE,保留 delta.roledelta.contentreasoning_contentfinish_reasondata: [DONE],不再包含最后一个 chunk 的 message 块、delta.nameaudio_content

Summary by CodeRabbit

  • Bug Fixes

    • Updated MiniMax chat-completions requests to use the OpenAI-compatible /v1/chat/completions endpoint.
    • Added coverage to verify the corrected endpoint selection.
  • Documentation

    • Added design, implementation plan, and review documentation describing MiniMax OpenAI-compatible response sanitization for streaming and non-streaming responses.

@coderabbitai

coderabbitai Bot commented Jul 4, 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 Plus

Run ID: 6dd662e2-43f6-4838-b3ad-29c9de9309d5

📥 Commits

Reviewing files that changed from the base of the PR and between 120237a and 487094e.

📒 Files selected for processing (1)
  • relay/channel/minimax/adaptor_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • relay/channel/minimax/adaptor_test.go

Walkthrough

MiniMax chat-completions routing now targets /v1/chat/completions, with regression coverage. New plan, design, and review documents describe intended response sanitization behavior and verification steps.

Changes

MiniMax routing and sanitization documentation

Layer / File(s) Summary
Chat-completions endpoint routing
relay/channel/minimax/relay-minimax.go, relay/channel/minimax/adaptor_test.go
Maps MiniMax chat completions to /v1/chat/completions and verifies the generated URL.
Sanitization design and verification documentation
docs/superpowers/plans/..., docs/superpowers/specs/..., docs/superpowers/reports/...
Documents forced formatting, response-field removal, streaming expectations, scope, and test commands.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit checks the endpoint route,
And tests the URL from root to shoot.
Plans describe each response field,
While review notes show what to yield.
Clean paths make the carrots sprout!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not implement the required response sanitization or streaming final-chunk fix; it mainly adds documentation and changes the endpoint path. Implement and test force formatting that removes MiniMax-specific fields and prevents a final streaming chunk from containing a message block.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the intended MiniMax OpenAI response-formatting change.
Out of Scope Changes check ✅ Passed The documentation, endpoint update, and endpoint test are related to the MiniMax OpenAI response-formatting objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🧹 Nitpick comments (1)
relay/channel/minimax/adaptor_test.go (1)

189-236: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Streaming test doesn't cover top-level base_resp/input_sensitive stripping for the final SSE chunk.

The non-stream test checks input_sensitive, output_sensitive, service_tier, and base_resp are stripped, but the streaming test only checks message/name/audio_content. MiniMax streaming responses commonly attach base_resp on the final chunk too; consider adding that assertion for full parity with issue #5834's requirement covering both paths.

🤖 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/minimax/adaptor_test.go` around lines 189 - 236, The streaming
coverage in TestDoResponseForOpenAIStreamStripsMiniMaxFieldsAndMessage is
missing the final SSE chunk stripping checks that are already validated in the
non-stream path. Update this test to assert the final chunk also removes
MiniMax-specific top-level fields like base_resp and input_sensitive, alongside
the existing message/name/audio_content checks, so DoResponse stays consistent
across stream and non-stream responses.
🤖 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 `@docs/superpowers/plans/2026-07-04-minimax-openai-response-sanitize.md`:
- Around line 13-87: The document outline skips levels by going from the
top-level title straight to the task sections, triggering the markdown heading
hierarchy warning. Update the task headings under the main plan so they are all
one level lower than the title (or add an intermediate parent heading) and keep
the rest of the section structure consistent. Apply this to the Task 1/Task
2/Task 3/Task 4 headings so the markdown outline is valid and easy to scan.

---

Nitpick comments:
In `@relay/channel/minimax/adaptor_test.go`:
- Around line 189-236: The streaming coverage in
TestDoResponseForOpenAIStreamStripsMiniMaxFieldsAndMessage is missing the final
SSE chunk stripping checks that are already validated in the non-stream path.
Update this test to assert the final chunk also removes MiniMax-specific
top-level fields like base_resp and input_sensitive, alongside the existing
message/name/audio_content checks, so DoResponse stays consistent across stream
and non-stream responses.
🪄 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: 80316fa9-5040-45ae-a85f-afdc72a3c360

📥 Commits

Reviewing files that changed from the base of the PR and between 722d036 and 4ca4c08.

📒 Files selected for processing (6)
  • docs/superpowers/plans/2026-07-04-minimax-openai-response-sanitize.md
  • docs/superpowers/reports/minimax-openai-response-sanitize-review.html
  • docs/superpowers/specs/2026-07-04-minimax-openai-response-sanitize-design.md
  • relay/channel/minimax/adaptor.go
  • relay/channel/minimax/adaptor_test.go
  • relay/channel/openai/relay-openai.go

Comment on lines +13 to +87
### Task 1: Non-Streaming Regression Test

**Files:**
- Modify: `relay/channel/minimax/adaptor_test.go`

- [ ] **Step 1: Write the failing test**

Add a test that sends a MiniMax-style non-streaming response with extra top-level and message fields through `Adaptor.DoResponse`. Assert the recorded response keeps OpenAI fields and omits MiniMax-only fields.

- [ ] **Step 2: Run test to verify it fails**

Run: `go test ./relay/channel/minimax -run TestDoResponseForOpenAITextStripsMiniMaxFields -count=1`

Expected before implementation: FAIL because `input_sensitive`, `base_resp`, `name`, or `audio_content` appears in the response body.

### Task 2: Streaming Regression Test

**Files:**
- Modify: `relay/channel/minimax/adaptor_test.go`

- [ ] **Step 1: Write the failing test**

Add a streaming test that sends SSE chunks containing MiniMax-only `delta.name`, `delta.audio_content`, and a final chunk containing `choices[].message`. Assert the downstream SSE omits those fields and still emits `[DONE]`.

- [ ] **Step 2: Run test to verify it fails**

Run: `go test ./relay/channel/minimax -run TestDoResponseForOpenAIStreamStripsMiniMaxFieldsAndMessage -count=1`

Expected before implementation: FAIL because raw SSE chunks are forwarded with `name`, `audio_content`, or `message`.

### Task 3: Minimal Implementation

**Files:**
- Modify: `relay/channel/minimax/adaptor.go`
- Modify: `relay/channel/openai/relay-openai.go`

- [ ] **Step 1: Force formatting for MiniMax OpenAI text responses**

In `Adaptor.DoResponse`, before delegating to `openai.Adaptor`, set `info.ChannelSetting.ForceFormat = true` when:

- `info.RelayFormat` is `types.RelayFormatOpenAI`
- `info.RelayMode` is `constant.RelayModeChatCompletions`

- [ ] **Step 2: Run targeted tests**

In `OpenaiHandler`, when `forceFormat` is true, clear `simpleResponse.Choices[i].Message.Name` before marshalling the response.

- [ ] **Step 3: Run targeted tests**

Run: `go test ./relay/channel/minimax -count=1`

Expected after implementation: PASS.

### Task 4: Related Verification

**Files:**
- No production edits expected.

- [ ] **Step 1: Run delegated OpenAI handler tests**

Run: `go test ./relay/channel/openai -count=1`

Expected: PASS.

- [ ] **Step 2: Run touched relay package tests**

Run: `go test ./relay/channel/minimax ./relay/channel/openai -count=1`

Expected: PASS.

- [ ] **Step 3: Inspect diff**

Run: `git diff -- relay/channel/minimax/adaptor.go relay/channel/minimax/adaptor_test.go docs/superpowers/specs/2026-07-04-minimax-openai-response-sanitize-design.md docs/superpowers/plans/2026-07-04-minimax-openai-response-sanitize.md`

Expected: Only the planned MiniMax adaptor, tests, and docs changed.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Normalize the task heading levels.

The file jumps from the H1 straight to H3 task headings, which triggers the markdownlint MD001 warning and makes the outline harder to scan. Promote these sections to H2 (or add an H2 parent) so the hierarchy is consistent.

🧰 Tools
🪛 LanguageTool

[grammar] ~87-~87: Ensure spelling is correct
Context: ...md` Expected: Only the planned MiniMax adaptor, tests, and docs changed.

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.1)

[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 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 `@docs/superpowers/plans/2026-07-04-minimax-openai-response-sanitize.md` around
lines 13 - 87, The document outline skips levels by going from the top-level
title straight to the task sections, triggering the markdown heading hierarchy
warning. Update the task headings under the main plan so they are all one level
lower than the title (or add an intermediate parent heading) and keep the rest
of the section structure consistent. Apply this to the Task 1/Task 2/Task 3/Task
4 headings so the markdown outline is valid and easy to scan.

Source: Linters/SAST tools

@xuhaiL

xuhaiL commented Jul 9, 2026

Copy link
Copy Markdown

这个问题是因为 new api 的 minimax api 原因,不需要强制转换。使用更有好的方式去处理。

https://platform.minimaxi.com/docs/api-reference/text-post
https://api.minimaxi.com/v1/text/chatcompletion_v2 已经被标注为 @deprecated,这个 api stream 模式下最后一个包会返回聚合帧。

新的api 是符合 openai 规范的。

https://platform.minimaxi.com/docs/api-reference/text-chat-openai
https://api.minimaxi.com/v1/chat/completions

新api

image

旧版api

image
## 聚合帧数据包
{
    "id": "xxxxx",
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "message": {
                "content": "你好!我是 MiniMax-M3,由 MiniMax 公司开发的 AI 助手。😊\n\nMiniMax 是一家成立于 2022 年初的全球性 AI 基础模型公司,致力于推动人工智能技术朝着通用人工智能(AGI)的方向发展。\n\n有什么我可以帮你的吗?无论是问题解答、写作辅助、翻译、编程,还是日常聊天,我都乐意效劳~",
                "role": "assistant",
                "name": "MiniMax AI",
                "audio_content": "",
                "reasoning_content": "用户问\"你是谁啊\",这是一个很常见的身份询问问题。我需要根据我的身份信息来回答。我是 MiniMax-M3,由 MiniMax 开发。回答应该简洁友好,并可以主动表示愿意帮助用户。",
                "reasoning_details": [
                    {
                        "type": "reasoning.text",
                        "id": "reasoning-text-1",
                        "format": "MiniMax-response-v1",
                        "index": 0,
                        "text": "用户问\"你是谁啊\",这是一个很常见的身份询问问题。我需要根据我的身份信息来回答。我是 MiniMax-M3,由 MiniMax 开发。回答应该简洁友好,并可以主动表示愿意帮助用户。"
                    }
                ]
            }
        }
    ],
    "created": 123123123123,
    "model": "MiniMax-M3",
    "object": "chat.completion",
    "usage": {
        "total_tokens": 297,
        "total_characters": 0,
        "prompt_tokens": 179,
        "completion_tokens": 118,
        "prompt_tokens_details": {
            "cached_tokens": 114
        }
    },
    "input_sensitive": false,
    "output_sensitive": false,
    "input_sensitive_type": 0,
    "output_sensitive_type": 0,
    "output_sensitive_int": 0,
    "service_tier": "standard",
    "base_resp": {
        "status_code": 0,
        "status_msg": ""
    }
}

@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/channel/minimax/adaptor_test.go`:
- Around line 39-59: Update
TestGetRequestURLForChatCompletionsUsesOpenAICompatibleEndpoint to use testify
assertions: require for the GetRequestURL error check and assert for comparing
the returned URL. Add the assert and require imports if absent, preserving the
test’s existing setup and expected endpoint.
🪄 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: 3a5d7403-38c6-4b3b-9a66-182173c1674a

📥 Commits

Reviewing files that changed from the base of the PR and between c2efde8 and 120237a.

📒 Files selected for processing (2)
  • relay/channel/minimax/adaptor_test.go
  • relay/channel/minimax/relay-minimax.go

Comment thread relay/channel/minimax/adaptor_test.go
@Calcium-Ion
Calcium-Ion force-pushed the main branch 2 times, most recently from 51fdfc5 to 2b6f1df Compare August 30, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants