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
26 changes: 26 additions & 0 deletions src/providers/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,30 @@ const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
* `preserveReasoningContentModels`, where flash DOES belong.
*/
const ZAI_GLM_5X_SIDECAR_VISION_MODELS = ZAI_GLM_5X_MODELS.filter(id => id !== "glm-5.3-flash");
/**
* Positive input-modality declaration for the Chat-path GLM rows.
*
* `noVisionModels` already keeps Flash out of the vision sidecar, but that is a NEGATIVE
* statement: it stops a detour without telling the catalog what the model can read. With
* no `modelInputModalities` entry, `configuredInputModalities` returns undefined and the
* catalog falls through to the `["text"]` floor, so every client export (ZCode, Pi, OMP)
* listed a native VLM as text-only and its picker refused to attach an image.
*
* The Responses sibling row below already declares this positively, so the same model was
* described two different ways in one registry.
*
* Authoritative source: `GET https://api.z.ai/api/v1/models` returns `input_modalities:
* ["text"]` for glm-5.3 and `["text", "image"]` for glm-5.3-flash (captured in
* devlog/_plan/260912_zcode_protocol_and_catalog/evidence/zai-responses-models.json).
* docs.z.ai/devpack/latest-model says the same in prose: "GLM-5.3 is a text-only model...
* GLM-5.3-FLASH is a multimodal model". Upstream also lists video and file for Flash;
* neither the internal vocabulary nor the export vocabulary can express them, so `image`
* is where this stops.
*/
const ZAI_GLM_5X_INPUT_MODALITIES: Record<string, string[]> = {
...Object.fromEntries(ZAI_GLM_5X_SIDECAR_VISION_MODELS.map(id => [id, ["text"]])),
"glm-5.3-flash": ["text", "image"],
};
const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
/**
* GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
Expand Down Expand Up @@ -2604,6 +2628,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
// Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
modelSuffixBracketStrip: true,
noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
modelInputModalities: ZAI_GLM_5X_INPUT_MODALITIES,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Document image support for the Chat presets

This changes user-visible catalog and client-export behavior for both zai and zhipu-bigmodel-coding: their pickers now permit native image attachments to glm-5.3-flash. The provider guide currently documents this capability only inside the separate BigModel Responses section, so users of either Chat preset have no corresponding documentation; update docs-site/ to describe the newly exposed Chat-path behavior.

AGENTS.md reference: src/AGENTS.md:L29-L29

Useful? React with 👍 / 👎.

modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
Expand Down Expand Up @@ -2685,6 +2710,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
modelSuffixBracketStrip: true,
noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
modelInputModalities: ZAI_GLM_5X_INPUT_MODALITIES,
modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
Expand Down
15 changes: 15 additions & 0 deletions tests/providers/provider-registry-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,21 @@ describe("provider registry parity", () => {
// The sibling it is most often confused with stays text-only, so the assertion above
// cannot pass by making every GLM row a VLM.
expect(zai?.noVisionModels ?? []).toContain("glm-5.3");
// The global loop above accepts an ABSENT declaration, which is exactly how the Chat
// rows shipped: Flash was kept out of the sidecar but never told the catalog it could
// read an image, so `configuredInputModalities` returned undefined and every client
// export listed a native VLM as text-only. Pin the positive declaration so the two
// Chat rows cannot drift back to describing Flash only by what it is not.
for (const id of ["zai", "zhipu-bigmodel-coding"] as const) {
const row = PROVIDER_REGISTRY.find(entry => entry.id === id);
expect(row?.modelInputModalities?.["glm-5.3-flash"]).toEqual(["text", "image"]);
expect(row?.modelInputModalities?.["glm-5.3"]).toEqual(["text"]);
// The bracketed aliases are looked up by their exact catalog id, not a stripped one,
// so they need their own text-only entries.
expect(row?.modelInputModalities?.["glm-5.3[1m]"]).toEqual(["text"]);
expect(row?.modelInputModalities?.["glm-5.2[1m]"]).toEqual(["text"]);
expect(row?.noVisionModels ?? []).not.toContain("glm-5.3-flash");
}
// `glm-5.3-flash` belongs in all three maps. It was seeded into the model list
// and the context map alone, so it advertised a 1M window with no effort ladder,
// no default effort and no output cap - and this assertion pinned that gap in
Expand Down
Loading