storytellurx-v1/app/actions/generate-image.ts

110 lines
4.1 KiB
TypeScript

"use server";
import type { VisualPromptInstruction } from "../game-engine/game-schemas.ts";
interface ImageGenerationResult {
data: {
output: string;
executionTime: number;
};
promptUsed?: string;
}
type SimpleHistory = Array<{ role: string; content: string }>;
export interface ImageGenerationPayload {
imageUrl?: string;
isLoadingImage: boolean;
}
export async function generateImageAction(
instructions: VisualPromptInstruction,
conversationHistory: SimpleHistory,
): Promise<ImageGenerationResult | { error: string }> {
console.log(
"Generating image with instructions:",
JSON.stringify(instructions, null, 2),
);
console.log(
"Image generation context history (last 5):",
conversationHistory.slice(-5),
);
const _contextPrompt = conversationHistory.slice(-5)
.map((m) => `${m.role}: ${m.content}`)
.join("\n");
const prompt =
`two black skinny laying chubby hairy male ejaculation, (fucked silly:1.2), (front:1.5), (skinny:1.5), (arabian:1.2), (skinny chest:1.5), (black cock:1.2), (sperm:1.4), (bukkake:1.3), (skinny hairy belly:1.4), (anus:1.4), (flat chest:1.2), (black:1.5) men skin (balls:1.7), earring, fiesta, cuban, lingerie (aki3240), gold, (chubby:1.1), (white:1.2), blonde, (hairy:1.8), (nipple piercing:1.2), (argent piercing through clothes:1.1) skinny male, (cock ring:1.1), (cock:1.1), (male:1.5) hairy crochet lace lingerie jumpsuit, male (tan:1.3), male bikini, male slutty, {gold|black|white} lace lingerie, nightwear, standing male chubby boobs, skinny black hairy torso male, lingerie, erotic, hips, skinny, submissive, happy, short hair, red, yellow, cream on breasts, thin, hairy boobs, (hairy:1.3), close, laying down, black, dark skin, {ginger arabian} men skin with erotic sperm liquid fluid {men|transmen} skin, ${
JSON.stringify(instructions)
}.`;
console.log("Runpod Prompt:", prompt);
const response = await fetch(
"https://api.runpod.ai/v2/c9vq7tnegstzie/runsync",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer ".concat(Deno.env.get("RUNPOD_API_KEY") ?? ""),
},
signal: AbortSignal.timeout(1000 * 60 * 0.5),
body: JSON.stringify({
input: {
"enable_refiner": true,
"refiner_denoising_strength": 0.2,
"refiner_num_inference_steps": 4,
"num_inference_steps": 3,
"width": 393,
"height": 652,
"guidance_scale": 1,
"scheduler": "euler_a",
"prompt": prompt,
},
}),
},
);
if (!response.ok) {
const errorBody = await response.text();
console.error("Runpod API Error:", response.status, errorBody);
return {
error: `Runpod API error! Status: ${response.status}. Body: ${errorBody}`,
};
}
try {
const resultJson = await response.json();
// --- Adjust based on Runpod's *actual* success response structure ---
// This assumes the structure you had before is correct. Verify with Runpod docs.
if (
resultJson && resultJson.output && typeof resultJson.output === "string"
) {
console.log("Runpod job suceeded for prompt", prompt);
const data: ImageGenerationResult["data"] = {
output: resultJson.output, // Assuming output is the base64 string
executionTime: resultJson.executionTime ?? 0, // Get execution time if available
};
return {
data,
promptUsed: prompt, // Return the prompt used
};
} else if (resultJson && resultJson.status === "FAILED") { // Check for Runpod failure status
console.error("Runpod job failed:", resultJson);
return {
error: `Runpod job failed. Reason: ${resultJson.error || "Unknown"}`,
};
} else {
console.error("Unexpected Runpod response format:", resultJson);
return {
error: "Unexpected response format from image generation service.",
};
}
// --- End Runpod response handling ---
} catch (e) {
console.error("Failed to parse Runpod JSON response:", e);
return { error: "Failed to parse image generation response." };
}
}