@llumiverse/drivers
Version:
LLM driver implementations. Currently supported are: openai, huggingface, bedrock, replicate.
308 lines (264 loc) • 10.9 kB
text/typescript
import { ExecutionOptions, NovaCanvasOptions } from "@llumiverse/core";
import { NovaMessage, NovaMessagesPrompt } from "@llumiverse/core/formatters";
function getFirstImageFromPrompt(prompt: NovaMessage[]) {
const msgImage = prompt.find(m => m.content.find(c => c.image));
if (!msgImage) {
return undefined;
}
return msgImage.content.find(c => c.image)?.image;
}
//@ts-ignore
function getAllImagesFromPrompt(prompt: NovaMessage[]) {
const contentMsg = prompt.filter(m => m.content).map(m => m.content).flat();
const imgParts = contentMsg.filter(c => c.image);
if (!imgParts?.length) {
return undefined;
}
const images = imgParts.map(i => i.image).filter(i => i?.source?.bytes).map(i => i!.source.bytes);
return images;
}
async function textToImagePayload(prompt: NovaMessagesPrompt, options: ExecutionOptions): Promise<NovaTextToImagePayload> {
const modelOptions = options.model_options as NovaCanvasOptions;
const textMessages = prompt.messages.map(m => m.content.map(c => c.text)).flat();
let text = textMessages.join("\n\n");
text += prompt.system ? "\n\n\nIMPORTANT: " + prompt.system?.map(m => m.text).join("\n\n") : '';
const conditionImage = (conditionImage: boolean) => {
const img = getFirstImageFromPrompt(prompt.messages);
if (img && conditionImage) {
return img
}
return undefined;
}
const payload: NovaTextToImagePayload = {
taskType: NovaImageGenerationTaskType.TEXT_IMAGE, // Always TEXT_IMAGE, as TEXT_IMAGE_WITH_IMAGE_CONDITIONING is only an internal marker.
imageGenerationConfig: {
quality: modelOptions?.quality,
width: modelOptions.width,
height: modelOptions.height,
numberOfImages: modelOptions.numberOfImages,
seed: modelOptions.seed,
cfgScale: modelOptions.cfgScale,
},
textToImageParams: {
text: text,
conditionImage: conditionImage(modelOptions?.controlMode ? true : false)?.source.bytes,
controlMode: modelOptions?.controlMode,
controlStrength: modelOptions?.controlStrength,
negativeText: prompt.negative
}
}
return payload;
}
async function imageVariationPayload(prompt: NovaMessagesPrompt, options: ExecutionOptions): Promise<NovaImageVariationPayload> {
const modelOptions = options.model_options as NovaCanvasOptions;
const text = prompt.messages.map(m => m.content).join("\n\n");
const images = getAllImagesFromPrompt(prompt.messages);
const payload: NovaImageVariationPayload = {
taskType: NovaImageGenerationTaskType.IMAGE_VARIATION,
imageGenerationConfig: {
quality: modelOptions?.quality,
width: modelOptions.width,
height: modelOptions.height,
numberOfImages: modelOptions.numberOfImages,
seed: modelOptions.seed,
cfgScale: modelOptions.cfgScale,
},
imageVariationParams: {
images: images ?? [],
text: text,
negativeText: prompt.negative,
similarityStrength: modelOptions?.similarityStrength,
}
}
return payload;
}
async function colorGuidedGenerationPayload(prompt: NovaMessagesPrompt, options: ExecutionOptions): Promise<NovaColorGuidedGenerationPayload> {
const modelOptions = options.model_options as NovaCanvasOptions;
const textMessages = prompt.messages.map(m => m.content.map(c => c.text)).flat();
let text = textMessages.join("\n\n");
text += prompt.system ? "\n\n\nIMPORTANT: " + prompt.system?.map(m => m.text).join("\n\n") : '';
const conditionImage = (conditionImage: boolean) => {
const img = getFirstImageFromPrompt(prompt.messages);
if (img && conditionImage) {
return img
}
return undefined;
}
const payload: NovaColorGuidedGenerationPayload = {
taskType: NovaImageGenerationTaskType.COLOR_GUIDED_GENERATION,
imageGenerationConfig: {
quality: modelOptions?.quality,
width: modelOptions.width,
height: modelOptions.height,
numberOfImages: modelOptions.numberOfImages,
seed: modelOptions.seed,
cfgScale: modelOptions.cfgScale,
},
colorGuidedGenerationParams: {
colors: modelOptions.colors ?? [],
text: text,
referenceImage: conditionImage(modelOptions?.controlMode ? true : false)?.source.bytes,
negativeText: prompt.negative
}
}
return payload;
}
async function backgroundRemovalPayload(prompt: NovaMessagesPrompt): Promise<NovaBackgroundRemovalPayload> {
const image = getFirstImageFromPrompt(prompt.messages);
if (!image?.source.bytes) {
throw new Error("No image found in prompt");
}
const payload: NovaBackgroundRemovalPayload = {
taskType: NovaImageGenerationTaskType.BACKGROUND_REMOVAL,
backgroundRemovalParams: {
image: image.source.bytes
}
}
console.log(payload)
return payload;
}
async function inpaintingPayload(prompt: NovaMessagesPrompt, options: ExecutionOptions): Promise<NovaInpaintingPayload> {
const modelOptions = options.model_options as NovaCanvasOptions;
const images = getAllImagesFromPrompt(prompt.messages);
if (!images?.length || images.length < 2) {
throw new Error("2 images are required for inpainting");
}
const sourceImage = images[0];
const maskImage = images[1];
const payload: NovaInpaintingPayload = {
taskType: NovaImageGenerationTaskType.INPAINTING,
imageGenerationConfig: {
quality: modelOptions?.quality,
width: modelOptions?.width,
height: modelOptions?.height,
numberOfImages: modelOptions?.numberOfImages,
seed: modelOptions?.seed,
cfgScale: modelOptions?.cfgScale,
},
inPaintingParams: {
image: sourceImage,
maskImage: maskImage,
text: prompt.messages.map(m => m.content.map(c => c.text)).flat().join("\n\n"),
negativeText: prompt.negative
}
}
return payload;
}
async function outpaintingPayload(prompt: NovaMessagesPrompt, options: ExecutionOptions): Promise<NovaOutpaintingPayload> {
const modelOptions = options.model_options as NovaCanvasOptions;
const images = getAllImagesFromPrompt(prompt.messages);
if (!images?.length || images.length < 2) {
throw new Error("2 images are required for outpainting");
}
const sourceImage = images[0];
const maskImage = images[1];
const payload: NovaOutpaintingPayload = {
taskType: NovaImageGenerationTaskType.OUTPAINTING,
imageGenerationConfig: {
quality: modelOptions?.quality,
width: modelOptions?.width,
height: modelOptions?.height,
numberOfImages: modelOptions?.numberOfImages,
seed: modelOptions?.seed,
cfgScale: modelOptions?.cfgScale,
},
outPaintingParams: {
image: sourceImage,
maskImage: maskImage,
text: prompt.messages.map(m => m.content.map(c => c.text)).flat().join("\n\n"),
negativeText: prompt.negative,
outPaintingMode: modelOptions?.outPaintingMode ?? "DEFAULT"
}
}
return payload;
}
export function formatNovaImageGenerationPayload(taskType: string, prompt: NovaMessagesPrompt, options: ExecutionOptions) {
switch (taskType) {
case NovaImageGenerationTaskType.TEXT_IMAGE:
return textToImagePayload(prompt, options);
case NovaImageGenerationTaskType.TEXT_IMAGE_WITH_IMAGE_CONDITIONING:
return textToImagePayload(prompt, options);
case NovaImageGenerationTaskType.COLOR_GUIDED_GENERATION:
return colorGuidedGenerationPayload(prompt, options);
case NovaImageGenerationTaskType.IMAGE_VARIATION:
return imageVariationPayload(prompt, options);
case NovaImageGenerationTaskType.INPAINTING:
return inpaintingPayload(prompt, options);
case NovaImageGenerationTaskType.OUTPAINTING:
return outpaintingPayload(prompt, options);
case NovaImageGenerationTaskType.BACKGROUND_REMOVAL:
return backgroundRemovalPayload(prompt);
default:
throw new Error("Task type not supported");
}
}
export interface InvokeModelPayloadBase {
taskType: NovaImageGenerationTaskType;
imageGenerationConfig: {
width?: number;
height?: number;
quality?: "standard" | "premium";
cfgScale?: number;
seed?: number;
numberOfImages?: number;
};
}
export interface NovaTextToImagePayload extends InvokeModelPayloadBase {
textToImageParams: {
conditionImage?: string;
controlMode?: "CANNY_EDGE" | "SEGMENTATION";
controlStrength?: number;
text: string;
negativeText?: string;
};
}
export interface NovaImageVariationPayload extends InvokeModelPayloadBase {
imageVariationParams: {
images: string[] //(list of Base64 encoded images),
similarityStrength?: number,
text?: string,
negativeText?: string
}
}
export interface NovaColorGuidedGenerationPayload extends InvokeModelPayloadBase {
colorGuidedGenerationParams: {
colors: string[]; //(list of hex color values),
text: string;
referenceImage?: string;
negativeText?: string;
}
}
export interface NovaInpaintingPayload extends InvokeModelPayloadBase {
inPaintingParams: {
image: string; //(Base64 encoded image),
maskImage?: string; //(Base64 encoded image),
maskPrompt?: string,
negativeText?: string,
text?: string,
}
}
export interface NovaOutpaintingPayload extends InvokeModelPayloadBase {
outPaintingParams: {
image: string; //(Base64 encoded image),
maskImage?: string; //(Base64 encoded image),
maskPrompt?: string,
negativeText?: string,
text?: string,
outPaintingMode: "DEFAULT" | "PRECISE";
}
}
export interface NovaBackgroundRemovalPayload {
taskType: NovaImageGenerationTaskType.BACKGROUND_REMOVAL;
backgroundRemovalParams: {
image: string //(Base64 encoded image),
}
}
export enum NovaImageGenerationTaskType {
TEXT_IMAGE = "TEXT_IMAGE",
TEXT_IMAGE_WITH_IMAGE_CONDITIONING = "TEXT_IMAGE_WITH_IMAGE_CONDITIONING",
COLOR_GUIDED_GENERATION = "COLOR_GUIDED_GENERATION",
IMAGE_VARIATION = "IMAGE_VARIATION",
INPAINTING = "INPAINTING",
OUTPAINTING = "OUTPAINTING",
BACKGROUND_REMOVAL = "BACKGROUND_REMOVAL",
}