fix: include runway models in canvas video mode
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-21 00:18:26 +08:00
parent dae5581c76
commit b22c4c5c2f
2 changed files with 98 additions and 23 deletions

View File

@ -6,17 +6,13 @@ import {
getUserById,
} from "@/lib/db/queries";
import { buildVideoToolCapability } from "@/lib/ai/video-tool-capability";
import {
isImageGenerationModelDescriptor,
isVideoModelDescriptor,
} from "@/lib/ai/model-classification";
import { ChatSDKError } from "@/lib/errors";
import { isAiModelAgentAuto, isAiModelMultimodal } from "@/lib/db/ai-model-flags";
const VIDEO_PROVIDER_TYPES = new Set([
"volcano-video",
"doubao-video",
"qianwen-video",
"fal-video",
"ruxa-video",
]);
function isNanoBananaProModel(model: { displayName?: string | null; modelId?: string | null }) {
const displayName = String(model.displayName || "").toLowerCase();
const modelId = String(model.modelId || "").toLowerCase();
@ -103,14 +99,34 @@ export async function GET() {
}));
const preferredNanoBananaModel = models.find(
(model) =>
model.supportsImageGen &&
!VIDEO_PROVIDER_TYPES.has(model.providerType || "") &&
isImageGenerationModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) &&
!isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) &&
isNanoBananaProModel(model)
);
const preferredImageModel = models.find(
(model) =>
model.supportsImageGen &&
!VIDEO_PROVIDER_TYPES.has(model.providerType || "")
isImageGenerationModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) &&
!isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
})
);
const resolvedDefaultModelId =

View File

@ -47,6 +47,10 @@ import {
inferOriginalImageUrl,
} from "@/lib/image-url";
import { buildImageProxyUrl } from "@/lib/image-delivery";
import {
isImageGenerationModelDescriptor,
isVideoModelDescriptor,
} from "@/lib/ai/model-classification";
import type { Attachment, ChatMessage } from "@/lib/types";
import type {
AgentPreset,
@ -2844,16 +2848,25 @@ export function CanvasChatPanel({
setSelectedSkillTagId(null);
}, [isWorkspacePresentation, selectedSkillTagId]);
const VIDEO_PROVIDER_TYPES = new Set(["volcano-video", "doubao-video", "qianwen-video", "fal-video", "ruxa-video"]);
const visualModels = useMemo(() => {
const filtered =
modelsData?.models?.filter(
(model) =>
model.modelId !== "volcano-video-master" &&
(
model.supportsImageGen ||
isImageGenerationModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) ||
model.isMultimodal ||
VIDEO_PROVIDER_TYPES.has(model.providerType)
isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
})
)
) || [];
@ -2918,9 +2931,19 @@ export function CanvasChatPanel({
() =>
visualModels.filter(
(model) =>
model.supportsImageGen &&
isImageGenerationModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) &&
!model.isMultimodal &&
!VIDEO_PROVIDER_TYPES.has(model.providerType)
!isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
})
),
[visualModels]
);
@ -2931,23 +2954,59 @@ export function CanvasChatPanel({
model.id !== "auto" &&
model.id !== modelsData?.agentModel?.id &&
model.isMultimodal &&
!model.supportsImageGen &&
!VIDEO_PROVIDER_TYPES.has(model.providerType)
!isImageGenerationModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
}) &&
!isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
})
),
[allModels, modelsData?.agentModel?.id]
);
const videoOnlyModels = useMemo(
() => visualModels.filter((model) => VIDEO_PROVIDER_TYPES.has(model.providerType)),
() =>
visualModels.filter((model) =>
isVideoModelDescriptor({
providerType: model.providerType,
modelId: model.modelId,
capabilities: model.capabilities,
supportsImageGen: model.supportsImageGen,
})
),
[visualModels]
);
const currentModel = allModels.find((model) => model.id === selectedModelId);
const isVideoModel = Boolean(currentModel && VIDEO_PROVIDER_TYPES.has(currentModel.providerType));
const isVideoModel = Boolean(
currentModel &&
isVideoModelDescriptor({
providerType: currentModel.providerType,
modelId: currentModel.modelId,
capabilities: currentModel.capabilities,
supportsImageGen: currentModel.supportsImageGen,
})
);
const isConversationModel = Boolean(
currentModel &&
currentModel.id !== "auto" &&
currentModel.isMultimodal &&
!currentModel.supportsImageGen &&
!VIDEO_PROVIDER_TYPES.has(currentModel.providerType)
!isImageGenerationModelDescriptor({
providerType: currentModel.providerType,
modelId: currentModel.modelId,
capabilities: currentModel.capabilities,
supportsImageGen: currentModel.supportsImageGen,
}) &&
!isVideoModelDescriptor({
providerType: currentModel.providerType,
modelId: currentModel.modelId,
capabilities: currentModel.capabilities,
supportsImageGen: currentModel.supportsImageGen,
})
);
const isAgentModel =
!isDetailGenerationMode &&