fix: recognize common multimodal text models

This commit is contained in:
Codex
2026-09-19 20:40:55 +08:00
parent 5843d489c6
commit 10b85962b6
4 changed files with 68 additions and 5 deletions

View File

@@ -105,6 +105,14 @@ export const STANDARD_IMAGE_SIZE_VALUES = [
"1024x1536",
] as const;
const DEFAULT_TEXT_REFERENCE_MAX_IMAGES = 16;
function isKnownMultimodalTextModel(protocol?: ModelProtocol, model = "") {
// These model families currently expose vision through their text/chat
// endpoint, including when they are wrapped by an OpenAI-compatible gateway.
return /gemini|gpt|doubao|豆包/i.test(model) || protocol === "gemini";
}
export function normalizeCapabilityString(value: string) {
const normalized = value.trim();
return normalized.startsWith("string:") ? normalized.slice("string:".length) : normalized;
@@ -286,8 +294,13 @@ export function defaultImageCapabilityConfig(protocol?: ModelProtocol, model = "
export function defaultModelCapabilityConfig(protocol?: ModelProtocol, model = ""): ModelCapabilityConfig {
const text: TextCapabilityConfig = {
streaming: true,
// 文本模型的视觉能力必须由管理员明确开启,不能根据模型名猜测。
references: { promptMaxChars: 32000, maxImages: 0, maxImageBytes: 0, maxVideos: 0, maxVideoBytes: 0 },
references: {
promptMaxChars: 32000,
maxImages: isKnownMultimodalTextModel(protocol, model) ? DEFAULT_TEXT_REFERENCE_MAX_IMAGES : 0,
maxImageBytes: isKnownMultimodalTextModel(protocol, model) ? 30 * 1024 * 1024 : 0,
maxVideos: 0,
maxVideoBytes: 0,
},
};
const video: VideoCapabilityConfig = {
references: {
@@ -398,7 +411,10 @@ export function modelCapabilityConfigFor(config: { channels: Array<{ id: string;
const fallback = defaultModelCapabilityConfig(cost?.protocol, modelName);
if (!cost?.capabilityConfig) return fallback;
const capabilityConfig = normalizeModelCapabilityConfig(cost.capabilityConfig);
const text = capabilityConfig.text ? { ...fallback.text!, ...capabilityConfig.text, references: { ...fallback.text!.references, ...capabilityConfig.text.references } } : fallback.text;
const mergedText = capabilityConfig.text ? { ...fallback.text!, ...capabilityConfig.text, references: { ...fallback.text!.references, ...capabilityConfig.text.references } } : fallback.text;
const text = mergedText && isKnownMultimodalTextModel(cost?.protocol, modelName) && mergedText.references.maxImages === 0
? { ...mergedText, references: { ...mergedText.references, maxImages: DEFAULT_TEXT_REFERENCE_MAX_IMAGES, maxImageBytes: mergedText.references.maxImageBytes || 30 * 1024 * 1024 } }
: mergedText;
const video = capabilityConfig.video ? { ...fallback.video!, ...capabilityConfig.video, references: { ...fallback.video!.references, ...capabilityConfig.video.references } } : fallback.video;
const configuredImage = capabilityConfig.image;
const image = configuredImage

View File

@@ -31,3 +31,16 @@ test("raising the video default leaves text and image limits untouched", () => {
assert.equal(profile.text!.references.promptMaxChars, 32000);
assert.equal(profile.image!.references.promptMaxChars, 32000);
});
test("known multimodal text model families default to image input", () => {
const gemini = defaultModelCapabilityConfig("chat-completion", "gemini-3.8-flash-high").text!;
const gpt = defaultModelCapabilityConfig("chat-completion", "gpt-4o").text!;
const doubao = defaultModelCapabilityConfig("chat-completion", "doubao-seed-1.6-flash").text!;
const plain = defaultModelCapabilityConfig("chat-completion", "plain-text-model").text!;
assert.equal(gemini.references.maxImages, 16);
assert.equal(gemini.references.maxImageBytes, 30 * 1024 * 1024);
assert.equal(gpt.references.maxImages, 16);
assert.equal(doubao.references.maxImages, 16);
assert.equal(plain.references.maxImages, 0);
});