Skip to content

Nextended.Aspire.Hosting.LocalAI

📚 Full API reference — every public type and member, generated from the compiled assembly.

Self-hosted, OpenAI-compatible multimodal AI — image generation, text-to-speech, speech-to-text and video — with gallery model management, GPU support and Open WebUI. NuGet

🇩🇪 Diese Seite auf Deutsch


Installation

bash
dotnet add package Nextended.Aspire.Hosting.LocalAI

Runnable sample

A complete AppHost you can start is checked into the repository:

AiStack.AppHost

bash
git clone https://github.com/fgilde/Nextended.git
cd Nextended/Tests/TestProjects/AiStack.AppHost
dotnet run

Self-hosted, OpenAI-compatible multimodal AI for .NET Aspire — the self-hosted counterpart to AddOllama for everything beyond text. Runs LocalAI as a single container resource that serves image generation, text-to-speech, speech-to-text, video generation, sound/music generation, chat and embeddings on one endpoint, with NVIDIA/AMD GPU support, gallery model management and an optional Open WebUI.

csharp
var builder = DistributedApplication.CreateBuilder(args);

var ai = builder.AddLocalAI("localai")                      // NVIDIA GPU + LocalAI 4.x image by default
    .WithDataVolume()                                       // persist model downloads
    .AddModel(KnownImageModel.Flux1Schnell)                 // image    -> /v1/images/generations
    .AddTextModel(KnownTextModel.Qwen3_8b)                  // chat/LLM -> /v1/chat/completions
    .AddTextToSpeechModel(KnownTextToSpeechModel.Kokoro)    // TTS      -> /v1/audio/speech
    .AddSpeechToTextModel(KnownSpeechToTextModel.WhisperBase) // STT    -> /v1/audio/transcriptions
    .WithOpenWebUI();                                       // dev-time UI (excluded from publish)

builder.AddProject<Projects.Web>("web")
    .WithLocalAI(ai);   // injects AI_API_BASE + IMAGE_MODEL / TEXT_MODEL / TTS_MODEL / STT_MODEL / VIDEO_MODEL / SOUND_MODEL / EMBEDDING_MODEL

Formerly Nextended.Aspire.Hosting.ImageGen (image-only). The container was always a full LocalAI server — this package now exposes the other modalities too. AddLocalAI/WithLocalAI replace AddImageGeneration/WithImageGeneration.

Version / image: the default tag is the standard non-AIO 4.x CUDA image (latest-gpu-nvidia-cuda-12) — that's what ships video generation and the ace-step sound backend. The all-in-one (-aio-) tags are frozen at v3.12.1 upstream (no video/sound); only pick one if you want the bundled default model set. Backends download on demand either way.

What the consumer gets

WithLocalAI(...) injects environment variables into the consuming resource. One base URL serves every modality; the default model per modality is injected only when you added one of that kind.

VariableValue
AI_PROVIDERopenai-compatible
AI_API_BASEthe service endpoint (all endpoints live under it)
IMAGE_MODELdefault image model (first image AddModel, else stablediffusion)
TEXT_MODELdefault chat/LLM model — only if an AddTextModel was added
TTS_MODELdefault TTS model — only if an AddTextToSpeechModel was added
STT_MODELdefault STT model — only if an AddSpeechToTextModel was added
VIDEO_MODELdefault video model — only if an AddVideoModel was added
SOUND_MODELdefault sound/music model — only if an AddSoundModel was added
EMBEDDING_MODELdefault embedding model — only if an AddEmbeddingModel was added
AI_API_KEYonly when configured

Back-compat: IMAGE_PROVIDER, IMAGE_API_BASE (and IMAGE_API_KEY) are still injected too, so existing image-only clients keep working after the rename with no code change.

Endpoints

CapabilityEndpointDefault-model env
Image generationPOST /v1/images/generationsIMAGE_MODEL
Text-to-speechPOST /v1/audio/speechTTS_MODEL
Speech-to-textPOST /v1/audio/transcriptionsSTT_MODEL
Video generationPOST /videoVIDEO_MODEL
Sound / music generationPOST /v1/sound-generationSOUND_MODEL
Chat / LLM / visionPOST /v1/chat/completionsTEXT_MODEL
EmbeddingsPOST /v1/embeddingsEMBEDDING_MODEL

All except /video are OpenAI-compatible, so any OpenAI client works unchanged. /video is LocalAI's own endpoint (there is no OpenAI video standard).

Chat / LLM, vision & embeddings

LocalAI is also a full LLM host — the AddOllama-style role for text, with 1000+ chat models in the gallery. Add them like any other modality; the string overload takes ANY gallery id:

csharp
ai.AddTextModel(KnownTextModel.Qwen3_8b)                    // enum -> gallery name
  .AddTextModel(KnownTextModel.Qwen3Vl8b)                   // vision-capable multimodal
  .AddTextModel("kimi-k2.7-code")                           // any of the 1000+ gallery LLMs by name
  .AddEmbeddingModel(KnownEmbeddingModel.NomicEmbedText);   // -> /v1/embeddings

Curated KnownTextModel picks (any other via string): Qwen3 (qwen3-0.6bqwen3-32b, qwen3-30b-a3b, qwen3-coder-480b-a35b-instruct), Llama 3.x (meta-llama-3.1-8b-instruct, llama-3.3-70b-instruct), Gemma 3 (gemma-3-4b-it-27b-it, vision), DeepSeek (deepseek-ai.deepseek-v3.2, deepseek-ocr), glm-4.7-flash, kimi-k2.7-code/kimi-k2.6, nousresearch_hermes-4-14b, vision qwen3-vl-{4,8,30}b, omni qwen3-omni-30b-a3b-instruct. KnownEmbeddingModel: bert-embeddings, nomic-embed-text-v1.5, bge-m3-colbert, granite-embedding-*, embeddinggemma-300m, qwen3-embedding-*.

Consume via the injected TEXT_MODEL/EMBEDDING_MODEL against {AI_API_BASE}/v1/chat/completions resp. /v1/embeddings (standard OpenAI shape). Browse all models in the LocalAI WebUI "Models" tab or at https://localai.io/gallery.html.

Generating from your app

Prefer calling the service server-side: if you set an ApiKey it must never reach the browser, and LocalAI is meant to live on a trusted network, not be exposed publicly.

Images

The endpoint speaks the OpenAI Images API and returns a base64 PNG.

csharp
// C# (from the consuming service)
var baseUrl = builder.Configuration["AI_API_BASE"]!;   // injected, e.g. http://localhost:5069
var model   = builder.Configuration["IMAGE_MODEL"]!;
var apiKey  = builder.Configuration["AI_API_KEY"];     // only if you set o.ApiKey

using var http = new HttpClient();
if (!string.IsNullOrEmpty(apiKey))
    http.DefaultRequestHeaders.Authorization = new("Bearer", apiKey);

var res = await http.PostAsJsonAsync($"{baseUrl}/v1/images/generations", new
{
    model, prompt = "a lighthouse at dawn, cinematic", size = "1024x1024", n = 1, response_format = "b64_json",
});
res.EnsureSuccessStatusCode();
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var b64 = doc.RootElement.GetProperty("data")[0].GetProperty("b64_json").GetString();
await File.WriteAllBytesAsync("out.png", Convert.FromBase64String(b64!));
ts
// TypeScript / JavaScript (Node, TanStack Start server fn, Next route handler, …)
// Keep this on the server — never ship AI_API_KEY to the browser.
const base   = process.env.AI_API_BASE!;   // injected by WithLocalAI
const apiKey = process.env.AI_API_KEY;      // optional
const auth   = apiKey ? { Authorization: `Bearer ${apiKey}` } : {};

const res = await fetch(`${base}/v1/images/generations`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...auth },
  body: JSON.stringify({
    model: process.env.IMAGE_MODEL, prompt: "a lighthouse at dawn", size: "1024x1024", n: 1, response_format: "b64_json",
  }),
});
if (!res.ok) throw new Error(`image backend ${res.status}: ${await res.text()}`);
const json = await res.json();
// OpenAI Images shape; extract defensively — some models/backends return a url instead.
const b64 = json.data?.[0]?.b64_json ?? json.images?.[0]?.b64_json;
const bytes = Buffer.from(b64, "base64");   // -> save, upload to storage, or return as data URI

Audio — text-to-speech & transcription

TTS returns audio bytes (not JSON); STT is a multipart upload returning { text }.

ts
// Text-to-speech (OpenAI-compatible). response_format: wav | mp3 | aac | flac | opus.
const speech = await fetch(`${base}/v1/audio/speech`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...auth },
  body: JSON.stringify({ model: process.env.TTS_MODEL, input: "Willkommen bei promote.me", response_format: "mp3" }),
});
const audio = Buffer.from(await speech.arrayBuffer());   // -> save as .mp3 / stream to the client

// Speech-to-text (OpenAI-compatible, multipart/form-data).
const form = new FormData();
form.append("model", process.env.STT_MODEL!);
form.append("file", new Blob([audioBytes], { type: "audio/wav" }), "clip.wav");
const stt = await fetch(`${base}/v1/audio/transcriptions`, { method: "POST", headers: auth, body: form });
const { text } = await stt.json();

Video

POST /video is long-running (seconds to minutes, GPU-bound) and returns a URL (or base64) to the generated clip. Treat it as a job, not a request/response you block a UI on.

ts
const res = await fetch(`${base}/video`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...auth },
  body: JSON.stringify({
    model: process.env.VIDEO_MODEL,
    prompt: "a neon city skyline at night, slow camera pan",
    width: 512, height: 512, num_frames: 16, fps: 8,
    response_format: "url",   // or "b64_json"
  }),
});
const json = await res.json();
const clipUrl = json.data?.[0]?.url;   // fetch / stream the mp4
bash
curl {AI_API_BASE}/video -H "Content-Type: application/json" -d '{
  "model": "vllm-omni-wan2.2-t2v", "prompt": "A cat playing in a garden on a sunny day",
  "width": 512, "height": 512, "num_frames": 16, "fps": 8 }'

Other /video params: negative_prompt, start_image, end_image, input_reference, seconds, size, seed, cfg_scale, step.

WebUI note (LocalAI ≤ 3.12.x): in LocalAI's built-in WebUI the Video tab's model dropdown reverts to "select a model" and won't keep a vllm-omni (or other non-diffusers) video model selected — a known upstream WebUI bug (#8659, fixed by PR #8781, which added vllm-omni to the WebUI's video-usecase detection). It is a UI-only bug: driving POST /video via the API (as shown above, and as this package does) works regardless. Fix ships in LocalAI ≥ 4.0 — upgrade the image tag if you need the WebUI video picker.

Sound / music

POST /v1/sound-generation is LocalAI's ElevenLabs-compatible music/sound-effect endpoint (distinct from TTS on /v1/audio/speech). It takes model_id + text and returns audio bytes (wav/flac/mp3). Like video it is long-running / GPU-bound — treat it as a job. ACE-Step also accepts optional music metadata (lyrics, bpm, keyscale, duration_seconds, …).

ts
const res = await fetch(`${base}/v1/sound-generation`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...auth },
  body: JSON.stringify({
    model_id: process.env.SOUND_MODEL,          // e.g. "ace-step-turbo"
    text: "an upbeat lofi hip-hop beat, mellow piano, 90 bpm",
    // optional ACE-Step music controls:
    // lyrics: "[Verse 1]\n…", bpm: 90, keyscale: "C major", duration_seconds: 30,
  }),
});
if (!res.ok) throw new Error(`sound backend ${res.status}: ${await res.text()}`);
const audio = Buffer.from(await res.arrayBuffer());   // -> save as .wav / stream to the client
bash
curl {AI_API_BASE}/v1/sound-generation -H "Content-Type: application/json" -d '{
  "model_id": "ace-step-turbo", "text": "A funky Japanese disco track",
  "bpm": 120, "keyscale": "Ab major", "duration_seconds": 30 }' --output music.wav

Calling straight from the browser is possible for the OpenAI-compatible endpoints — but only in a trusted/dev setup: no API key (nothing to leak) and the backend reachable with permissive CORS. For anything user-facing, proxy through your own backend so you keep control of auth, rate limiting and prompt policy. (This server-proxy shape is exactly what the promote.me app uses in media-provider.server.tsgenerateSpeech / transcribeAudio / generateVideo / generateSound.)

Using it from n8n

Because the endpoint is OpenAI-compatible, LocalAI is a drop-in backend for n8n's OpenAI, AI Agent and Embeddings OpenAI nodes — point them at {endpoint}/v1 with any key (default sk-local); pair it with AddOllama for the native Ollama nodes. When you host n8n via Nextended.Aspire.Hosting.N8n, wire the URL in and order startup:

csharp
var localai = builder.AddLocalAI("localai").AddTextModel(KnownTextModel.Qwen3_8b);

var n8n = builder.AddN8n("n8n")
    .WaitFor(localai)
    .WithEnvironment("OPENAI_API_BASE_URL",
        ReferenceExpression.Create($"{localai.Resource.HttpEndpoint}/v1"))
    .WithEnvironment("OPENAI_API_KEY", "sk-local");

n8n keeps node credentials in its own DB, so you still add the OpenAI credential once in the editor (Base URL http://localai:8080/v1, key sk-local) — or pre-seed it via WithImportCredentials. A full runnable example (LocalAI + Ollama + n8n) is in Tests/TestProjects/AiStack.AppHost.

Options

csharp
builder.AddLocalAI("localai", o =>
{
    o.Gpu = LocalAiGpu.Nvidia;               // None | Nvidia | Amd
    o.Image = "localai/localai";
    o.Tag = "latest-aio-gpu-nvidia-cuda-12"; // AIO = batteries included; slim tags also work
    o.HostPort = 5069;                       // fixed host port (optional)
    o.AioProfile = "gpu-8g";                 // force AIO profile (see GPU note below)
    o.ApiKey = "my-secret";                  // require a bearer key
});

Models

Each Add…Model call installs a model from the LocalAI gallery on startup and tags it with a modality, so WithLocalAI knows which default-model env var to inject. The typed enums are a curated convenience — any gallery model works via the string overload, and the gallery is always the source of truth.

  • AddModel — text-to-image (KnownImageModel, gallery names, huggingface/OCI URIs, config URLs)
  • AddTextToSpeechModel — TTS (KnownTextToSpeechModel or any gallery name)
  • AddSpeechToTextModel — STT (KnownSpeechToTextModel or any gallery name)
  • AddVideoModel — video (KnownVideoModel or any gallery name)
  • AddSoundModel — sound / music generation (KnownSoundModel or any gallery name)
  • AddHuggingFaceModel — any diffusers-format HuggingFace image repo (not in the gallery)

Image models (AddModel)

KnownImageModelGallery name (API model)Notes
StableDiffusionAiostablediffusionBundled with AIO images; the default when no image model is added
StableDiffusion15sd-1.5-ggmlClassic SD 1.5, small & fast (GGML)
StableDiffusion3Mediumstable-diffusion-3-mediumSD 3 Medium
StableDiffusion35Mediumsd-3.5-medium-ggmlSD 3.5 Medium (GGML)
StableDiffusion35Largesd-3.5-large-ggmlSD 3.5 Large (GGML), higher quality & VRAM
DreamShaperdreamshaperPopular general-purpose SD 1.5 fine-tune
Flux1Schnellflux.1-schnellFLUX.1 [schnell], fast few-step
Flux1Devflux.1-dev-ggmlFLUX.1 [dev] (GGML)
Flux1DevUncensoredflux.1-dev-ggml-abliterated-v2-q8_0FLUX.1 [dev] abliterated — no content filter, GGML Q8 (~13 GB)
Flux1DevUncensoredDiffusersflux.1dev-abliteratedv2Same model, full fp16 via the python diffusers backend (~24 GB, heavy VRAM)
Flux1KontextDevflux.1-kontext-devFLUX.1 Kontext [dev] (instruction / image editing)
Flux1KreaDevflux.1-krea-dev-ggmlFLUX.1 Krea [dev] (GGML)
Flux2Devflux.2-devFLUX.2 [dev]
Flux2Klein4bflux.2-klein-4bFLUX.2 Klein, 4B params (lighter)
Flux2Klein9bflux.2-klein-9bFLUX.2 Klein, 9B params
Ideogram4ideogram-4-q8_0-ggmlIdeogram 4 (GGML Q8)
ZImageTurboz-image-turbo-diffusersZ-Image Turbo (diffusers backend)
ZImageTurboGgmlZ-Image-TurboZ-Image Turbo (GGML build)
QwenImageqwen-imageQwen-Image
Chroma1Hdchroma1-hdChroma1 HD
csharp
ai.AddModel(KnownImageModel.Flux1Schnell)          // enum -> gallery name
  .AddModel(KnownImageModel.Flux1DevUncensored)    // uncensored FLUX (GGML Q8)
  .AddModel("dreamshaper");                         // implicit string -> ImageModel

Audio models (AddTextToSpeechModel / AddSpeechToTextModel)

Text-to-speech (served on /v1/audio/speech):

KnownTextToSpeechModelGallery nameNotes
KokorokokoroMultilingual (incl. German), fast, high quality — good default
KokoroGermankokoros-deKokoro (Rust port) with German voices
VibeVoicevibevoiceExpressive multi-speaker TTS
OmniVoiceomnivoice-cppFast TTS with voice cloning from a reference clip
OmniVoiceHqomnivoice-cpp-hqHigher-fidelity OmniVoice variant
PocketTtspocket-ttsSmall, fast general-purpose TTS
OuteTtsoutettsMultilingual TTS
KittenTtskitten-ttsVery small / fast TTS
PiperGermanvits-piper-de_DE-thorsten-sherpaPiper German voice (Thorsten), robust offline

Speech-to-text / whisper (served on /v1/audio/transcriptions):

KnownSpeechToTextModelGallery nameNotes
WhisperBasewhisper-base74M — good default, fast, low memory
WhisperSmallwhisper-small244M — better accuracy, still light
WhisperMediumwhisper-medium769M — high accuracy
WhisperLargeV3whisper-large-v31.55B — best accuracy, multilingual; wants a GPU
csharp
ai.AddTextToSpeechModel(KnownTextToSpeechModel.Kokoro)
  .AddSpeechToTextModel(KnownSpeechToTextModel.WhisperBase)
  .AddTextToSpeechModel("vibevoice-cpp");   // any other gallery TTS model by exact name

Video models (AddVideoModel)

Served on POST /video. Weights are large (many GB) and generation is slow / GPU-bound — combine with WithDataVolume().

KnownVideoModelGallery nameNotes
Wan22TextToVideovllm-omni-wan2.2-t2vWan 2.2 text-to-video, 14B (vllm-omni). Strong GPU
Wan22ImageToVideovllm-omni-wan2.2-i2vWan 2.2 image-to-video, 14B (vllm-omni)
Wan21TextToVideoGgmlwan-2.1-t2v-1.3b-ggmlWan 2.1 text-to-video 1.3B, GGUF — cheapest, CPU-offloadable (~10 GB RAM)
Wan21ImageToVideo480pGgmlwan-2.1-i2v-14b-480p-ggmlWan 2.1 image-to-video 14B 480p, GGUF Q4
Wan21ImageToVideo720pGgmlwan-2.1-i2v-14b-720p-ggmlWan 2.1 image-to-video 14B 720p, GGUF Q4_K_M
Wan21FirstLastFrameToVideo720pGgmlwan-2.1-flf2v-14b-720p-ggmlWan 2.1 first-last-frame→video 14B 720p — interpolate/loop between two images
Ltx2ltx-2Lightricks LTX-2 — synchronized audio + video (diffusers). GPU
Ltx23ltx-2.3Lightricks LTX-2.3 — improved LTX-2, synchronized audio-video (diffusers). GPU

The video gallery moves fast. Install any other family by its exact gallery id:

csharp
ai.AddVideoModel(KnownVideoModel.Wan22TextToVideo)
  .AddVideoModel(KnownVideoModel.Ltx2)
  .AddVideoModel("some-new-video-id");   // any gallery id — browse https://localai.io/gallery.html

The built-in WebUI's Video tab can't select vllm-omni video models before LocalAI 4.0 — see the WebUI note under Video above. The API works either way.

Sound / music models (AddSoundModel)

Served on the ElevenLabs-compatible POST /v1/sound-generation (model_id + text; ACE-Step also takes lyrics/bpm/keyscale/duration_seconds). Multi-GB and GPU-bound — combine with WithDataVolume().

KnownSoundModelGallery nameNotes
AceStepTurboace-step-turboACE-Step 1.5 Turbo — music from text/lyrics with BPM/key control (ace-step backend). Good default
AceStepCppTurboacestep-cpp-turboACE-Step 1.5 Turbo, native C++/GGML (acestep-cpp), stereo 48kHz, Q8_0
AceStepCppTurbo4bacestep-cpp-turbo-4bACE-Step 1.5 Turbo C++/GGML with the larger 4B LM — higher quality
csharp
ai.AddSoundModel(KnownSoundModel.AceStepTurbo)
  .AddSoundModel("acestep-cpp-turbo");   // any gallery sound_generation model by exact name

HuggingFace diffusers image models (AddHuggingFaceModel)

Not in the LocalAI gallery — loaded from a HuggingFace repo by generating a model config that is bind-mounted into the container; LocalAI downloads the weights on startup. Includes curated, verified NSFW-capable SDXL models for adult platforms.

csharp
ai.AddHuggingFaceModel(KnownHuggingFaceImageModel.RealVisXL4)      // photorealistic, NSFW-capable
  .AddHuggingFaceModel(KnownHuggingFaceImageModel.NsfwGenV2)       // explicit content, unfiltered
  .AddHuggingFaceModel("mymodel", "SG161222/RealVisXL_V5.0");      // any repo id (string overload)
KnownHuggingFaceImageModelHuggingFace repoNotes
RealVisXL4SG161222/RealVisXL_V4.0Photorealistic SDXL, NSFW-capable
RealVisXL5SG161222/RealVisXL_V5.0Photorealistic SDXL v5
RealVisXL5LightningSG161222/RealVisXL_V5.0_LightningLightning variant — 6 steps
NsfwGenV2UnfilteredAI/NSFW-gen-v2Explicit content, unfiltered
NsfwGenAnimeUnfilteredAI/NSFW-GEN-ANIMEExplicit anime, unfiltered
NsfwV1stablediffusionapi/nsfwExplicit content
OmnigenXLstablediffusionapi/omnigen-xlOmniGen XL
OmnigenXLNsfwstablediffusionapi/omnigenxlnsfwsfw-v10OmniGen XL, NSFW
WaiIllustriousSDXLvotepurchase/waiIllustriousSDXL_v150Illustrious / anime SDXL
PonyDiffusionV6XLJohn6666/pony-diffusion-v6-xl-sdxl-spoPony Diffusion v6 XL
DreamShaperXLTurboLykon/dreamshaper-xl-v2-turboTurbo — 8 steps
DreamShaper8Lykon/dreamshaper-8DreamShaper 8 (SD 1.5)
AnimagineXL4cagliostrolab/animagine-xl-4.0Anime SDXL v4
AnimagineXL31cagliostrolab/animagine-xl-3.1Anime SDXL v3.1
PlaygroundV25playgroundai/playground-v2.5-1024px-aestheticPlayground v2.5
SdxlBasestabilityai/stable-diffusion-xl-base-1.0Stock SDXL base 1.0
MajicMixRealisticdigiplay/majicMIX_realistic_v7Photoreal SD 1.5
JuggernautXLv9RunDiffusion/Juggernaut-XL-v9Juggernaut XL v9
EpicRealismXLmisri/epicrealismXL_v7FinalDestinationepiCRealism XL
EpicRealism15emilianJR/epiCRealismepiCRealism (SD 1.5)
RealisticVision51stablediffusionapi/realistic-vision-v51Realistic Vision v5.1

These run on LocalAI's python diffusers backend (first use downloads the backend + weights; SDXL-class models want ≥8 GB free VRAM). Sampler steps and the fp16 file-variant flag are set automatically per known model — turbo/lightning models use fewer steps, and repos that don't ship *.fp16.safetensors load their default-named weights (the wrong flag fails to load). For custom repos via the string overload, tune steps and pass f16: true only if the repo ships fp16-variant files:

csharp
ai.AddHuggingFaceModel(
    name: "my-turbo",
    hfRepo: "some/sdxl-turbo-repo",
    pipelineType: "StableDiffusionXLPipeline",   // default; fits SDXL-class models
    steps: 8,                                     // turbo/lightning want ~6-8
    f16: true);                                   // only if the repo publishes fp16 files

Changing/adding models on an existing stack: LocalAI imports each model config into its /models volume on first start and does not overwrite it later. After changing models (or this package), drop the volume so configs re-import: docker volume rm <name>-models.

Note: LocalAI gallery names (used with AddModel/AddTextToSpeechModel/…) and HuggingFace repos (used with AddHuggingFaceModel) are different namespaces. A gallery name like nsfw-v1 does not exist in the gallery — use AddHuggingFaceModel(KnownHuggingFaceImageModel.NsfwV1).

Browse everything at https://localai.io/gallery.html.

Behavior notes:

  • No Add…Model → AIO images load their bundled default set (image-gen, chat, tts, …).
  • With any Add…Model → only the models you added are downloaded/loaded (the AIO default set is overridden via the MODELS env var). Mix modalities freely in one service.
  • WithDataVolume() persists /models and /backends — both downloads are multi-GB and only happen once.

GPU notes

  • NVIDIA: the container gets --gpus all. Requirements: recent NVIDIA driver + Docker Desktop (WSL2) or NVIDIA Container Toolkit on Linux. Verify passthrough with: docker run --rm --gpus all ubuntu nvidia-smi
  • Docker Desktop / WSL2 quirk: LocalAI's AIO images detect GPUs via lspci, which does not see the WSL2 GPU even when --gpus all works. This package therefore forces PROFILE=gpu-8g for NVIDIA + AIO automatically (override via AioProfile).
  • First start downloads several GB (backends + models) — be patient, then use WithDataVolume() so it only happens once.

UIs

  • LocalAI WebUI is built in — open the resource endpoint in the browser. It has real text-to-image, text-to-speech, transcription, video and sound-generation tabs, so it doubles as a quick way to try every modality without writing a line of client code. (Before LocalAI 4.0 the Video tab can't keep a vllm-omni video model selected — a UI-only bug, see the Video section; the POST /video API is unaffected.)
  • SD.Next (for serious image work): WithSdNextUi() adds a full SD.Next studio — proper txt2img/img2img UI, model & LoRA management, Civitai/HuggingFace downloads. Runs its own GPU container with its own models (independent of LocalAI). Default image vladmandic/sdnext-cuda:latest, UI on port 7860.
  • ACE-Step UI (music studio): WithAceStepUi() adds a local Suno-style music studio — ace-step-ui (song library, lyrics editor, stem separation, audio editor) plus the ACE-Step 1.5 server it requires, run in Gradio mode with --enable-api (the API surface the UI generates through). NOTE: the UI speaks ACE-Step's own Gradio/REST API — not LocalAI's /v1/sound-generation — so like SD.Next it runs its own model container with its own weights ({name}-checkpoints volume, ~18 GB on first start); models added via AddSoundModel are independent of it. Both containers are built from source on first run: the UI ships no image, and the server is built from the pinned ACE-Step tag v0.1.4 — the UI calls the Gradio endpoint with positional args, and its argument list matches exactly that signature (newer releases inserted parameters, shifting positions and breaking generation; GHCR offers no v0.1.4 image). If you change ApiGitRef/ApiTag, pin UiGitRef to a UI revision built against that server version. The generated server Dockerfile also pins torchcodec==0.10.0 (the release matching torch 2.10 on the CUDA-12.8 base) — v0.1.4 has no lockfile, so an unpinned build otherwise pulls a torchcodec built for CUDA 13 and cover/reference-audio generation fails to decode its input (plain text2music is unaffected).
  • Open WebUI: WithOpenWebUI() adds a ghcr.io/open-webui/open-webui container. Overloads let you reuse an existing Open WebUI (e.g. the one from the Ollama integration) instead of a second one:
    csharp
    var ollama = builder.AddOllama("ollama").WithOpenWebUI();  // Ollama's Open WebUI
    var ai = builder.AddLocalAI("localai").AddModel(KnownImageModel.Flux1Schnell)
        .WithOpenWebUI(useExistingIfFound: true);              // reuse it, add image models
    // or pass it explicitly:
    var ui = builder.Resources.OfType<OpenWebUIResource>().FirstOrDefault();
    if (ui is not null) ai.WithOpenWebUI(ui);
    Note Open WebUI's image generation is awkward — you chat with a text model and press the image button; selecting an image model as a chat model yields unimplemented (it hits /v1/chat/completions). Prefer the LocalAI WebUI or SD.Next for pure image generation.
csharp
var ai = builder.AddLocalAI("localai")
    .WithDataVolume()
    .AddHuggingFaceModel(KnownHuggingFaceImageModel.NsfwV1)
    .WithSdNextUi()      // full image studio on :7860
    .WithAceStepUi()     // Suno-style music studio (own ACE-Step 1.5 server)
    .WithOpenWebUI();    // optional, chat-first

All UIs are dev-time only (ExcludeFromManifest).

Supported frameworks

  • net8.0
  • net9.0
  • net10.0

Dependencies

  • Nextended.Aspire
  • Aspire.Hosting.AppHost