fix(ruxa): resolve runtime model ids from config for image flows
This commit is contained in:
parent
5a04304ea9
commit
6a9beb418a
@ -7,6 +7,7 @@ import { createHuankeGemini } from "@/lib/ai/providers/huanke-gemini";
|
||||
import { createAlibabaImage } from "@/lib/ai/providers/alibaba-image";
|
||||
import { createBflImage } from "@/lib/ai/providers/bfl-image";
|
||||
import { createRuxaImage } from "@/lib/ai/providers/ruxa-image";
|
||||
import { resolveModelRuntimeId } from "@/lib/ai/model-runtime";
|
||||
import { createFalImage } from "@/lib/ai/providers/fal-image";
|
||||
import { createOpenAIImage } from "@/lib/ai/providers/openai-image";
|
||||
import { createStepfunImage } from "@/lib/ai/providers/stepfun-image";
|
||||
@ -426,7 +427,10 @@ async function generateTextEditImageWithFailover(params: {
|
||||
});
|
||||
|
||||
const provider = createTextEditProvider(selected.provider);
|
||||
const result = await provider.chat(selected.model.modelId).doGenerate({
|
||||
const runtimeModelId = resolveModelRuntimeId(selected.model, {
|
||||
providerType: provider.type,
|
||||
});
|
||||
const result = await provider.chat(runtimeModelId).doGenerate({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
|
||||
@ -33,6 +33,7 @@ import {
|
||||
} from "@/lib/scene-fusion/output-capabilities";
|
||||
import { acquireCanvasImageModelSlot } from "@/lib/canvas/image-concurrency";
|
||||
import { createLogger } from "@/lib/observability/logger";
|
||||
import { resolveModelRuntimeId } from "@/lib/ai/model-runtime";
|
||||
|
||||
// 请求体 Schema
|
||||
const sceneFusionRequestSchema = z.object({
|
||||
@ -81,6 +82,7 @@ interface SceneFusionModelCandidate {
|
||||
supportsVision: boolean;
|
||||
imageGenConfig?: SceneFusionImageGenConfig | null;
|
||||
excludeFromFailover?: boolean;
|
||||
defaultParams?: Record<string, any> | null;
|
||||
};
|
||||
provider: {
|
||||
id?: string;
|
||||
@ -901,6 +903,7 @@ function buildSceneFusionCandidatePool(
|
||||
supportsVision: Boolean(entry.model.supportsVision),
|
||||
imageGenConfig: (entry.model.imageGenConfig || null) as SceneFusionImageGenConfig | null,
|
||||
excludeFromFailover: Boolean(entry.model.excludeFromFailover),
|
||||
defaultParams: (entry.model.defaultParams as Record<string, any> | null) || null,
|
||||
},
|
||||
provider: {
|
||||
id: String(entry.provider.id || ""),
|
||||
@ -1475,6 +1478,7 @@ async function processSceneWithProvider(
|
||||
sceneMode: SceneFusionMode,
|
||||
provider: { type: string; baseUrl: string; apiKey: string },
|
||||
modelId: string,
|
||||
runtimeModelId: string,
|
||||
aspectRatio?: string,
|
||||
imageSize?: string
|
||||
): Promise<{ thumbnail: string; original: string; prompt: string }> {
|
||||
@ -1501,7 +1505,15 @@ async function processSceneWithProvider(
|
||||
return processWithAlibabaImage(productImageUrl, sceneImageUrl, prompt, provider, modelId, aspectRatio, imageSize);
|
||||
|
||||
case "ruxa-image":
|
||||
return processWithRuxaImage(productImageUrl, sceneImageUrl, prompt, provider, modelId, aspectRatio, imageSize);
|
||||
return processWithRuxaImage(
|
||||
productImageUrl,
|
||||
sceneImageUrl,
|
||||
prompt,
|
||||
provider,
|
||||
runtimeModelId || modelId,
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
);
|
||||
|
||||
case "fal-image":
|
||||
return processWithFalImage(productImageUrl, sceneImageUrl, prompt, provider, modelId, aspectRatio, imageSize);
|
||||
@ -1957,6 +1969,9 @@ async function processSceneWithDiversity(
|
||||
});
|
||||
let result: Awaited<ReturnType<typeof processSceneWithProvider>>;
|
||||
try {
|
||||
const runtimeModelId = resolveModelRuntimeId(candidate.model, {
|
||||
providerType: candidate.provider.type,
|
||||
});
|
||||
await params.onProcessingStart?.();
|
||||
result = await processSceneWithProvider(
|
||||
params.productImageUrl,
|
||||
@ -1974,6 +1989,7 @@ async function processSceneWithDiversity(
|
||||
apiKey: candidate.provider.apiKey,
|
||||
},
|
||||
candidate.model.modelId,
|
||||
runtimeModelId,
|
||||
normalizedOutputParams.aspectRatio,
|
||||
normalizedOutputParams.imageSize
|
||||
);
|
||||
|
||||
@ -37,6 +37,7 @@ import {
|
||||
isMoonshotOfficialBaseUrl,
|
||||
} from "./detail-agent-kimi-profile";
|
||||
import { isBltcyVideoBaseUrl } from "./bltcy-video-models";
|
||||
import { resolveModelRuntimeId } from "./model-runtime";
|
||||
|
||||
// 缓存已创建的 provider 实例
|
||||
const providerCache = new Map<string, any>();
|
||||
@ -387,7 +388,10 @@ export async function getDynamicModel(
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolvedModelId = String(overrideModelId || "").trim() || model.modelId;
|
||||
const resolvedModelId = resolveModelRuntimeId(model, {
|
||||
overrideModelId,
|
||||
providerType: provider.type,
|
||||
});
|
||||
|
||||
// 创建基础模型
|
||||
logger.debug("model creation started", {
|
||||
|
||||
54
lib/ai/model-runtime.ts
Normal file
54
lib/ai/model-runtime.ts
Normal file
@ -0,0 +1,54 @@
|
||||
interface ModelRuntimeLike {
|
||||
modelId?: string | null;
|
||||
defaultParams?: unknown;
|
||||
}
|
||||
|
||||
interface ResolveModelRuntimeIdOptions {
|
||||
overrideModelId?: string | null;
|
||||
providerType?: string | null;
|
||||
}
|
||||
|
||||
function fallbackRuxaRuntimeId(modelId: string) {
|
||||
if (!modelId.startsWith("google/")) {
|
||||
return modelId;
|
||||
}
|
||||
|
||||
const candidate = modelId.slice("google/".length).trim();
|
||||
return candidate || modelId;
|
||||
}
|
||||
|
||||
function toRecord(value: unknown): Record<string, unknown> | null {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
return null;
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function normalizeRuntimeModelId(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const normalized = value.trim();
|
||||
return normalized || null;
|
||||
}
|
||||
|
||||
export function resolveModelRuntimeId(
|
||||
model: ModelRuntimeLike | null | undefined,
|
||||
options?: ResolveModelRuntimeIdOptions,
|
||||
): string {
|
||||
const overrideModelId = normalizeRuntimeModelId(options?.overrideModelId);
|
||||
if (overrideModelId) {
|
||||
return overrideModelId;
|
||||
}
|
||||
|
||||
const defaultParams = toRecord(model?.defaultParams);
|
||||
const endpointModelId = normalizeRuntimeModelId(defaultParams?.endpoint);
|
||||
if (endpointModelId) {
|
||||
return endpointModelId;
|
||||
}
|
||||
|
||||
const resolvedModelId = normalizeRuntimeModelId(model?.modelId) || "";
|
||||
if (options?.providerType === "ruxa-image") {
|
||||
return fallbackRuxaRuntimeId(resolvedModelId);
|
||||
}
|
||||
|
||||
return resolvedModelId;
|
||||
}
|
||||
@ -38,7 +38,7 @@ export const RUXA_IMAGE_MODEL_PRESETS: RuxaImageModelPreset[] = [
|
||||
supportsImageGen: true,
|
||||
defaultParams: {
|
||||
provider: "ruxa",
|
||||
endpoint: RUXA_NANO_BANANA_2_MODEL_ID,
|
||||
endpoint: "nano-banana-2",
|
||||
imageSize: "2K",
|
||||
aspectRatio: "1:1",
|
||||
},
|
||||
@ -55,7 +55,7 @@ export const RUXA_IMAGE_MODEL_PRESETS: RuxaImageModelPreset[] = [
|
||||
supportsImageGen: true,
|
||||
defaultParams: {
|
||||
provider: "ruxa",
|
||||
endpoint: RUXA_NANO_BANANA_PRO_MODEL_ID,
|
||||
endpoint: "nano-banana-pro",
|
||||
imageSize: "2K",
|
||||
aspectRatio: "1:1",
|
||||
},
|
||||
@ -71,4 +71,3 @@ export function getRuxaImageModelPresetById(modelId: string) {
|
||||
(preset) => preset.modelId === String(modelId || "").trim()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import { createMidjourney } from "@/lib/ai/providers/midjourney";
|
||||
import { createRunway } from "@/lib/ai/providers/runway";
|
||||
import { createKrea } from "@/lib/ai/providers/krea";
|
||||
import { createVolcanoVision } from "@/lib/ai/providers/volcano-vision";
|
||||
import { resolveModelRuntimeId } from "@/lib/ai/model-runtime";
|
||||
import { filterReachableRemoteUrls } from "@/lib/net/url-access";
|
||||
import {
|
||||
acquireCanvasImageModelSlot,
|
||||
@ -579,7 +580,10 @@ export async function generateCanvasImage(
|
||||
apiKey: provider.apiKey,
|
||||
baseURL: provider.baseUrl,
|
||||
});
|
||||
const modelInstance = ruxaProvider.chat(selectedModel.model.modelId);
|
||||
const runtimeModelId = resolveModelRuntimeId(selectedModel.model, {
|
||||
providerType: provider.type,
|
||||
});
|
||||
const modelInstance = ruxaProvider.chat(runtimeModelId);
|
||||
executeGenerateBatch = async (batchImageCount) => {
|
||||
const result = await modelInstance.doGenerate({
|
||||
messages,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user