fix: lock workflow hidden models
This commit is contained in:
parent
35c4c02579
commit
83c274e8d4
@ -4,6 +4,7 @@ import {
|
||||
getDefaultAiModel,
|
||||
getEnabledAiModelsWithProvider,
|
||||
getSiteSettings,
|
||||
getSystemSetting,
|
||||
getUserById,
|
||||
} from "@/lib/db/queries";
|
||||
import { buildVideoToolCapability } from "@/lib/ai/video-tool-capability";
|
||||
@ -13,6 +14,11 @@ import {
|
||||
} from "@/lib/ai/model-classification";
|
||||
import { ChatSDKError } from "@/lib/errors";
|
||||
import { isAiModelAgentAuto, isAiModelMultimodal } from "@/lib/db/ai-model-flags";
|
||||
import {
|
||||
AGENT_WORKFLOW_UI_CONFIG_KEY,
|
||||
getAgentWorkflowBusinessModeSettings,
|
||||
normalizeAgentWorkflowUiConfig,
|
||||
} from "@/lib/agents/workflow-ui-config";
|
||||
|
||||
function isNanoBananaProModel(model: { displayName?: string | null; modelId?: string | null }) {
|
||||
const displayName = String(model.displayName || "").toLowerCase();
|
||||
@ -39,6 +45,28 @@ function sortModelsForDisplay<T extends { sortOrder?: number | null; displayName
|
||||
});
|
||||
}
|
||||
|
||||
function isWorkflowImageGenerationModel(model: {
|
||||
providerType?: string | null;
|
||||
modelId?: string | null;
|
||||
capabilities?: unknown;
|
||||
supportsImageGen?: boolean | null;
|
||||
}) {
|
||||
return (
|
||||
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,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 获取前端可用的 AI 模型列表(需要登录但不需要管理员权限)
|
||||
export async function GET() {
|
||||
try {
|
||||
@ -56,27 +84,12 @@ export async function GET() {
|
||||
const modelsWithProvider = await getEnabledAiModelsWithProvider(false, true);
|
||||
const agentModelWithProvider = await getAgentAiModelWithProvider();
|
||||
const defaultModel = await getDefaultAiModel();
|
||||
const siteSettings = await getSiteSettings();
|
||||
const visibleModelsWithProvider = sortModelsForDisplay(
|
||||
modelsWithProvider
|
||||
.filter(
|
||||
({ model }) =>
|
||||
!isAiModelAgentAuto(model) &&
|
||||
(allowHiddenModels || !model.isHidden)
|
||||
)
|
||||
.map((item) => item.model)
|
||||
);
|
||||
const visibleModelIdSet = new Set(visibleModelsWithProvider.map((m) => m.id));
|
||||
const visibleModelsWithProviderSorted = modelsWithProvider.filter(({ model }) =>
|
||||
visibleModelIdSet.has(model.id)
|
||||
).sort((a, b) => {
|
||||
const left = visibleModelsWithProvider.findIndex((m) => m.id === a.model.id);
|
||||
const right = visibleModelsWithProvider.findIndex((m) => m.id === b.model.id);
|
||||
return left - right;
|
||||
});
|
||||
|
||||
// 转换为前端需要的格式
|
||||
const models = visibleModelsWithProviderSorted.map(({ model, provider }) => ({
|
||||
const [siteSettings, agentWorkflowUiConfigSetting] = await Promise.all([
|
||||
getSiteSettings(),
|
||||
getSystemSetting({ key: AGENT_WORKFLOW_UI_CONFIG_KEY }).catch(() => null),
|
||||
]);
|
||||
type ModelWithProvider = (typeof modelsWithProvider)[number];
|
||||
const serializeModel = ({ model, provider }: ModelWithProvider) => ({
|
||||
id: model.id,
|
||||
modelId: model.modelId,
|
||||
displayName: model.displayName,
|
||||
@ -101,7 +114,42 @@ export async function GET() {
|
||||
}),
|
||||
imageGenConfig: model.imageGenConfig,
|
||||
sortOrder: model.sortOrder ?? 0,
|
||||
}));
|
||||
});
|
||||
const workflowImageModelById = new Map<string, ModelWithProvider>();
|
||||
modelsWithProvider.forEach((entry) => {
|
||||
const { model, provider } = entry;
|
||||
if (
|
||||
!isAiModelAgentAuto(model) &&
|
||||
isWorkflowImageGenerationModel({
|
||||
providerType: provider.type,
|
||||
modelId: model.modelId,
|
||||
capabilities: model.capabilities,
|
||||
supportsImageGen: model.supportsImageGen,
|
||||
})
|
||||
) {
|
||||
workflowImageModelById.set(model.id, entry);
|
||||
}
|
||||
});
|
||||
const visibleModelsWithProvider = sortModelsForDisplay(
|
||||
modelsWithProvider
|
||||
.filter(
|
||||
({ model }) =>
|
||||
!isAiModelAgentAuto(model) &&
|
||||
(allowHiddenModels || !model.isHidden)
|
||||
)
|
||||
.map((item) => item.model)
|
||||
);
|
||||
const visibleModelIdSet = new Set(visibleModelsWithProvider.map((m) => m.id));
|
||||
const visibleModelsWithProviderSorted = modelsWithProvider.filter(({ model }) =>
|
||||
visibleModelIdSet.has(model.id)
|
||||
).sort((a, b) => {
|
||||
const left = visibleModelsWithProvider.findIndex((m) => m.id === a.model.id);
|
||||
const right = visibleModelsWithProvider.findIndex((m) => m.id === b.model.id);
|
||||
return left - right;
|
||||
});
|
||||
|
||||
// 转换为前端需要的格式
|
||||
const models = visibleModelsWithProviderSorted.map(serializeModel);
|
||||
const preferredNanoBananaModel = models.find(
|
||||
(model) =>
|
||||
isImageGenerationModelDescriptor({
|
||||
@ -147,29 +195,43 @@ export async function GET() {
|
||||
const configuredAgentWorkflowModelId = String(
|
||||
siteSettings?.agentWorkflowDefaultModelId || ""
|
||||
).trim();
|
||||
const agentWorkflowDefaultModel = configuredAgentWorkflowModelId
|
||||
? models.find(
|
||||
(model) =>
|
||||
model.id === configuredAgentWorkflowModelId &&
|
||||
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 hasConfiguredAgentWorkflowModel = Boolean(
|
||||
configuredAgentWorkflowModelId &&
|
||||
configuredAgentWorkflowModelId !== "auto" &&
|
||||
configuredAgentWorkflowModelId !== "__auto"
|
||||
);
|
||||
const agentWorkflowDefaultModel = hasConfiguredAgentWorkflowModel
|
||||
? workflowImageModelById.get(configuredAgentWorkflowModelId) || null
|
||||
: null;
|
||||
const workflowUiConfig = normalizeAgentWorkflowUiConfig(
|
||||
agentWorkflowUiConfigSetting?.value
|
||||
);
|
||||
const workflowBusinessModeSettings = getAgentWorkflowBusinessModeSettings(
|
||||
workflowUiConfig
|
||||
);
|
||||
const configuredWorkflowModelIds = new Set<string>();
|
||||
if (agentWorkflowDefaultModel) {
|
||||
configuredWorkflowModelIds.add(agentWorkflowDefaultModel.model.id);
|
||||
}
|
||||
workflowBusinessModeSettings.modes.forEach((mode) => {
|
||||
const modelId = String(mode.defaultModelId || "").trim();
|
||||
if (modelId && workflowImageModelById.has(modelId)) {
|
||||
configuredWorkflowModelIds.add(modelId);
|
||||
}
|
||||
});
|
||||
const agentWorkflowConfiguredModels = Array.from(configuredWorkflowModelIds)
|
||||
.map((id) => workflowImageModelById.get(id))
|
||||
.filter((entry): entry is ModelWithProvider => Boolean(entry))
|
||||
.map(serializeModel);
|
||||
|
||||
return Response.json({
|
||||
models,
|
||||
defaultModelId: resolvedDefaultModelId,
|
||||
agentWorkflowDefaultModelId: agentWorkflowDefaultModel?.id || null,
|
||||
agentWorkflowConfiguredDefaultModelId: hasConfiguredAgentWorkflowModel
|
||||
? configuredAgentWorkflowModelId
|
||||
: null,
|
||||
agentWorkflowDefaultModelId: agentWorkflowDefaultModel?.model.id || null,
|
||||
agentWorkflowConfiguredModels,
|
||||
agentModel: agentModelWithProvider
|
||||
? {
|
||||
id: agentModelWithProvider.model.id,
|
||||
|
||||
@ -157,6 +157,15 @@ interface AgentGenerationWorkspaceDarkV2Props {
|
||||
workflowStylePanel?: ReactNode;
|
||||
}
|
||||
|
||||
interface AgentWorkflowModelOption {
|
||||
id: string;
|
||||
displayName: string;
|
||||
providerType: string;
|
||||
supportsImageGen?: boolean;
|
||||
isMultimodal?: boolean;
|
||||
imageGenConfig?: any;
|
||||
}
|
||||
|
||||
interface PickedAttachment extends Attachment {}
|
||||
|
||||
const MAX_CHAT_TEXT_PART_LENGTH = 2000;
|
||||
@ -1210,16 +1219,11 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
|
||||
const { data: modelsData } = useSWR<{
|
||||
models: Array<{
|
||||
id: string;
|
||||
displayName: string;
|
||||
providerType: string;
|
||||
supportsImageGen?: boolean;
|
||||
isMultimodal?: boolean;
|
||||
imageGenConfig?: any;
|
||||
}>;
|
||||
models: AgentWorkflowModelOption[];
|
||||
defaultModelId?: string | null;
|
||||
agentWorkflowConfiguredDefaultModelId?: string | null;
|
||||
agentWorkflowDefaultModelId?: string | null;
|
||||
agentWorkflowConfiguredModels?: AgentWorkflowModelOption[];
|
||||
agentModel?: any;
|
||||
}>("/api/models", fetcher);
|
||||
|
||||
@ -1258,10 +1262,15 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const activeBusinessModeDefaultModelId = !requiresPresetSelection
|
||||
? String(activeBusinessMode?.defaultModelId || "").trim() || null
|
||||
: null;
|
||||
const effectiveDefaultModelId =
|
||||
const lockedWorkflowModelId =
|
||||
activeBusinessModeDefaultModelId ||
|
||||
selectedAgentPreset?.defaultModelId ||
|
||||
modelsData?.agentWorkflowConfiguredDefaultModelId ||
|
||||
modelsData?.agentWorkflowDefaultModelId ||
|
||||
null;
|
||||
const isWorkflowModelLocked = Boolean(lockedWorkflowModelId);
|
||||
const effectiveDefaultModelId =
|
||||
lockedWorkflowModelId ||
|
||||
modelsData?.defaultModelId ||
|
||||
null;
|
||||
const presetDrivenResultModeType: AgentGenerationResultModeType =
|
||||
@ -1353,7 +1362,8 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
const workflowBrandName = getCurrentWorkflowBrandName();
|
||||
const shouldAllowModelOverride =
|
||||
isDirectModelGenerationMode || !isSceneGenerationPreset || presetModeConfig.scene.allowModelOverride;
|
||||
!isWorkflowModelLocked &&
|
||||
(isDirectModelGenerationMode || !isSceneGenerationPreset || presetModeConfig.scene.allowModelOverride);
|
||||
const brandModelAttachmentKeys = useMemo(() => {
|
||||
const keys = new Set<string>();
|
||||
referenceImages
|
||||
@ -1737,6 +1747,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
selectedAgentPresetId: string | null;
|
||||
selectedAgentPresetType: AgentPreset["type"] | null;
|
||||
selectedModelId: string;
|
||||
lockedWorkflowModelId: string | null;
|
||||
imageParams: ImageGenerationParams;
|
||||
isDetailReplacementPreset: boolean;
|
||||
isSkuReplacementPreset: boolean;
|
||||
@ -1754,6 +1765,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
selectedAgentPresetId: null,
|
||||
selectedAgentPresetType: null,
|
||||
selectedModelId: "auto",
|
||||
lockedWorkflowModelId: null,
|
||||
imageParams: {
|
||||
aspectRatio: "auto",
|
||||
imageSize: "2K",
|
||||
@ -1776,6 +1788,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
selectedAgentPresetId: selectedAgentPresetId || null,
|
||||
selectedAgentPresetType: selectedAgentPreset?.type || null,
|
||||
selectedModelId,
|
||||
lockedWorkflowModelId,
|
||||
imageParams,
|
||||
isDetailReplacementPreset,
|
||||
isSkuReplacementPreset,
|
||||
@ -1807,10 +1820,40 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
return [autoOption, ...imageModels];
|
||||
}, [modelsData?.agentModel?.imageGenConfig, modelsData?.models]);
|
||||
|
||||
const currentModel = useMemo(
|
||||
() => selectableModels.find((model) => model.id === selectedModelId) || selectableModels[0],
|
||||
[selectableModels, selectedModelId]
|
||||
const configuredWorkflowModels = useMemo(
|
||||
() => modelsData?.agentWorkflowConfiguredModels || [],
|
||||
[modelsData?.agentWorkflowConfiguredModels]
|
||||
);
|
||||
const executableModelOptions = useMemo(() => {
|
||||
const seen = new Set<string>();
|
||||
return [...selectableModels, ...configuredWorkflowModels].filter((model) => {
|
||||
if (seen.has(model.id)) return false;
|
||||
seen.add(model.id);
|
||||
return true;
|
||||
});
|
||||
}, [configuredWorkflowModels, selectableModels]);
|
||||
const executableModelIdSet = useMemo(
|
||||
() => new Set(executableModelOptions.map((model) => model.id)),
|
||||
[executableModelOptions]
|
||||
);
|
||||
const lockedWorkflowModel = useMemo(
|
||||
() =>
|
||||
lockedWorkflowModelId
|
||||
? executableModelOptions.find((model) => model.id === lockedWorkflowModelId) || null
|
||||
: null,
|
||||
[executableModelOptions, lockedWorkflowModelId]
|
||||
);
|
||||
const currentModel = useMemo(
|
||||
() =>
|
||||
lockedWorkflowModel ||
|
||||
executableModelOptions.find((model) => model.id === selectedModelId) ||
|
||||
selectableModels[0],
|
||||
[executableModelOptions, lockedWorkflowModel, selectableModels, selectedModelId]
|
||||
);
|
||||
const lockedWorkflowModelLabel =
|
||||
lockedWorkflowModel?.displayName ||
|
||||
selectedAgentPreset?.defaultModel?.displayName ||
|
||||
(lockedWorkflowModelId ? `配置模型不可用(${lockedWorkflowModelId})` : "未配置");
|
||||
const [activeToolStep, setActiveToolStep] = useState<ToolStepKey>("settings");
|
||||
const hasBrandModelStep = Boolean(brandModelPanel) && modelSlotEnabled;
|
||||
const hasBrandStoreStep = Boolean(brandStorePanel);
|
||||
@ -1862,9 +1905,10 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
? transportState.selectedAgentPresetId || selectedAgentPresetId || undefined
|
||||
: undefined;
|
||||
const resolvedSelectedModelId =
|
||||
transportState.selectedModelId !== "auto"
|
||||
transportState.lockedWorkflowModelId ||
|
||||
(transportState.selectedModelId !== "auto"
|
||||
? transportState.selectedModelId
|
||||
: effectiveDefaultModelId || transportState.selectedModelId;
|
||||
: effectiveDefaultModelId || transportState.selectedModelId);
|
||||
const replacementModePayload =
|
||||
transportState.isDetailReplacementPreset || transportState.isSkuReplacementPreset
|
||||
? {
|
||||
@ -1958,6 +2002,10 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}, [historyMessages, setMessages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isWorkflowModelLocked) {
|
||||
setSelectedModelId((current) => (current === "auto" ? current : "auto"));
|
||||
return;
|
||||
}
|
||||
if (normalizedIncomingWorkspaceState.selectedModelId) {
|
||||
const nextModelId = normalizedIncomingWorkspaceState.selectedModelId;
|
||||
setSelectedModelId((current) =>
|
||||
@ -1973,6 +2021,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}
|
||||
}, [
|
||||
effectiveDefaultModelId,
|
||||
isWorkflowModelLocked,
|
||||
normalizedIncomingWorkspaceState.selectedModelId,
|
||||
]);
|
||||
|
||||
@ -2625,7 +2674,9 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
return arePickedAttachmentsEqual(current, nextAttachments) ? current : nextAttachments;
|
||||
});
|
||||
setSelectedModelId((current) => {
|
||||
const nextModelId = normalizedIncomingWorkspaceState.selectedModelId || "auto";
|
||||
const nextModelId = isWorkflowModelLocked
|
||||
? "auto"
|
||||
: normalizedIncomingWorkspaceState.selectedModelId || "auto";
|
||||
return current === nextModelId ? current : nextModelId;
|
||||
});
|
||||
setImageParams((current) => {
|
||||
@ -2636,7 +2687,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
};
|
||||
return areImageGenerationParamsEqual(current, nextParams) ? current : nextParams;
|
||||
});
|
||||
}, [normalizedIncomingWorkspaceState]);
|
||||
}, [isWorkflowModelLocked, normalizedIncomingWorkspaceState]);
|
||||
|
||||
useEffect(() => {
|
||||
const nextWorkspaceState = {
|
||||
@ -2987,16 +3038,17 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
return;
|
||||
}
|
||||
const resolvedModelId =
|
||||
selectedModelId !== "auto"
|
||||
lockedWorkflowModelId ||
|
||||
(selectedModelId !== "auto"
|
||||
? selectedModelId
|
||||
: effectiveDefaultModelId || selectedModelId;
|
||||
: effectiveDefaultModelId || selectedModelId);
|
||||
if (!resolvedModelId) {
|
||||
toast.error("当前风格化未配置模型");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
resolvedModelId !== "auto" &&
|
||||
!selectableModels.some((model) => model.id === resolvedModelId)
|
||||
!executableModelIdSet.has(resolvedModelId)
|
||||
) {
|
||||
toast.error("当前风格化绑定的模型不存在或已停用,请重新编辑风格化模型配置");
|
||||
return;
|
||||
@ -3113,11 +3165,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
isSelectedBusinessModeUnavailable,
|
||||
isSkuReplacementPreset,
|
||||
effectiveDefaultModelId,
|
||||
executableModelIdSet,
|
||||
lockedWorkflowModelId,
|
||||
requiredProductUploadCount,
|
||||
requiresPresetSelection,
|
||||
selectedAgentPreset,
|
||||
selectedAgentPresetId,
|
||||
selectableModels,
|
||||
selectedModelId,
|
||||
sendMessage,
|
||||
setPendingGeneration,
|
||||
@ -3670,9 +3723,18 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
? "en"
|
||||
: "zh";
|
||||
const resolvedModelId =
|
||||
selectedModelId !== "auto"
|
||||
lockedWorkflowModelId ||
|
||||
(selectedModelId !== "auto"
|
||||
? selectedModelId
|
||||
: effectiveDefaultModelId || undefined;
|
||||
: effectiveDefaultModelId || undefined);
|
||||
if (
|
||||
resolvedModelId &&
|
||||
resolvedModelId !== "auto" &&
|
||||
!executableModelIdSet.has(resolvedModelId)
|
||||
) {
|
||||
toast.error("当前风格化绑定的模型不存在或已停用,请重新编辑风格化模型配置");
|
||||
return;
|
||||
}
|
||||
|
||||
detailGenerationCancelRef.current = false;
|
||||
setDetailGenerationRunResults([]);
|
||||
@ -4918,9 +4980,11 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
</Select>
|
||||
) : (
|
||||
<div className="rounded-[24px] border border-white/10 bg-black/30 px-4 py-3 text-sm text-white/70">
|
||||
当前生图模式已锁定模型:
|
||||
{isWorkflowModelLocked ? "后台已锁定生图模型:" : "当前生图模式使用默认模型:"}
|
||||
<span className="ml-2 font-medium text-white">
|
||||
{selectedAgentPreset?.defaultModel?.displayName || currentModel?.displayName || "未配置"}
|
||||
{isWorkflowModelLocked
|
||||
? lockedWorkflowModelLabel
|
||||
: currentModel?.displayName || "自动选择"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user