138 lines · 4.2 KB · TypeScript
RawDownload
// CRITICALimport { existsSync, mkdirSync, readdirSync } from "node:fs";import { dirname, join } from "node:path";import { resolveBinary, runCommand } from "../../command/command-utilities";import { runCli } from "../cli/cli-runner";import type { ImageAdapter, ImageBackend } from "./types"; const listFiles = (directory: string): string[] => {  try {    return readdirSync(directory, { withFileTypes: true })      .filter((entry) => entry.isFile())      .map((entry) => entry.name);  } catch {    return [];  }}; /** * Adapter for `stable-diffusion.cpp` CLI (`sd`) image generation. */export class StableDiffusionCppAdapter implements ImageAdapter {  public readonly id = "stable-diffusion.cpp";   /**   * Resolve the configured image generation CLI binary.   * @returns Binary path, or null if not found.   */  private resolveCli(): string | null {    const configured = (process.env["VLLM_STUDIO_IMAGE_CLI"] || "sd").trim();    return resolveBinary(configured);  }   /**   * Check whether the integration is installed (binary resolvable).   * @returns True if installed.   */  public isInstalled(): boolean {    return Boolean(this.resolveCli());  }   /**   * Get the CLI version string.   * @returns Version string, or null if unavailable.   */  public async getVersion(): Promise<string | null> {    const cli = this.resolveCli();    if (!cli) return null;    const result = runCommand(cli, ["--version"], 2000);    if (result.status === 0) {      const line = (result.stdout || result.stderr).split("\n")[0]?.trim();      return line || null;    }    return null;  }   /**   * List discoverable image models in the models directory.   * @param modelsDirectory - Base models directory.   * @returns Model filenames.   */  public async listModels(modelsDirectory: string): Promise<string[]> {    const directory = join(modelsDirectory, "image");    const files = listFiles(directory);    return files.filter((name) => name.endsWith(".gguf") || name.endsWith(".safetensors") || name.endsWith(".ckpt"));  }   /**   * Report the preferred backend(s) for this integration.   * @returns Backends.   */  public async getBackends(): Promise<ImageBackend[]> {    const backend = (process.env["VLLM_STUDIO_IMAGE_BACKEND"] || "").trim().toLowerCase();    if (backend) return [backend as ImageBackend];    return ["cpu"];  }   /**   * Generate an image via the CLI adapter.   * @param args - Generation arguments.   * @param args.prompt - Prompt text.   * @param args.negativePrompt - Optional negative prompt.   * @param args.width - Image width.   * @param args.height - Image height.   * @param args.steps - Diffusion steps.   * @param args.seed - Optional seed.   * @param args.modelPath - Model file path.   * @param args.outputPath - Output file path.   * @returns void   */  public async generate(args: {    prompt: string;    negativePrompt?: string | null;    width: number;    height: number;    steps: number;    seed?: number | null;    modelPath: string;    outputPath: string;  }): Promise<void> {    const cli = this.resolveCli();    if (!cli) {      throw new Error("Image integration not installed (missing sd CLI). Set VLLM_STUDIO_IMAGE_CLI.");    }    if (!existsSync(args.modelPath)) {      throw new Error(`Image model not found: ${args.modelPath}`);    }    const outputDirectory = dirname(args.outputPath);    mkdirSync(outputDirectory, { recursive: true });     // Stable Diffusion CLI flags vary; these are a pragmatic baseline for stable-diffusion.cpp `sd`.    const cliArguments: string[] = [      "-m",      args.modelPath,      "-p",      args.prompt,      "-W",      String(args.width),      "-H",      String(args.height),      "--steps",      String(args.steps),      "-o",      args.outputPath,    ];    if (args.negativePrompt) {      cliArguments.push("--negative-prompt", args.negativePrompt);    }    if (typeof args.seed === "number") {      cliArguments.push("--seed", String(args.seed));    }     const result = await runCli(cli, cliArguments, { timeoutMs: 600_000 });    if (result.exitCode !== 0) {      const details = result.stderr || result.stdout || "unknown error";      throw new Error(`Image generation failed: ${details}`);    }  }}