mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
@@ -0,0 +1,100 @@
|
||||
import type { ChatCompletionMessage } from "@/services/api/image";
|
||||
import type { ReferenceImage } from "@/types/image";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData } from "../types";
|
||||
|
||||
export type NodeGenerationContext = {
|
||||
prompt: string;
|
||||
referenceImages: ReferenceImage[];
|
||||
textCount: number;
|
||||
imageCount: number;
|
||||
};
|
||||
|
||||
export type NodeGenerationInput = {
|
||||
nodeId: string;
|
||||
type: "text" | "image";
|
||||
title: string;
|
||||
text?: string;
|
||||
image?: ReferenceImage;
|
||||
};
|
||||
|
||||
export function buildNodeGenerationContext(
|
||||
nodeId: string,
|
||||
nodes: CanvasNodeData[],
|
||||
connections: CanvasConnection[],
|
||||
prompt: string,
|
||||
): NodeGenerationContext {
|
||||
const inputs = buildNodeGenerationInputs(nodeId, nodes, connections);
|
||||
const upstreamText = inputs.map((input) => input.text).filter(Boolean).join("\n\n");
|
||||
const referenceImages = inputs.map((input) => input.image).filter((image): image is ReferenceImage => Boolean(image));
|
||||
|
||||
return {
|
||||
prompt: upstreamText ? `${prompt}\n\n${upstreamText}` : prompt,
|
||||
referenceImages,
|
||||
textCount: inputs.filter((input) => input.type === "text").length,
|
||||
imageCount: referenceImages.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildNodeGenerationInputs(
|
||||
nodeId: string,
|
||||
nodes: CanvasNodeData[],
|
||||
connections: CanvasConnection[],
|
||||
): NodeGenerationInput[] {
|
||||
return getOrderedUpstreamNodes(nodeId, nodes, connections).flatMap((node): NodeGenerationInput[] => {
|
||||
const image = readReferenceImage(node);
|
||||
if (image) return [{ nodeId: node.id, type: "image" as const, title: node.title, image }];
|
||||
const text = readNodeTextInput(node);
|
||||
if (text) return [{ nodeId: node.id, type: "text" as const, title: node.title, text }];
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
export function buildNodeChatMessages(context: NodeGenerationContext): ChatCompletionMessage[] {
|
||||
if (!context.referenceImages.length) {
|
||||
return [{ role: "user", content: context.prompt }];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text" as const, text: context.prompt },
|
||||
...context.referenceImages.map((image) => ({ type: "image_url" as const, image_url: { url: image.dataUrl } })),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export async function hydrateNodeGenerationContext(context: NodeGenerationContext) {
|
||||
const { imageToDataUrl } = await import("@/services/image-storage");
|
||||
return { ...context, referenceImages: await Promise.all(context.referenceImages.map(async (image) => ({ ...image, dataUrl: await imageToDataUrl(image) }))) };
|
||||
}
|
||||
|
||||
function readNodeTextInput(node: CanvasNodeData) {
|
||||
if (node.type === CanvasNodeType.Text) return node.metadata?.content || node.metadata?.prompt || "";
|
||||
return node.metadata?.prompt || "";
|
||||
}
|
||||
|
||||
function readReferenceImage(node: CanvasNodeData): ReferenceImage | null {
|
||||
if (node.type !== CanvasNodeType.Image || !node.metadata?.content) return null;
|
||||
return {
|
||||
id: node.id,
|
||||
name: `${node.title || node.id}.png`,
|
||||
type: node.metadata.mimeType || "image/png",
|
||||
dataUrl: node.metadata.content,
|
||||
storageKey: node.metadata.storageKey,
|
||||
};
|
||||
}
|
||||
|
||||
function getOrderedUpstreamNodes(nodeId: string, nodes: CanvasNodeData[], connections: CanvasConnection[]) {
|
||||
const target = nodes.find((node) => node.id === nodeId);
|
||||
const upstreamNodes = connections
|
||||
.filter((connection) => connection.toNodeId === nodeId)
|
||||
.map((connection) => nodes.find((node) => node.id === connection.fromNodeId))
|
||||
.filter((node): node is CanvasNodeData => Boolean(node));
|
||||
const order = target?.metadata?.inputOrder || [];
|
||||
return [
|
||||
...order.map((id) => upstreamNodes.find((node) => node.id === id)).filter((node): node is CanvasNodeData => Boolean(node)),
|
||||
...upstreamNodes.filter((node) => !order.includes(node.id)),
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user