| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { parseImageModel } from "@omniroute/open-sse/config/imageRegistry.ts";
|
| import { resolveComboTargets } from "@omniroute/open-sse/services/combo.ts";
|
|
|
| import { getComboByName, getCombos } from "@/lib/db/combos";
|
| import { getProviderNodes } from "@/lib/db/providers";
|
|
|
| |
| |
| |
| |
| |
|
|
| export async function resolveImageModelPrefix(modelStr: string): Promise<string> {
|
| if (typeof modelStr !== "string") return modelStr;
|
| const slash = modelStr.indexOf("/");
|
| if (slash <= 0) return modelStr;
|
|
|
| const prefixPart = modelStr.slice(0, slash);
|
| const rest = modelStr.slice(slash + 1);
|
| if (!rest) return modelStr;
|
|
|
| try {
|
| const nodes = await getProviderNodes({ type: "openai-compatible" });
|
|
|
|
|
| const matched = nodes.find((node: { prefix?: unknown }) => node.prefix === prefixPart);
|
| if (matched && typeof matched.id === "string" && matched.id && matched.id !== prefixPart) {
|
| return `${matched.id}/${rest}`;
|
| }
|
| } catch {
|
|
|
| }
|
| return modelStr;
|
| }
|
|
|
| |
| |
| |
|
|
| export async function resolveSingleImageComboTarget(name: string): Promise<string | null> {
|
| if (typeof name !== "string" || !name.trim()) return null;
|
| try {
|
| const combo = await getComboByName(name);
|
| if (!combo) return null;
|
| const allCombos = await getCombos();
|
| const targets = resolveComboTargets(combo as never, allCombos as never);
|
| const first = targets.find(
|
| (t: { modelStr?: unknown }) => typeof t?.modelStr === "string" && (t.modelStr as string).trim()
|
| );
|
| return (first?.modelStr as string) ?? null;
|
| } catch {
|
| return null;
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function resolveImageRouteModel(modelStr: string): Promise<string> {
|
| if (typeof modelStr !== "string" || !modelStr.trim()) return modelStr;
|
|
|
|
|
| if (parseImageModel(modelStr).provider) return modelStr;
|
|
|
|
|
|
|
| if (!modelStr.includes("/")) {
|
| const target = await resolveSingleImageComboTarget(modelStr);
|
| if (target && target !== modelStr) return resolveImageModelPrefix(target);
|
| return modelStr;
|
| }
|
|
|
|
|
| return resolveImageModelPrefix(modelStr);
|
| }
|
|
|
| interface ParsedImageEditInput {
|
| prompt: string;
|
| model: string | null;
|
| size: string | null;
|
| responseFormat: string | null;
|
| imageBytes: Buffer | null;
|
| imageMime: string | null;
|
| }
|
|
|
|
|
| export function parseDataUrl(value: unknown): { bytes: Buffer; mime: string } | null {
|
| if (typeof value !== "string") return null;
|
| const match = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(value.trim());
|
| if (!match) return null;
|
| const mime = match[1] || "image/png";
|
| const isBase64 = Boolean(match[2]);
|
| const payload = match[3] ?? "";
|
| try {
|
| const bytes = isBase64
|
| ? Buffer.from(payload, "base64")
|
| : Buffer.from(decodeURIComponent(payload), "utf8");
|
| if (bytes.length === 0) return null;
|
| return { bytes, mime };
|
| } catch {
|
| return null;
|
| }
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
| export function extractImageEditInputFromJson(body: unknown): ParsedImageEditInput {
|
| const obj = (body && typeof body === "object" ? body : {}) as Record<string, unknown>;
|
| const str = (v: unknown): string | null =>
|
| typeof v === "string" && v.trim() ? v.trim() : null;
|
|
|
| const prompt = typeof obj.prompt === "string" ? obj.prompt.trim() : "";
|
| const model = str(obj.model);
|
| const size = str(obj.size);
|
| const responseFormat = str(obj.response_format);
|
|
|
| const candidates: unknown[] = [];
|
| if (obj.image !== undefined) candidates.push(obj.image);
|
| const images = obj.images;
|
| if (Array.isArray(images)) {
|
| for (const entry of images) {
|
| if (typeof entry === "string") candidates.push(entry);
|
| else if (entry && typeof entry === "object") {
|
| const e = entry as Record<string, unknown>;
|
| candidates.push(e.image_url ?? e.url ?? e.b64_json);
|
| }
|
| }
|
| }
|
|
|
| let imageBytes: Buffer | null = null;
|
| let imageMime: string | null = null;
|
| for (const candidate of candidates) {
|
| const parsed = parseDataUrl(candidate);
|
| if (parsed) {
|
| imageBytes = parsed.bytes;
|
| imageMime = parsed.mime;
|
| break;
|
| }
|
| }
|
|
|
| return { prompt, model, size, responseFormat, imageBytes, imageMime };
|
| }
|
|
|