fix(capabilities): add deepseek-v4-1-flash vision alias; fix(modal): add zed to live catalog providers

This commit is contained in:
semihisikman authored and decolua committed 2026-09-28 13:09:25 +07:00
1 parent 45d42b8066
commit 8a4f4d9d2c
3 files changed
+89 -2

No files matched your search

+3
View File
@@ -130,7 +130,10 @@ export const MODEL_CAPABILITIES = {
// DeepSeek API; it previously fell through to the generic *deepseek* pattern, whose // DeepSeek API; it previously fell through to the generic *deepseek* pattern, whose
// 128K/64K limits are kept here. The repeated fields are deliberate: an exact entry // 128K/64K limits are kept here. The repeated fields are deliberate: an exact entry
// short-circuits the pattern table, so a vision-only delta would drop them. // short-circuits the pattern table, so a vision-only delta would drop them.
// Some providers (e.g. Kenari) expose this model under the hyphenated ID
// "deepseek-v4-1-flash" (dash instead of dot); add it as an alias (#4293).
"deepseek-v4.1-flash": { vision: true, reasoning: true, thinkingFormat: "deepseek", contextWindow: 1000000, maxOutput: 384000 }, "deepseek-v4.1-flash": { vision: true, reasoning: true, thinkingFormat: "deepseek", contextWindow: 1000000, maxOutput: 384000 },
"deepseek-v4-1-flash": { vision: true, reasoning: true, thinkingFormat: "deepseek", contextWindow: 1000000, maxOutput: 384000 },
"deepseek-flash": { vision: true, reasoning: true, thinkingFormat: "deepseek", contextWindow: 128000, maxOutput: 64000 }, "deepseek-flash": { vision: true, reasoning: true, thinkingFormat: "deepseek", contextWindow: 128000, maxOutput: 64000 },
// Qwen plain coder/text (no vision) — registry "vision-model" / "coder-model" aliases // Qwen plain coder/text (no vision) — registry "vision-model" / "coder-model" aliases
+6 -2
View File
@@ -22,7 +22,9 @@ const NO_AUTH_PROVIDER_IDS = Object.keys(FREE_PROVIDERS).filter(id => FREE_PROVI
// Providers with per-account live catalogs via /api/providers/[id]/models. // Providers with per-account live catalogs via /api/providers/[id]/models.
// Static registry stays as fallback when live fetch fails or is empty. // Static registry stays as fallback when live fetch fails or is empty.
const LIVE_CATALOG_PROVIDERS = ["cursor", "cline", "clinepass"]; // zed added in #4244: its backend customResolver already returns live models
// but the frontend omitted it, hiding Zed entirely from the Combo picker.
const LIVE_CATALOG_PROVIDERS = ["cursor", "cline", "clinepass", "zed"];
// Fetch a provider's account-scoped catalog for every active connection and merge // Fetch a provider's account-scoped catalog for every active connection and merge
// the results. Entries collapse by model id on purpose: two connections of the // the results. Entries collapse by model id on purpose: two connections of the
@@ -112,10 +114,12 @@ export default function ModelSelectModal({
const cursorConnectionIds = liveConnectionIdsByProvider.cursor; const cursorConnectionIds = liveConnectionIdsByProvider.cursor;
const clineConnectionIds = liveConnectionIdsByProvider.cline; const clineConnectionIds = liveConnectionIdsByProvider.cline;
const clinepassConnectionIds = liveConnectionIdsByProvider.clinepass; const clinepassConnectionIds = liveConnectionIdsByProvider.clinepass;
const zedConnectionIds = liveConnectionIdsByProvider.zed;
const cursorModels = useLiveProviderModels(isOpen, cursorConnectionIds, "Cursor"); const cursorModels = useLiveProviderModels(isOpen, cursorConnectionIds, "Cursor");
const clineModels = useLiveProviderModels(isOpen, clineConnectionIds, "Cline"); const clineModels = useLiveProviderModels(isOpen, clineConnectionIds, "Cline");
const clinepassModels = useLiveProviderModels(isOpen, clinepassConnectionIds, "ClinePass"); const clinepassModels = useLiveProviderModels(isOpen, clinepassConnectionIds, "ClinePass");
const zedModels = useLiveProviderModels(isOpen, zedConnectionIds, "Zed");
const fetchCombos = async () => { const fetchCombos = async () => {
try { try {
@@ -348,7 +352,7 @@ export default function ModelSelectModal({
hasModels: mergedModels.length > 0, hasModels: mergedModels.length > 0,
}; };
} else { } else {
const liveModels = providerId === "cursor" ? cursorModels : providerId === "cline" ? clineModels : providerId === "clinepass" ? clinepassModels : []; const liveModels = providerId === "cursor" ? cursorModels : providerId === "cline" ? clineModels : providerId === "clinepass" ? clinepassModels : providerId === "zed" ? zedModels : [];
const hardcodedModels = liveModels.length > 0 const hardcodedModels = liveModels.length > 0
? liveModels ? liveModels
: getModelsByProviderId(providerId); : getModelsByProviderId(providerId);
@@ -0,0 +1,80 @@
/**
* Tests for two independent frontend/capabilities fixes:
*
* #4293 — deepseek-v4-1-flash (hyphen) has vision=false
* Kenari exposes the model under the ID "deepseek-v4-1-flash" (dash instead
* of dot). The capabilities table only had "deepseek-v4.1-flash" (dot), so
* the hyphenated variant fell through to the generic *deepseek* pattern which
* has vision:false. Fix: add "deepseek-v4-1-flash" as an alias with the same
* vision:true entry.
*
* #4244 — Zed missing from SelectModelModal LIVE_CATALOG_PROVIDERS
* The hardcoded list ["cursor","cline","clinepass"] omitted "zed", so the
* Zed provider card was never fetched and never shown in the Combo picker.
* Fix: add "zed" to the list (and wire up the corresponding state/hook/ternary).
*/
import { describe, it, expect, beforeAll } from "vitest";
import { getCapabilitiesForModel } from "../../open-sse/providers/capabilities.js";
// ── #4293 deepseek-v4-1-flash vision capability ───────────────────────────
describe("deepseek-v4-1-flash capabilities (#4293)", () => {
it("reports vision:true for the hyphenated deepseek-v4-1-flash id (Kenari variant)", () => {
// Any provider that uses the hyphenated id should get vision:true
const caps = getCapabilitiesForModel("kenari", "deepseek-v4-1-flash");
expect(caps.vision).toBe(true);
});
it("still reports vision:true for the dotted deepseek-v4.1-flash id", () => {
const caps = getCapabilitiesForModel("ollama", "deepseek-v4.1-flash");
expect(caps.vision).toBe(true);
});
it("still reports reasoning:true for deepseek-v4-1-flash", () => {
const caps = getCapabilitiesForModel("kenari", "deepseek-v4-1-flash");
expect(caps.reasoning).toBe(true);
});
it("reports the correct contextWindow (1M) for deepseek-v4-1-flash", () => {
const caps = getCapabilitiesForModel("kenari", "deepseek-v4-1-flash");
expect(caps.contextWindow).toBe(1000000);
});
});
// ── #4244 LIVE_CATALOG_PROVIDERS includes zed ────────────────────────────
// ModelSelectModal.js is JSX so we cannot import it in Vitest without a
// JSX transform. Read the source text and verify the constant definition
// directly — this is reliable and does not require a full React setup.
import fs from "fs";
import path from "path";
describe("ModelSelectModal LIVE_CATALOG_PROVIDERS includes zed (#4244)", () => {
let src;
beforeAll(() => {
const fileUrl = new URL("../../src/shared/components/ModelSelectModal.js", import.meta.url);
src = fs.readFileSync(fileUrl, "utf-8");
});
it("includes zed in LIVE_CATALOG_PROVIDERS", () => {
// Match the const definition line and verify zed is present
const match = src.match(/const LIVE_CATALOG_PROVIDERS\s*=\s*\[([^\]]+)\]/);
expect(match).toBeTruthy();
const list = match[1];
expect(list).toContain('"zed"');
});
it("still includes cursor, cline, clinepass", () => {
const match = src.match(/const LIVE_CATALOG_PROVIDERS\s*=\s*\[([^\]]+)\]/);
const list = match[1];
expect(list).toContain('"cursor"');
expect(list).toContain('"cline"');
expect(list).toContain('"clinepass"');
});
it("zedModels hook call is present in the file", () => {
expect(src).toContain("zedModels");
expect(src).toContain("zedConnectionIds");
});
});