fix(ruxa-image): normalize runtime model id and enforce nano-banana-2 endpoint
This commit is contained in:
parent
609700ab54
commit
cd817a6336
@ -2,6 +2,10 @@ import "server-only";
|
||||
|
||||
import sharp from "sharp";
|
||||
import { uploadFile } from "@/lib/storage/service";
|
||||
import {
|
||||
RUXA_NANO_BANANA_2_RUNTIME_MODEL_ID,
|
||||
resolveRuxaImageRuntimeModelId,
|
||||
} from "@/lib/ai/ruxa-image-models";
|
||||
|
||||
interface RuxaImageConfig {
|
||||
apiKey: string;
|
||||
@ -41,6 +45,11 @@ function normalizeRuxaModelId(modelId?: string) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
function resolveRuxaRuntimeModelId(modelId: string) {
|
||||
const normalized = modelId.trim();
|
||||
return resolveRuxaImageRuntimeModelId(normalized);
|
||||
}
|
||||
|
||||
function normalizeUrlForCompare(url: string) {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
@ -118,7 +127,7 @@ function resolveRuxaTaskInputMode({
|
||||
return "resolution-based";
|
||||
}
|
||||
|
||||
return activeModelId === "gemini-3.1-flash-image-preview"
|
||||
return activeModelId === RUXA_NANO_BANANA_2_RUNTIME_MODEL_ID
|
||||
? "size-image"
|
||||
: "resolution-based";
|
||||
}
|
||||
@ -469,7 +478,18 @@ export function createRuxaImage({ apiKey, baseURL }: RuxaImageConfig) {
|
||||
const aspectRatio = normalizeAspectRatio(options.imageParams?.aspectRatio);
|
||||
const resolution = normalizeResolution(options.imageParams?.imageSize);
|
||||
const requestedModelId = options.overrideModelId || modelId;
|
||||
const activeModelId = normalizeRuxaModelId(requestedModelId);
|
||||
const normalizedRequestedModelId = normalizeRuxaModelId(requestedModelId);
|
||||
const activeModelId = resolveRuxaRuntimeModelId(normalizedRequestedModelId);
|
||||
if (normalizedRequestedModelId !== activeModelId) {
|
||||
console.warn("[Ruxa Image] 运行时模型已映射", {
|
||||
requestedModelId: normalizedRequestedModelId,
|
||||
runtimeModelId: activeModelId,
|
||||
});
|
||||
console.warn("[Ruxa Image] 请确认数据库中已同步更新该模型的运行时 endpoint", {
|
||||
requestedModelId: normalizedRequestedModelId,
|
||||
runtimeModelId: activeModelId,
|
||||
});
|
||||
}
|
||||
const taskInputMode = resolveRuxaTaskInputMode({
|
||||
activeModelId,
|
||||
modelConfig: options.imageGenConfig,
|
||||
|
||||
@ -74,3 +74,14 @@ export function getRuxaImageModelPresetById(modelId: string) {
|
||||
(preset) => preset.modelId === String(modelId || "").trim()
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveRuxaImageRuntimeModelId(modelId: string) {
|
||||
const preset = getRuxaImageModelPresetById(modelId);
|
||||
const endpoint = preset?.defaultParams?.endpoint;
|
||||
|
||||
if (typeof endpoint === "string" && endpoint.trim()) {
|
||||
return endpoint.trim();
|
||||
}
|
||||
|
||||
return modelId;
|
||||
}
|
||||
|
||||
@ -3,17 +3,25 @@ import { and, eq, sql } from "drizzle-orm";
|
||||
|
||||
import { db } from "../lib/db";
|
||||
import { aiModel, aiProvider } from "../lib/db/schema";
|
||||
import { RUXA_NANO_BANANA_2_RUNTIME_MODEL_ID } from "../lib/ai/ruxa-image-models";
|
||||
|
||||
loadEnv({ path: ".env.local" });
|
||||
|
||||
type ModelExpectation = {
|
||||
modelId: string;
|
||||
displayName: string;
|
||||
expectedRuntimeModelId?: string;
|
||||
expectedTaskMode?: "size-image" | "resolution-based";
|
||||
};
|
||||
|
||||
const EXPECTED_MODELS: ModelExpectation[] = [
|
||||
{ modelId: "google/nano-banana-2", displayName: "nano-banana-2「专线」" },
|
||||
{ modelId: "google/nano-banana-pro", displayName: "nano-banana-pro「专线」" },
|
||||
{
|
||||
modelId: "google/nano-banana-2",
|
||||
displayName: "nano-banana-2「专线」",
|
||||
expectedRuntimeModelId: RUXA_NANO_BANANA_2_RUNTIME_MODEL_ID,
|
||||
expectedTaskMode: "size-image",
|
||||
},
|
||||
{ modelId: "google/nano-banana-pro", displayName: "nano-banana-pro「专线」", expectedTaskMode: "resolution-based" },
|
||||
];
|
||||
|
||||
type Row = {
|
||||
@ -28,6 +36,7 @@ type Row = {
|
||||
isHidden: boolean | null;
|
||||
healthStatus: string | null;
|
||||
imageGenConfig: unknown;
|
||||
defaultParams: unknown;
|
||||
};
|
||||
|
||||
function getConfigFlag(
|
||||
@ -61,6 +70,7 @@ async function main() {
|
||||
supportsVision: aiModel.supportsVision,
|
||||
isEnabled: aiModel.isEnabled,
|
||||
isHidden: aiModel.isHidden,
|
||||
defaultParams: aiModel.defaultParams,
|
||||
healthStatus: aiModel.healthStatus,
|
||||
imageGenConfig: aiModel.imageGenConfig,
|
||||
})
|
||||
@ -91,7 +101,7 @@ async function main() {
|
||||
|
||||
const failures: string[] = [];
|
||||
const expectByModelId = new Map(
|
||||
EXPECTED_MODELS.map((entry) => [entry.modelId, entry.displayName])
|
||||
EXPECTED_MODELS.map((entry) => [entry.modelId, entry])
|
||||
);
|
||||
const expectedSet = new Set(expectByModelId.keys());
|
||||
|
||||
@ -101,10 +111,15 @@ async function main() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const expectedDisplayName = expectByModelId.get(row.modelId);
|
||||
if (expectedDisplayName && row.displayName !== expectedDisplayName) {
|
||||
const expected = expectByModelId.get(row.modelId);
|
||||
if (!expected) {
|
||||
failures.push(`Unexpected model present in RUXA image provider: ${row.modelId}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row.displayName !== expected.displayName) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: displayName mismatch (expected "${expectedDisplayName}", got "${row.displayName}")`
|
||||
`${row.providerName} ${row.modelId}: displayName mismatch (expected "${expected.displayName}", got "${row.displayName}")`
|
||||
);
|
||||
}
|
||||
|
||||
@ -146,6 +161,22 @@ async function main() {
|
||||
`${row.providerName} ${row.modelId}: imageGenConfig.supports4K should be true`
|
||||
);
|
||||
}
|
||||
|
||||
const defaultParams = getImageGenConfig(row.defaultParams) ?? {};
|
||||
const runtimeModelFromDb = getRuntimeModelId(row, defaultParams);
|
||||
const expectedRuntimeModelId = expected.expectedRuntimeModelId;
|
||||
if (expectedRuntimeModelId && runtimeModelFromDb !== expectedRuntimeModelId) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: default runtime model should be ${expectedRuntimeModelId}, got ${runtimeModelFromDb || "null"}`
|
||||
);
|
||||
}
|
||||
|
||||
const runtimeMode = config?.ruxaTaskInputMode;
|
||||
if (runtimeMode !== expected.expectedTaskMode) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: imageGenConfig.ruxaTaskInputMode should be ${expected.expectedTaskMode}, got ${runtimeMode}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const provider of providers) {
|
||||
@ -204,6 +235,17 @@ async function main() {
|
||||
console.log("RUXA image model check passed.");
|
||||
}
|
||||
|
||||
function getRuntimeModelId(row: Row, defaultParams: Record<string, unknown>) {
|
||||
const endpoint = getConfigValue(defaultParams, "endpoint");
|
||||
if (endpoint) return endpoint;
|
||||
return row.modelId;
|
||||
}
|
||||
|
||||
function getConfigValue(config: Record<string, unknown>, key: string): string | null {
|
||||
const raw = config?.[key];
|
||||
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("RUXA image model verification failed to execute:", error);
|
||||
process.exit(1);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user