From 906eee4da715768dc56d02a6a7772c878c0d6bb4 Mon Sep 17 00:00:00 2001 From: GreifMax <142740995+GreifMax@users.noreply.github.com> Date: Wed, 26 Aug 2026 00:00:54 +0200 Subject: [PATCH] Restore configurable Vision Model call limits Expose timeout and max-token controls only in the visible Vision sidecar advanced settings, defaulting to 300 seconds and 2000 tokens, and pass them through the model builder. Point prompt customization to Agent Editor and promote legacy kwargs into the dedicated preset fields. --- plugins/_model_config/AGENTS.md | 1 + plugins/_model_config/helpers/model_config.py | 16 ++- .../_model_config/webui/model-config-store.js | 25 ++++- plugins/_model_config/webui/model-field.html | 38 +++++-- tests/test_model_config_project_presets.py | 98 +++++++++++++++++++ tests/test_model_config_ui.py | 21 ++++ 6 files changed, 188 insertions(+), 11 deletions(-) diff --git a/plugins/_model_config/AGENTS.md b/plugins/_model_config/AGENTS.md index f6350ea11..1c4dce812 100644 --- a/plugins/_model_config/AGENTS.md +++ b/plugins/_model_config/AGENTS.md @@ -26,6 +26,7 @@ - The optional `vision` slot is strictly per preset and never inherited from `Default`; an empty slot disables the separate Vision Model for that preset. - Main native vision wins by default. A configured Vision Model handles `vision_load` when Main lacks vision, or when that preset explicitly enables `override_main`. - Keep the optional Vision provider/model selector inside the Main Model card and flush with Main's field alignment, without a nested left inset. Show it only while Main vision is disabled or `override_main` is enabled; do not render a standalone Vision Model card. +- Keep Vision timeout and maximum-output-token controls, plus the Agent Editor prompt-customization note, inside the visible Vision sidecar's Advanced Settings only. The Vision call limits belong to the preset/model builder, not to `vision_load` call-site constants. - Show `Use separate Vision Model` immediately below `Supports Vision` while Main vision is enabled, not inside Advanced Settings; describe the disabled state as using Main's native vision. - In model overviews, render the effective Vision Model as a text-only `Vision override / Provider / Model` child aligned with Main's provider column, not as an icon-bearing peer row. - Changing a model provider in the settings UI must clear `api_base` and `kwargs` because both may be provider-specific. diff --git a/plugins/_model_config/helpers/model_config.py b/plugins/_model_config/helpers/model_config.py index 0c6f539c2..0aa73a37f 100644 --- a/plugins/_model_config/helpers/model_config.py +++ b/plugins/_model_config/helpers/model_config.py @@ -11,6 +11,8 @@ PRESETS_FILE = "presets.yaml" FALLBACK_PRESETS_FILE = "mode_presets_fallback.yaml" PROVIDER_METADATA_FILE = "provider_metadata.yaml" DEFAULT_PRESET_NAME = "Default" +DEFAULT_VISION_TIMEOUT_SECONDS = 300 +DEFAULT_VISION_MAX_TOKENS = 2000 MODEL_PRESET_CONFIG_KEY = "model_preset" PRESET_SCOPE_GLOBAL = "global" PRESET_SCOPE_PROJECT = "project" @@ -25,6 +27,8 @@ IMPLICIT_PRESET_SLOT_DEFAULTS = { "vision": { "vision": True, "max_embeds": 10, + "timeout": DEFAULT_VISION_TIMEOUT_SECONDS, + "max_tokens": DEFAULT_VISION_MAX_TOKENS, "override_main": False, "rl_requests": 0, "rl_input": 0, @@ -873,8 +877,18 @@ def build_vision_model(agent=None): cfg = get_vision_model_config(agent) mc = build_model_config(cfg, models.ModelType.CHAT) mc.vision = True + kwargs = mc.build_kwargs() + for key, default in ( + ("timeout", DEFAULT_VISION_TIMEOUT_SECONDS), + ("max_tokens", DEFAULT_VISION_MAX_TOKENS), + ): + value = cfg.get(key) + if value not in (None, ""): + kwargs[key] = _normalize_kwargs({key: value})[key] + else: + kwargs.setdefault(key, default) return models.get_chat_model( - mc.provider, mc.name, model_config=mc, **mc.build_kwargs() + mc.provider, mc.name, model_config=mc, **kwargs ) diff --git a/plugins/_model_config/webui/model-config-store.js b/plugins/_model_config/webui/model-config-store.js index 1ab6d8c8e..74c723132 100644 --- a/plugins/_model_config/webui/model-config-store.js +++ b/plugins/_model_config/webui/model-config-store.js @@ -63,6 +63,8 @@ const IMPLICIT_PRESET_SLOT_DEFAULTS = { vision: { vision: true, max_embeds: 10, + timeout: 300, + max_tokens: 2000, override_main: false, rl_requests: 0, rl_input: 0, @@ -222,7 +224,23 @@ export const store = createStore("modelConfig", { const source = (rawPresets || []).filter(p => p && typeof p === 'object'); const rawDefault = source.find(p => String(p.name || '').toLowerCase() === 'default') || {}; const slot = value => ({ provider: '', name: '', api_key: '', api_base: '', kwargs: {}, ...(value || {}) }); - const visionSlot = value => ({ ...slot(value), vision: true, max_embeds: Number(value?.max_embeds ?? 10), override_main: !!value?.override_main }); + const visionSlot = value => { + const normalized = slot(value); + const kwargs = { ...(normalized.kwargs || {}) }; + const timeout = Number(value?.timeout ?? kwargs.timeout ?? 300); + const maxTokens = Number(value?.max_tokens ?? kwargs.max_tokens ?? 2000); + delete kwargs.timeout; + delete kwargs.max_tokens; + return { + ...normalized, + vision: true, + max_embeds: Number(value?.max_embeds ?? 10), + timeout, + max_tokens: maxTokens, + override_main: !!value?.override_main, + kwargs, + }; + }; const defaultConfig = { chat_model: slot(rawDefault.chat), vision_model: hasModelIdentity(rawDefault.vision) ? visionSlot(rawDefault.vision) : {}, @@ -234,10 +252,11 @@ export const store = createStore("modelConfig", { const effective = String(p.name || '').toLowerCase() === 'default' ? defaultConfig : configFromPreset(p, defaultConfig, true); + const vision = visionSlot(effective.vision_model); return { name: p.name || '', chat: { ...slot(effective.chat_model), _kwargs_text: kwargsToText(effective.chat_model?.kwargs) }, - vision: { ...visionSlot(effective.vision_model), _kwargs_text: kwargsToText(effective.vision_model?.kwargs) }, + vision: { ...vision, _kwargs_text: kwargsToText(vision.kwargs) }, utility: { ...slot(effective.utility_model), _kwargs_text: kwargsToText(effective.utility_model?.kwargs) }, embedding: { ...slot(effective.embedding_model), _kwargs_text: kwargsToText(effective.embedding_model?.kwargs) }, }; @@ -360,7 +379,7 @@ export const store = createStore("modelConfig", { || this.presets[0] || { chat: { provider: '', name: '', api_base: '', kwargs: {}, _kwargs_text: '' }, - vision: { provider: '', name: '', api_base: '', vision: true, max_embeds: 10, override_main: false, kwargs: {}, _kwargs_text: '' }, + vision: { provider: '', name: '', api_base: '', vision: true, max_embeds: 10, timeout: 300, max_tokens: 2000, override_main: false, kwargs: {}, _kwargs_text: '' }, utility: { provider: '', name: '', api_base: '', kwargs: {}, _kwargs_text: '' }, embedding: { provider: '', name: '', api_base: '', kwargs: {}, _kwargs_text: '' }, } diff --git a/plugins/_model_config/webui/model-field.html b/plugins/_model_config/webui/model-field.html index 51c70741a..a0703ddb3 100644 --- a/plugins/_model_config/webui/model-field.html +++ b/plugins/_model_config/webui/model-field.html @@ -9,7 +9,7 @@ ') + vision_advanced_start = model_field.index('