vision-control
Name, description, and notes are shown in the submitted language and are not translated.
管理 llm-pi-ai 按模型的图片视觉开关;只写自有来源标记的配置,不覆盖用户声明。
For agents: Markdown · JSON · tarball
Install on a machine that already has the plugin market
curl -fsSL https://addon.dshdo.com/install-plugin-market.sh | sh
# then in /plugin-market/ install vision-control and enable it
Open /plugin-market/ → Cloud catalog, install this id, then enable. Install only registers; enable runs it.
Versions
0.1.4· Published0.1.3· Published — 0.1.3: packaging-only release. The 0.1.2 archive unintentionally carried an AppleDouble sidecar (._cordis.patch.yml) from the platform tar tool0.1.2· Published — 0.1.2: apply is now a non-constructible arrow so Cordis fiber runner collects and awaits its disposer — unload waits for an in-flight settings.replace and writes nothing after dispose. Real-Cordis/Loader fixture regression test added.0.1.1· Published — 0.1.1: ownership/provenance rewrite — write only what this plugin provably owns (marker-gated), verify catalog-route disables against the authoritative llm adapter, async dispose awaiting in-flight reconcile; lost-adapter re-arm + repeated-dispose fixed.0.1.0· Published — 首个版本:默认全开 llm-pi-ai 各路由图片输入(defaultInput),支持按 provider/模型手动关闭(disabled),双向收敛与幂等。
Files
README
vision-control
Out-of-tree Cordis plugin that manages the llm-pi-ai image-vision switch per model (no main-program changes, no UI — a pure configuration plane).
Version: 0.1.3
Purpose
Writes to the llm-pi-ai user section through dsh-settings, doing two things:
- Vision on by default: when a route declares no
defaultInputin either
the user section or the composition base layer, write route-level defaultInput: [text, image]. A declared defaultInput (even [text]) is never overwritten.
- Vision off per model: for models listed in
disabled, write `input:
[text] to switch vision off; when a model leaves disabled`, undo only the entry this plugin wrote itself.
Ownership and undo rules (since 0.1.1)
The plugin writes only fields it "owns", and records every write with a deterministic provenance marker:
- Disabling a model on a hand-declared route (a route whose config carries a
models list) writes input: [text] only when the model entry declares no input, together with the provenance marker _visionControl: { input: true }.
- Disabling a model on a catalog route (no
modelslist; served from the
installed pi-ai catalog) writes modelOverrides.<id>: { input: [text], _visionControl: { input: true } } only when the authoritative host model catalog does describe that model on that route.
- Writing route-level
defaultInput: [text, image]also records the marker
_visionControl: { defaultInput: true } on the route object.
- When a disable is lifted or
defaultImageis set tofalse, **only marker
verified writes are removed**. Any unmarked input / defaultInput — including a user-authored input: [text] — is treated as user-owned and is never automatically deleted or rewritten.
- If the user later edits a value under a marker (an
inputor a route
defaultInput), the plugin releases ownership of that entry (removes the marker, keeps the user's value) and will not auto-undo it.
- A route that exists only in the resolved layer (never user-declared) is never
materialized into the user document.
- Idempotent: repeated loads and repeated reconciles have no side effects;
config validation failures error at load, never silently skipped.
Catalog-route disable verification
Before disabling a model on a catalog route, the plugin verifies membership through the runtime llm service (ctx.get('llm')): the adapter that service materializes for that route is exactly the set of models for which llm-pi-ai would accept a modelOverrides entry. When membership cannot be verified (no llm service, no adapter for the route, or a catalog error), the disable is explicitly refused and logged — never silently treated as applied, which would leave a model looking vision-off while still on. Adapter registration often trails this plugin's mount or a settings edit, so the plugin listens for llm/adapters-updated: a refused catalog disable retries automatically once the adapter is ready instead of staying unapplied.
0.1.3 packaging-only release note
0.1.3 is a packaging-only release: identical plugin code and behavior to 0.1.2. The 0.1.2 archive unintentionally carried an AppleDouble sidecar (._cordis.patch.yml) created by the platform tar tool from a local extended attribute; 0.1.3 is built with the maintained node-tar writer (portable), which never stores macOS filesystem metadata, so the archive contains exactly the intended payload. The 0.1.2 release and its public archive remain immutable history.
0.1.2 fix-release note
0.1.2 fixes a real-framework disposal bug: apply was a constructible function, so Cordis' fiber runner newed it and discarded the async disposer it returns — the "dispose awaits an in-flight settings.replace" guarantee silently never registered when the plugin was unloaded by the actual Loader. apply is now an arrow function (export const apply = …), which Cordis calls directly and whose disposer it collects and awaits. Behavior is otherwise identical to 0.1.1; the 0.1.1 ownership/undo rules and the migration notes below all still apply. The regression is covered by a real-Cordis/Loader fixture test that holds a settings.replace in flight and proves the fiber dispose waits for it, with nothing written after disposal.
0.1.0 → 0.1.1 migration notes
0.1.0's automatic sync deleted user-authored input: [text] / modelOverrides and wrote its values without provenance markers (including route-level defaultInput). After upgrading, which values were originally user-authored cannot be recovered: every unmarked existing value is treated as "user-owned" and is never auto-removed by this plugin (setting defaultImage to false will not undo a route defaultInput that 0.1.0 wrote). If 0.1.0 removed an explicit declaration of yours, restore it by hand (for example, rewrite a model's input as ['text']).
Configuration
In the plugin entry's config (a manual cordis.patch.yml or your config source):
- insert:
- id: vision-control
name: vision-control
config:
defaultImage: true # vision on by default (defaults to true)
disabled: # per-model vision off (optional)
seawork: [ gpt-5.6-terra ]
grok: [ grok-imagine ]
defaultImage: boolean, defaults totrue. Whenfalse, route-level
defaults are not written, and undoes route defaultInput values this plugin wrote earlier that still carry the marker (the schema's ['text'] floor resumes); user-declared or 0.1.0-era unmarked values are unaffected.
disabled: an array of model ids (applies to every route), or a
provider-keyed map; * matches any route:
config:
disabled:
seawork: [ gpt-5.6-terra ]
'*': [ shared-model ]
Behavior boundaries
- Writes only the
llm-pi-ainamespace's user configuration section; does not
read or write other namespaces and does not touch the main program.
- Disable writes act only on hand-declared-route model entries or
catalog-route modelOverrides, never writing modelOverrides for a model also listed in a models array (llm-pi-ai rejects that combination).
llm-deepseekruns through its own adapter and is outside this plugin's
scope.
- This plugin is not a vision-content gate; it only keeps
llm-pi-ai's model
capability declarations (input / defaultInput) consistent with your configuration intent.