Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion open-sse/config/providerRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1412,7 +1412,14 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
},
{ id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true },
// #3110: MiniMax M3 free tier via OpenCode
{ id: "minimax-m3-free", name: "MiniMax M3 Free", contextLength: 1048576 },
// #3328: MiniMax M3 is multimodal (verified: describes base64 images via the
// opencode upstream) — flag it so vision requests aren't gated/stripped.
{
id: "minimax-m3-free",
name: "MiniMax M3 Free",
contextLength: 1048576,
supportsVision: true,
},
{ id: "minimax-m2.5-free", name: "MiniMax M2.5 Free", contextLength: 204800 },
{ id: "ling-2.6-1t-free", name: "Ling 2.6 Free", contextLength: 262000 },
{
Expand Down
5 changes: 4 additions & 1 deletion open-sse/services/compression/lite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ function modelSupportsVision(model: string): boolean {
normalized.includes("gpt-4") ||
normalized.includes("4o") ||
normalized.includes("claude-3") ||
normalized.includes("gemini")
normalized.includes("gemini") ||
// #3328: MiniMax M3 is multimodal — verified it describes images via the opencode
// upstream. Without this, compression strips the image and the model goes "blind".
normalized.includes("minimax-m3")
);
}

Expand Down
18 changes: 18 additions & 0 deletions tests/unit/compression/lite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ describe("replaceImageUrls", () => {
assert.equal(result.applied, false);
});

// #3328: MiniMax M3 is multimodal (verified: it describes a base64 image via the
// opencode upstream). It must not be treated as a non-vision model, or compression
// strips the image and the model goes "blind".
it("keeps images for MiniMax M3 (incl. provider-prefixed / free ids)", () => {
const mkBody = () => ({
messages: [
{
role: "user",
content: [{ type: "image_url", image_url: { url: "data:image/png;base64,iVBOR" } }],
},
],
});
for (const id of ["minimax-m3", "minimax-m3-free", "oc/minimax-m3-free"]) {
const result = replaceImageUrls(mkBody(), id);
assert.equal(result.applied, false, `expected images kept for ${id}`);
}
});

it("skips non-image content", () => {
const body = {
messages: [{ role: "user", content: "just text" }],
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/provider-registry-qwen-vision.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ test("#2822 opencode-go/qwen3.6-plus deve ter supportsVision !== true", () => {
);
});

// #3328 — o oposto do #2822: MiniMax M3 (opencode) É multimodal (verificado
// empiricamente: descreve imagens base64 via o upstream opencode). Deve ter
// supportsVision: true para não ser barrado/strippado em requests com imagem.
test("#3328 opencode/minimax-m3-free deve ter supportsVision: true", () => {
const model = getModel("opencode", "minimax-m3-free");
assert.ok(model, "minimax-m3-free deve estar registrado em opencode");
assert.strictEqual(
model.supportsVision,
true,
"opencode/minimax-m3-free é multimodal — supportsVision deve ser true"
);
});

test("#2822 opencode-go/qwen3.5-plus deve ter supportsVision !== true", () => {
const model = getModel("opencode-go", "qwen3.5-plus");
assert.ok(model, "qwen3.5-plus deve estar registrado em opencode-go");
Expand Down