From 25cf9d9065c59aae9efae8247049a2ba02c78fee Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 4 Aug 2026 18:25:58 -0300 Subject: [PATCH] fix(providers): switch minimax from claude to openai format so images work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Anthropic-compatible /anthropic/v1/messages endpoint rejects image input with 403. MiniMax's OpenAI-compatible /v1/chat/completions endpoint supports image_url natively for MiniMax-M3. - minimax + minimax-cn: format claude→openai, baseUrl→/v1/chat/completions - Remove Anthropic-Version header + ?beta=true suffix (not needed for openai) - Remove minimax/minimax-cn from ?beta=true executor case - Update cache-control tests (openai format uses different caching path) - Fix reasoning-split test names (no longer claude format) TDD: 2 registry tests assert format=openai (red→green). Refs: Hermes Agent #15715, MiniMax OpenAI-compatible API docs. --- .../providers/registry/minimax/cn/index.ts | 7 ++----- .../providers/registry/minimax/index.ts | 7 ++----- open-sse/executors/default.ts | 2 -- .../cache-control-claude-providers.test.ts | 12 +++++++---- tests/unit/minimax-m3-model-registry.test.ts | 20 +++++++++++++++++++ .../responsesanitizer-reasoning-split.test.ts | 4 ++-- 6 files changed, 34 insertions(+), 18 deletions(-) diff --git a/open-sse/config/providers/registry/minimax/cn/index.ts b/open-sse/config/providers/registry/minimax/cn/index.ts index 2274b01c39..91981768d3 100644 --- a/open-sse/config/providers/registry/minimax/cn/index.ts +++ b/open-sse/config/providers/registry/minimax/cn/index.ts @@ -1,17 +1,14 @@ import type { RegistryEntry } from "../../../shared.ts"; -import { getAnthropicCompatHeaders, ANTHROPIC_VERSION_HEADER } from "../../../shared.ts"; export const minimax_cnProvider: RegistryEntry = { id: "minimax-cn", alias: "minimax-cn", // unique alias (was colliding with minimax) - format: "claude", + format: "openai", executor: "default", - baseUrl: "https://api.minimaxi.com/anthropic/v1/messages", + baseUrl: "https://api.minimaxi.com/v1/chat/completions", modelsUrl: "https://api.minimaxi.com/v1/models", - urlSuffix: "?beta=true", authType: "apikey", authHeader: "bearer", - headers: getAnthropicCompatHeaders(), models: [ // Keep parity with minimax to ensure model discovery works for minimax-cn connections. // #3110: MiniMax M3 — frontier coding model with 1M context diff --git a/open-sse/config/providers/registry/minimax/index.ts b/open-sse/config/providers/registry/minimax/index.ts index 3033fccb46..54fc7b3058 100644 --- a/open-sse/config/providers/registry/minimax/index.ts +++ b/open-sse/config/providers/registry/minimax/index.ts @@ -1,17 +1,14 @@ import type { RegistryEntry } from "../../shared.ts"; -import { getAnthropicCompatHeaders, ANTHROPIC_VERSION_HEADER } from "../../shared.ts"; export const minimaxProvider: RegistryEntry = { id: "minimax", alias: "minimax", - format: "claude", + format: "openai", executor: "default", - baseUrl: "https://api.minimax.io/anthropic/v1/messages", + baseUrl: "https://api.minimax.io/v1/chat/completions", modelsUrl: "https://api.minimax.io/v1/models", - urlSuffix: "?beta=true", authType: "apikey", authHeader: "bearer", - headers: getAnthropicCompatHeaders(), models: [ // T12/T28: MiniMax default upgraded from M2.5 to M2.7 // #3110: MiniMax M3 — frontier coding model with 1M context diff --git a/open-sse/executors/default.ts b/open-sse/executors/default.ts index 98e53ab03c..4955d8adfb 100644 --- a/open-sse/executors/default.ts +++ b/open-sse/executors/default.ts @@ -289,8 +289,6 @@ export class DefaultExecutor extends BaseExecutor { case "glm": case "glmt": case "kimi-coding": - case "minimax": - case "minimax-cn": return `${this.config.baseUrl}?beta=true`; case "agentrouter": return this.usesClaudeCodeProtocol(credentials) diff --git a/tests/unit/cache-control-claude-providers.test.ts b/tests/unit/cache-control-claude-providers.test.ts index ed50593e33..5f8a96255c 100644 --- a/tests/unit/cache-control-claude-providers.test.ts +++ b/tests/unit/cache-control-claude-providers.test.ts @@ -17,8 +17,10 @@ describe("Cache Control Policy - Claude Protocol Providers", () => { // These should be detected via targetFormat assert.equal(providerSupportsCaching("bailian-coding-plan", "claude"), true); assert.equal(providerSupportsCaching("glm", "claude"), true); - assert.equal(providerSupportsCaching("minimax", "claude"), true); - assert.equal(providerSupportsCaching("minimax-cn", "claude"), true); + // minimax/minimax-cn use openai format (#3110 / image 403 fix); + // caching support for their OpenAI-compatible endpoint is TBD + assert.equal(providerSupportsCaching("minimax", "openai"), false); + assert.equal(providerSupportsCaching("minimax-cn", "openai"), false); assert.equal(providerSupportsCaching("kimi-coding", "claude"), true); // #3955 — OpenAI / Codex use automatic prefix caching (no cache_control needed). @@ -66,15 +68,17 @@ describe("Cache Control Policy - Claude Protocol Providers", () => { true ); + // minimax now uses openai format — caching behavior may differ; + // cache_control preservation depends on whether it joins CACHING_PROVIDERS assert.equal( shouldPreserveCacheControl({ userAgent: claudeCodeUA, isCombo: false, targetProvider: "minimax", - targetFormat: "claude", + targetFormat: "openai", settings: { alwaysPreserveClientCache: "auto" }, }), - true + false ); }); diff --git a/tests/unit/minimax-m3-model-registry.test.ts b/tests/unit/minimax-m3-model-registry.test.ts index 2aae2b3a69..d1e1ec74be 100644 --- a/tests/unit/minimax-m3-model-registry.test.ts +++ b/tests/unit/minimax-m3-model-registry.test.ts @@ -73,6 +73,26 @@ describe("MiniMax M3 model registration (#3110)", () => { assert.equal(m3.contextLength, 1_048_576); }); + it("minimax uses openai format (not claude) so images work via /v1/chat/completions", () => { + const entry = REGISTRY.minimax; + assert.ok(entry, "minimax registry entry must exist"); + assert.equal( + entry.format, + "openai", + "minimax must use openai format — the Anthropic-compatible /anthropic/v1/messages endpoint rejects images with 403; images work on the OpenAI-compatible /v1/chat/completions endpoint. See Hermes Agent #15715." + ); + }); + + it("minimax-cn uses openai format (not claude) so images work via /v1/chat/completions", () => { + const entry = REGISTRY["minimax-cn"]; + assert.ok(entry, "minimax-cn registry entry must exist"); + assert.equal( + entry.format, + "openai", + "minimax-cn must use openai format — parity with minimax; the Anthropic endpoint on api.minimaxi.com also rejects images." + ); + }); + it("nvidia provider does NOT list minimaxai/minimax-m3 (removed in #3329 — 404 upstream)", () => { const entry = REGISTRY.nvidia; assert.ok(entry, "nvidia registry entry must exist"); diff --git a/tests/unit/responsesanitizer-reasoning-split.test.ts b/tests/unit/responsesanitizer-reasoning-split.test.ts index dde3d50cd6..70ee456d7d 100644 --- a/tests/unit/responsesanitizer-reasoning-split.test.ts +++ b/tests/unit/responsesanitizer-reasoning-split.test.ts @@ -137,12 +137,12 @@ describe("responseSanitizer/reasoning — MiniMax M3 textual reasoning-tag route }); describe("responseSanitizer/reasoning — MiniMax M3 fix regression guards", () => { - it("direct minimax tier (claude format) stays unaffected", () => { + it("direct minimax tier (openai format) stays unaffected for textual reasoning tags", () => { assert.equal(isTextualReasoningTagNativeRoute("minimax", "minimax-m3"), false); assert.equal(shouldParseTextualReasoningTags("minimax", "MiniMax-M3"), false); }); - it("direct minimax-cn tier (claude format) stays unaffected", () => { + it("direct minimax-cn tier (openai format) stays unaffected for textual reasoning tags", () => { assert.equal(isTextualReasoningTagNativeRoute("minimax-cn", "minimax-m3"), false); assert.equal(shouldParseTextualReasoningTags("minimax-cn", "MiniMax-M3"), false); });