feat(caps): user-registered models trust upstream vision instead of stripping media

This commit is contained in:
2026-08-22 14:33:53 +07:00
parent 144dda2ac2
commit bb8d67ba9c
3 changed files with 23 additions and 4 deletions

View File

@@ -57,7 +57,7 @@ export function stripContinuityFields(body) {
return body;
}
export async function handleChatCore({ body, modelInfo, credentials, log, onCredentialsRefreshed, onRequestSuccess, onDisconnect, clientRawRequest, connectionId, userAgent, apiKey, ccFilterNaming, rtkEnabled, headroomEnabled, headroomUrl, headroomCompressUserMessages, cavemanEnabled, cavemanLevel, ponytailEnabled, ponytailLevel, pxpipeEnabled, pxpipeMinChars, pxpipeTimeoutMs, pxpipeTransform, onPxpipeEvent, sourceFormatOverride, providerThinking }) {
export async function handleChatCore({ body, modelInfo, credentials, log, onCredentialsRefreshed, onRequestSuccess, onDisconnect, clientRawRequest, connectionId, userAgent, apiKey, ccFilterNaming, rtkEnabled, headroomEnabled, headroomUrl, headroomCompressUserMessages, cavemanEnabled, cavemanLevel, ponytailEnabled, ponytailLevel, pxpipeEnabled, pxpipeMinChars, pxpipeTimeoutMs, pxpipeTransform, onPxpipeEvent, sourceFormatOverride, providerThinking, capsOverride }) {
const { provider, model } = modelInfo;
const requestStartTime = Date.now();
// Stable per-session color so all lines of one CLI conversation share a tag
@@ -149,8 +149,10 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
if (credentials) credentials.rawHeaders = clientRawRequest?.headers || {};
// Auto-strip media blocks the model can't read (vision/audio/pdf) before translation.
// capsOverride lets the app layer assert per-model capabilities (e.g. user-registered
// models) on top of the static tables.
if (!passthrough) {
const caps = getCapabilitiesForModel(provider, model);
const caps = { ...getCapabilitiesForModel(provider, model), ...(capsOverride || {}) };
if (stripUnsupportedModalities(body, sourceFormat, caps)) {
log?.debug?.("MODALITY", `stripped unsupported media for ${provider}/${model}`);
}

View File

@@ -317,6 +317,11 @@ export const PATTERN_CAPABILITIES = [
{ pattern: "*ling-*", caps: { reasoning: true, contextWindow: 128000 } },
];
// OpenRouter-style gateways validate modalities upstream — a text-only model
// sent an image gets a clear upstream error instead of silent corruption. So for
// unknown models on these providers, trust vision instead of stripping images.
const TRUST_UPSTREAM_VISION = new Set(["openrouter"]);
/**
* Resolve capabilities for a model using the 4-step fallback chain,
* merged over DEFAULT_CAPABILITIES so the result is always complete.
@@ -349,6 +354,9 @@ export function getCapabilitiesForModel(provider, model) {
}
}
// 4. Floor
// 4. Floor (upstream-validated gateways keep vision on for unknown models)
if (provider && TRUST_UPSTREAM_VISION.has(provider)) {
return { ...DEFAULT_CAPABILITIES, vision: true };
}
return { ...DEFAULT_CAPABILITIES };
}