

























@@ -85,6 +85,15 @@ function resolveConfiguredImageModelId(params: {
8585cfg: OpenClawConfig;
8686providerId: string;
8787}): string | undefined {
88+const configured = resolveConfiguredImageModel(params);
89+const id = configured?.id?.trim();
90+return id || undefined;
91+}
92+93+function resolveConfiguredImageModel(params: {
94+cfg: OpenClawConfig;
95+providerId: string;
96+}): { id?: string; input?: string[] } | undefined {
8897const providerCfg = findNormalizedProviderValue(
8998params.cfg.models?.providers,
9099params.providerId,
@@ -96,12 +105,10 @@ function resolveConfiguredImageModelId(params: {
96105}>;
97106}
98107| undefined;
99-const configured = providerCfg?.models?.find((entry) => {
108+return providerCfg?.models?.find((entry) => {
100109const id = entry?.id?.trim();
101110return Boolean(id) && entry?.input?.includes("image");
102111});
103-const id = configured?.id?.trim();
104-return id || undefined;
105112}
106113107114function resolveCatalogImageModelId(params: {
@@ -119,14 +126,38 @@ function resolveCatalogImageModelId(params: {
119126return normalizeOptionalString((autoEntry ?? matches[0])?.id);
120127}
121128129+async function explicitImageModelVisionStatus(params: {
130+cfg: OpenClawConfig;
131+providerId: string;
132+model: string;
133+}): Promise<"supported" | "unsupported" | "unknown"> {
134+const configured = resolveConfiguredImageModel(params);
135+if (configured?.id?.trim() === params.model && configured.input?.includes("image")) {
136+return "supported";
137+}
138+const catalog = await loadModelCatalog({ config: params.cfg });
139+const entry = findModelInCatalog(catalog, params.providerId, params.model);
140+if (!entry) {
141+return "unknown";
142+}
143+return modelSupportsVision(entry) ? "supported" : "unsupported";
144+}
145+122146async function resolveAutoImageModelId(params: {
123147cfg: OpenClawConfig;
124148providerId: string;
125149explicitModel?: string;
126150}): Promise<string | undefined> {
127151const explicit = normalizeOptionalString(params.explicitModel);
128152if (explicit) {
129-return explicit;
153+const explicitStatus = await explicitImageModelVisionStatus({
154+cfg: params.cfg,
155+providerId: params.providerId,
156+model: explicit,
157+});
158+if (explicitStatus !== "unsupported") {
159+return explicit;
160+}
130161}
131162const configuredModel = resolveConfiguredImageModelId(params);
132163if (configuredModel) {
@@ -498,13 +529,29 @@ function resolveImageModelFromAgentDefaults(cfg: OpenClawConfig): MediaUnderstan
498529return entries;
499530}
500531532+function hasExplicitImageUnderstandingConfig(params: {
533+cfg: OpenClawConfig;
534+config?: MediaUnderstandingConfig;
535+}): boolean {
536+return (
537+(params.config?.models?.length ?? 0) > 0 ||
538+resolveImageModelFromAgentDefaults(params.cfg).length > 0
539+);
540+}
541+501542async function resolveAutoEntries(params: {
502543cfg: OpenClawConfig;
503544agentDir?: string;
504545providerRegistry: ProviderRegistry;
505546capability: MediaUnderstandingCapability;
506547activeModel?: ActiveMediaModel;
507548}): Promise<MediaUnderstandingModelConfig[]> {
549+if (params.capability === "image") {
550+const imageModelEntries = resolveImageModelFromAgentDefaults(params.cfg);
551+if (imageModelEntries.length > 0) {
552+return imageModelEntries;
553+}
554+}
508555const activeEntry = await resolveActiveModelEntry(params);
509556if (activeEntry) {
510557return [activeEntry];
@@ -519,12 +566,6 @@ async function resolveAutoEntries(params: {
519566return [localAudio];
520567}
521568}
522-if (params.capability === "image") {
523-const imageModelEntries = resolveImageModelFromAgentDefaults(params.cfg);
524-if (imageModelEntries.length > 0) {
525-return imageModelEntries;
526-}
527-}
528569const gemini = await resolveGeminiCliEntry(params.capability);
529570if (gemini) {
530571return [gemini];
@@ -553,6 +594,12 @@ export async function resolveAutoImageModel(params: {
553594}
554595return { provider, model };
555596};
597+const configuredImageModel = resolveImageModelFromAgentDefaults(params.cfg)
598+.map((entry) => toActive(entry))
599+.find((entry): entry is ActiveMediaModel => entry !== null);
600+if (configuredImageModel) {
601+return configuredImageModel;
602+}
556603const activeEntry = await resolveActiveModelEntry({
557604cfg: params.cfg,
558605agentDir: params.agentDir,
@@ -772,7 +819,11 @@ export async function runCapability(params: {
772819// Skip image understanding when the primary model supports vision natively.
773820// The image will be injected directly into the model context instead.
774821const activeProvider = params.activeModel?.provider?.trim();
775-if (capability === "image" && activeProvider) {
822+if (
823+capability === "image" &&
824+activeProvider &&
825+!hasExplicitImageUnderstandingConfig({ cfg, config })
826+) {
776827const catalog = await loadModelCatalog({ config: cfg });
777828const entry = findModelInCatalog(catalog, activeProvider, params.activeModel?.model ?? "");
778829if (modelSupportsVision(entry)) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。