ai-chatbot/lib/style-pro/model-options.ts

127 lines
3.8 KiB
TypeScript

import "server-only";
import type { DiscountableImageGenConfigLike } from "@/lib/ai/image-model-pricing";
import {
getImageCreditCostFromConfig,
getImageModelCreditDiscountLabel,
} from "@/lib/ai/image-model-pricing";
import {
type ModelFrontendAvailabilityStatus,
} from "@/lib/ai/model-frontend-availability";
import { isStillImageGenerationModelDescriptor } from "@/lib/ai/model-classification";
import {
getEnabledAiModelsWithProvider,
getFrontendDisplayAiModelsWithProvider,
} from "@/lib/db/queries";
import {
getStyleProModelAvailability,
isStyleProModelRunnable,
} from "./model-availability";
export type StyleProModelEntry = Awaited<
ReturnType<typeof getEnabledAiModelsWithProvider>
>[number];
export type StyleProDisplayModelEntry = Awaited<
ReturnType<typeof getFrontendDisplayAiModelsWithProvider>
>[number];
export type PublicStyleProModelOption = {
id: string;
modelId: string;
displayName: string;
avatarUrl: string | null;
providerName: string;
providerType: string;
isDefault: boolean;
unitCost: number;
creditDiscountLabel: string | null;
availabilityStatus: ModelFrontendAvailabilityStatus;
availabilityLabel: string | null;
selectable: boolean;
disabledReason: string | null;
};
export function isStyleProImageModel(
entry: StyleProModelEntry | StyleProDisplayModelEntry
) {
return isStillImageGenerationModelDescriptor({
providerType: entry.provider.type,
modelId: entry.model.modelId,
capabilities: entry.model.capabilities,
supportsImageGen: entry.model.supportsImageGen,
});
}
export async function getStyleProImageModelEntries(options?: {
includeHidden?: boolean;
}) {
const models = await getEnabledAiModelsWithProvider(
false,
options?.includeHidden === true
);
return models
.filter(isStyleProImageModel)
.filter(isStyleProModelRunnable);
}
export async function getStyleProImageModelDisplayEntries(options?: {
includeHidden?: boolean;
}) {
const models = await getFrontendDisplayAiModelsWithProvider(
false,
options?.includeHidden === true
);
return models.filter(isStyleProImageModel);
}
export function findStyleProImageModelByRef(
entries: Array<StyleProModelEntry | StyleProDisplayModelEntry>,
modelRef?: string | null
) {
const normalized = String(modelRef || "").trim();
if (!normalized) return null;
return (
entries.find(
({ model }) => model.id === normalized || model.modelId === normalized
) || null
);
}
export function sortStyleProImageModelEntries<
T extends StyleProModelEntry | StyleProDisplayModelEntry,
>(entries: T[]) {
return [...entries].sort((left, right) => {
const leftOrder = Number.isFinite(Number(left.model.sortOrder))
? Number(left.model.sortOrder)
: 0;
const rightOrder = Number.isFinite(Number(right.model.sortOrder))
? Number(right.model.sortOrder)
: 0;
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
const leftName = String(left.model.displayName || left.model.modelId || "");
const rightName = String(right.model.displayName || right.model.modelId || "");
return leftName.localeCompare(rightName, "zh-Hans-CN");
});
}
export function toPublicStyleProModelOption(
entry: StyleProModelEntry | StyleProDisplayModelEntry
): PublicStyleProModelOption {
const imageGenConfig = (entry.model.imageGenConfig ||
null) as DiscountableImageGenConfigLike | null;
const availability = getStyleProModelAvailability(entry);
return {
id: entry.model.id,
modelId: entry.model.modelId,
displayName: entry.model.displayName,
avatarUrl: entry.model.avatarUrl || null,
providerName: entry.provider.name,
providerType: entry.provider.type,
isDefault: entry.model.isDefault,
unitCost: getImageCreditCostFromConfig(imageGenConfig),
creditDiscountLabel: getImageModelCreditDiscountLabel(imageGenConfig),
...availability,
};
}