Replies: 1 comment
|
Source-verified against master (
Your key new fact changes that calculus. OpenRouter
Your proposals 2 and 3 are exactly right and cheap: the per-model One framing note for whoever picks this up: the three threads are one family with two distinct fixes — (a) declaration-surface: make editors/documentation expose the modality fields (#5367's fork shape + your proposals 2–3), and (b) resolution-source: let catalog-confirmed modalities fill the gap when undeclared (your proposal 1, now enabled by vendor catalogs). (b) is the bigger behavioral change and deserves its own design pass because it flips the fail-closed default for catalog-known models; (a) is uncontroversial. The modlens |
Uh oh!
There was an error while loading. Please reload this page.
The problem
For custom provider entries under
llm-pi-ai.providers.*.models[](OpenRouter, NVIDIA NIM, Empero, custom openai-completions routes), a model's resolvedinputModalitiesresolve through a chain:inputfield on the entry,base?.input,defaultInput,["text"](the default).The image-admission gate (
dsh-api-session-controller, errorsession/attachment-invalid, reasonMODEL_DOES_NOT_SUPPORT_IMAGES) trusts only that resolved value, so an undeclared multimodal model is silently treated as text-only and attachments are refused at send time with "select a vision-enabled model." The model itself sees fine; the harness metadata is what's wrong.In one settings.yaml audit I hit five routes in this state, all confirmed multimodal against the vendor's own docs:
z-ai/glm-5.3-flash(OpenRouter + Empero) — Z.ai's VLM guide and "Z.ai's first natively multimodal open-weights model".qwen/qwen3.7-flash(OpenRouter) — OpenRouter model card, "Alibaba's cheap vision model lands on OpenRouter".minimaxai/minimax-m3(NVIDIA) andminimax/minimax-m3:free(OpenRouter) — MiniMax M3: 1M context, native multimodality.google/gemma-4-26b-a4b-it:freeandgoogle/gemma-4-31b-it:free(OpenRouter) — Gemma 4 is multimodal (vision input documented in the model card).nvidia/nemotron-3-nano-omni-30b-a3b-reasoning(NVIDIA) — Nemotron Omni is multimodal (omni = text + image + audio; the -omni suffix in the model id is the signal).Each needed a hand edit of
~/.dsh/settings.yamlto addinput: [text, image]. None of this is surfaced in the GUI; users hit the admission error and have to reverse-engineer the metadata schema. The modlens plugin even special-casesglm-5.3-flashas a native vision model (itsVISION_IDregex lists it explicitly), which is independent evidence that the model is multimodal and the harness is just uninformed.Proposed change
Auto-merge the provider's live catalog modalities into the resolved model. OpenRouter's
/api/v1/modelsreturnsarchitecture.modality/input_modalitiesper model; NVIDIA NIM's catalog returns modality per model. Whenllm-pi-aibuilds the catalogbasefor a model, mergeinput_modalitiesinto the resolvedinputModalitiesunless the user has explicitly overridden via the per-modelinputfield. The fallback chain becomes: per-modelinput> catalog modality >defaultInput>["text"]. This makes "no declaration = catalog default" instead of "no declaration = text-only," and turns the most common case (the model is in the catalog, the catalog is correct) into zero-config.Document the per-model
inputfield. The field exists in the zod schema for thellm-pi-aipackage and accepts aMODALITIESunion (text, image, audio, video) — but it isn't in the dsh readme, isn't in a settings example, and isn't surfaced in the GUI. Document it next to (or instead of) thellm-deepseekplugin'sinputModalitiesfield. The spelling difference between the two provider plugins (inputvsinputModalities) is a real footgun and not obvious from the file structure.Surface vision-capable routes in the model selector. A small badge/column showing whether each entry declares image input, and whether the live catalog confirms it. This is the discoverability fix that turns "select a vision-enabled model" from a guessing game into something users can see and act on. It also surfaces the catalog-vs-declaration drift so power users know which entries to override.
Scope check
Pure metadata plumbing; no change to provider adapters, the image admission gate, or the image read/transform pipeline. The admission gate already handles
inputModalities: [text, image]correctly — it's the gate that refused these five cases, and it would simply stop refusing once the metadata is right. Every existing override mechanism (per-modelinput,defaultInput) stays intact; the change is purely additive to the fallback chain.Alternatives considered
inputinsettings.yaml. Works, not discoverable, and breaks every new multimodal model a user adds until they notice the admission error.llm-deepseek) per custom provider. Doesn't scale;llm-pi-aiis the right home and already has the catalog fetch surface to extend.Environment
0.1.2-alpha.5(web profile,~/.dsh/profiles/web)@deepseek-ai/dsh-llm-pi-ai(current installed version)llm-pi-ai.providers.{nvidia, openrouter-live, empero}mounted insettings.yaml; models span text-only and multimodal in each provider.My notes for you (not part of the issue)
MODALITIESunion and the per-modelinputfield are in the installedllm-pi-aipackage's zod schema (the zod field isinput: z.array(z.union(MODALITIES)); the defaultdefaultInputis["text"]). They are functional, just not surfaced.llm-deepseekplugin usesinputModalities(plural) instead ofinput. That's a different plugin with a different field name for the same concept, which is exactly the cross-plugin footgun the issue calls out.llm-pi-aiproviders (thebaselayer is the catalog fetch). The change is to readarchitecture.modality/ equivalent from the fetched model and merge it into the resolvedinputModalities— small, contained, no new dependency.All reactions