Skip to content

Latest commit

 

History

History
982 lines (810 loc) · 25 KB

File metadata and controls

982 lines (810 loc) · 25 KB

Runtime Admin API

This note documents the image-pool runtime API for inspecting models, loading and unloading models, running image generation/edit requests, and starting LoRA training jobs.

The goal is similar to the llm-pool runtime admin API: routine model management should not require editing local.json and restarting the service.

It is intentionally a v1 design:

  • live runtime control only
  • no automatic writes back to settings.json or local.json
  • no arbitrary model definitions via API
  • no runtime load overrides in the load request body
  • no background job system for model loads
  • no force unload or graceful drain state yet

Current reality note:

  • this admin API is implemented and is the live control plane used by the workbench
  • model definitions still come from merged settings.json + local.json
  • load/unload only changes in-process runtime state
  • generation_parameters and edit_parameters are surfaced by public and admin model endpoints
  • recommended_steps and recommended_guidance still exist as compatibility fields
  • imported LoRA records can be listed, inspected, imported, edited, and deleted
  • Flux and Z-Image LoRA training endpoints exist and share one in-process training slot

Contents

Purpose

The current service merges settings.json and local.json into one effective config, then loads enabled models at startup.

The admin API adds a separate live control plane on top of that merged config:

  • the merged config tells us which models are known to the service
  • the live runtime state tells us which of those models are currently loaded
  • generation and edit endpoints accept only loaded models

That distinction must stay explicit in both the API and the UI.

Core Concepts

Configured Model Definition

A configured model definition comes from the merged settings.json + local.json payload.

This is static process input. It includes fields such as:

  • backend
  • enabled
  • target_inflight
  • model_path
  • base_model_path
  • transformer_config_path
  • modalities
  • output_modalities
  • tasks
  • max_images
  • max_output_images
  • vram_estimate_mib
  • recommended_steps
  • recommended_guidance
  • generation_parameters
  • edit_parameters

This definition is not modified by the admin API in v1.

Runtime State

Each configured model also has live runtime state inside the process.

Current states are represented by booleans:

  • loaded
  • loading

The GPU memory view maps those booleans to:

  • unloaded
  • loading
  • loaded

There is no separate unloading state yet.

Capabilities

Capabilities are derived from the model definition.

Current shape:

{
  "input_modalities": ["text", "image"],
  "output_modalities": ["image"],
  "tasks": ["image_generation", "image_to_image"],
  "max_images": 1,
  "max_output_images": 1
}

tasks is the field the UI should use to decide whether a model can be shown for text-to-image, image-to-image, true image edit, or a combination.

  • image_generation: generate from text
  • image_to_image: use input image(s) as generation references or img2img source
  • image_edit: instruction editing where preserving unchanged image regions is part of the model contract

State Semantics

unloaded

  • the model exists in merged config
  • no runtime is currently loaded
  • image requests for this model are rejected
  • the model may be loaded through the admin API

loading

  • a runtime load has started but is not complete yet
  • image requests for this model are rejected
  • duplicate load requests currently race with the in-progress load rather than joining a background job

loaded

  • a runtime exists and may serve image requests
  • the model may be unloaded through the admin API

Failed Load

Failed load attempts store last_error on the model state. The model remains unloaded and may be loaded again through the admin API.

Model Definition Fields

Example model definition:

{
  "backend": "diffusers_z_image",
  "enabled": false,
  "target_inflight": 1,
  "model_path": "/home/gunnar/models/Z-Image-Turbo",
  "modalities": ["text", "image"],
  "output_modalities": ["image"],
  "tasks": ["image_generation", "image_to_image"],
  "max_images": 1,
  "max_output_images": 1,
  "vram_estimate_mib": 16000,
  "recommended_steps": 9,
  "recommended_guidance": 0.0,
  "generation_parameters": {
    "size": {
      "kind": "enum",
      "target": "request",
      "default": "1024x1024",
      "allowed_values": ["768x768", "1024x1024", "1024x768", "768x1024"]
    },
    "steps": {
      "kind": "integer",
      "target": "metadata",
      "default": 9,
      "minimum": 1,
      "maximum": 80,
      "step": 1
    },
    "guidance": {
      "kind": "number",
      "target": "metadata",
      "default": 0.0,
      "minimum": 0.0,
      "maximum": 20.0,
      "step": 0.1
    },
    "seed": {
      "kind": "integer_or_null",
      "target": "request",
      "default": null,
      "minimum": 0,
      "step": 1
    },
    "lora_scale": {
      "kind": "number",
      "target": "metadata",
      "default": 1.75,
      "minimum": 0.0,
      "maximum": 2.0,
      "step": 0.05
    }
  },
  "edit_parameters": {
    "strength": {
      "kind": "number",
      "target": "metadata",
      "default": 0.35,
      "minimum": 0.0,
      "maximum": 1.0,
      "step": 0.05
    }
  }
}

The parameter objects above are shortened for readability. In the live config, edit_parameters usually repeats the shared generation fields and adds edit-only fields.

Known backend ids:

  • stub
  • diffusers_flux2_klein
  • diffusers_firered_gguf
  • diffusers_qwen_image_edit
  • diffusers_sdxl
  • diffusers_z_image

vram_estimate_mib is a configured estimate unless the runtime has observed a load delta for the model during this process lifetime. See GET /v1/admin/gpu-memory.

Endpoints

GET /v1/models

Returns loaded models only. This is the public model list for image requests.

Response shape:

{
  "object": "list",
  "data": [
    {
      "id": "z-image-turbo",
      "object": "model",
      "owned_by": "image-pool",
      "backend": "diffusers_z_image",
      "capabilities": {
        "input_modalities": ["text", "image"],
        "output_modalities": ["image"],
        "tasks": ["image_generation", "image_to_image"],
        "max_images": 1,
        "max_output_images": 1
      },
      "recommended_steps": 9,
      "recommended_guidance": 0.0,
      "generation_parameters": {
        "size": {
          "kind": "enum",
          "target": "request",
          "default": "1024x1024",
          "allowed_values": ["768x768", "1024x1024", "1024x768", "768x1024"]
        },
        "steps": {
          "kind": "integer",
          "target": "metadata",
          "default": 9,
          "minimum": 1,
          "maximum": 80,
          "step": 1
        },
        "guidance": {
          "kind": "number",
          "target": "metadata",
          "default": 0.0,
          "minimum": 0.0,
          "maximum": 20.0,
          "step": 0.1
        },
        "lora_scale": {
          "kind": "number",
          "target": "metadata",
          "default": 1.75,
          "minimum": 0.0,
          "maximum": 2.0,
          "step": 0.05
        }
      },
      "edit_parameters": {
        "strength": {
          "kind": "number",
          "target": "metadata",
          "default": 0.35,
          "minimum": 0.0,
          "maximum": 1.0,
          "step": 0.05
        }
      }
    }
  ]
}

Schema objects in endpoint examples may be shortened. Clients should treat generation_parameters and edit_parameters as dictionaries keyed by parameter name.

GET /v1/admin/models

Returns all known models from merged config together with their live runtime state. This endpoint is the main UI source of truth for model management.

Response shape:

{
  "object": "list",
  "data": [
    {
      "id": "z-image-turbo",
      "backend": "diffusers_z_image",
      "enabled": false,
      "loaded": true,
      "loading": false,
      "loaded_at": 1782500000.0,
      "last_error": null,
      "scheduler": {
        "target_inflight": 1,
        "inflight": 0,
        "queued": 0
      },
      "capabilities": {
        "input_modalities": ["text", "image"],
        "output_modalities": ["image"],
        "tasks": ["image_generation", "image_to_image"],
        "max_images": 1,
        "max_output_images": 1
      },
      "model_path": "/home/gunnar/models/Z-Image-Turbo",
      "base_model_path": null,
      "vram_estimate_mib": 20600,
      "vram_estimate_source": "observed_load_delta",
      "recommended_steps": 9,
      "recommended_guidance": 0.0,
      "generation_parameters": {
        "size": {
          "kind": "enum",
          "target": "request",
          "default": "1024x1024",
          "allowed_values": ["768x768", "1024x1024", "1024x768", "768x1024"]
        },
        "steps": {
          "kind": "integer",
          "target": "metadata",
          "default": 9,
          "minimum": 1,
          "maximum": 80,
          "step": 1
        }
      },
      "edit_parameters": {
        "strength": {
          "kind": "number",
          "target": "metadata",
          "default": 0.35,
          "minimum": 0.0,
          "maximum": 1.0,
          "step": 0.05
        }
      }
    }
  ]
}

vram_estimate_source can currently be:

  • observed_load_delta
  • configured
  • model_artifact_size
  • unavailable

GET /v1/admin/gpu-memory

Returns GPU memory data plus a model-oriented projection for the workbench model table.

Response shape:

{
  "gpus": [
    {
      "index": 0,
      "name": "NVIDIA ...",
      "memory_total_mib": 98304,
      "memory_used_mib": 68200,
      "memory_free_mib": 30104
    }
  ],
  "models": [
    {
      "name": "z-image-turbo",
      "runtime_state": "loaded",
      "is_loaded": true,
      "configured_target_inflight": 1,
      "effective_target_inflight": 1,
      "vram_estimate_mib": 20600,
      "vram_estimate_source": "observed_load_delta"
    }
  ],
  "error": null
}

If GPU probing fails, gpus may be empty and error contains the probe error.

GET /v1/admin/loras

Returns imported LoRA records and the edit schema for imported LoRA metadata.

The edit_schema field tells the workbench which fields can be edited and which values are valid for model-bound fields.

Response shape:

{
  "object": "list",
  "data": [
    {
      "id": "imported/c64style-z-image-turbo",
      "name": "C64style Z Image Turbo",
      "family": "z-image",
      "source_type": "imported",
      "artifact_type": "imported",
      "trained_on_model_id": "z-image-turbo",
      "compatible_models": ["z-image-turbo"],
      "trigger_words": ["C64style pixel art"],
      "default_strength": 1.75,
      "path": "/home/gunnar/projects/image-pool/data/loras/imported/c64style-z-image-turbo/adapter.safetensors",
      "deletable": true,
      "delete_slug": "c64style-z-image-turbo",
      "editable": true,
      "update_slug": "c64style-z-image-turbo",
      "inspection": {
        "format_guess": "diffusers-transformer",
        "family_guess": "z-image",
        "confidence": 0.65,
        "detected_modules": ["transformer"],
        "key_count": 480
      }
    }
  ],
  "edit_schema": {
    "source_types": ["imported"],
    "fields": {
      "family": {
        "kind": "enum",
        "required": true,
        "allowed_values": ["flux2-klein", "sdxl", "z-image"]
      },
      "compatible_models": {
        "kind": "string_list",
        "required": true,
        "allowed_values_by_family": {
          "z-image": ["z-image-base", "z-image-turbo"]
        }
      },
      "default_strength": {
        "kind": "number_or_null",
        "minimum": 0.0,
        "maximum": 2.0,
        "step": 0.05
      }
    }
  }
}

Training-run LoRAs are listed by the workbench, not by this image-pool endpoint. This endpoint owns imported LoRAs stored under image-pool data.

POST /v1/admin/loras/inspect

Inspects a local .safetensors file before import.

Request shape:

{
  "source_path": "/tmp/uploaded-lora.safetensors"
}

The response includes:

  • format_guess
  • family_guess
  • compatible_model_suggestions
  • default_strength_suggestion
  • safetensors metadata and sample tensor keys

Inspection is best-effort. A successful load/generation is still the final compatibility check.

POST /v1/admin/loras/import

Copies a .safetensors file into the image-pool imported LoRA library and writes its editable metadata.

Required fields:

  • source_path
  • name
  • family
  • compatible_models

family and compatible_models are validated against configured LoRA-capable models. A model must support LoRA and belong to the selected family.

PATCH /v1/admin/loras/{slug}

Updates metadata for an imported LoRA.

Editable fields:

  • name
  • family
  • compatible_models
  • trained_on_model_id
  • trigger_words
  • default_strength
  • description
  • source_url

default_strength may be null. family and compatible_models are validated against the same rules as import.

Request shape:

{
  "family": "z-image",
  "compatible_models": ["z-image-turbo"],
  "trained_on_model_id": "z-image-turbo",
  "default_strength": 1.75
}

The response returns the updated LoRA payload.

DELETE /v1/admin/loras/{slug}

Deletes an imported LoRA directory from image-pool data.

Only imported LoRAs have a delete_slug.

POST /v1/admin/models/{model_name}/load

Loads a configured model into the current process.

Request body: none.

Returns the same state object used by GET /v1/admin/models.

Notes:

  • this does not change enabled in config
  • this does not persist anything to settings.json or local.json
  • if the model is already loaded, the current state is returned
  • load failures are returned as 500 Internal Server Error by FastAPI unless caught by a more specific handler

POST /v1/admin/models/{model_name}/unload

Unloads a configured model from the current process.

Request body: none.

Returns the same state object used by GET /v1/admin/models.

Notes:

  • this does not change enabled in config
  • this does not persist anything to settings.json or local.json
  • if the model is already unloaded, the current unloaded state is returned

POST /v1/images/generations

Runs text-to-image generation.

Request shape:

{
  "model": "z-image-turbo",
  "prompt": "A product photo of black running shoes on a concrete floor",
  "n": 1,
  "size": "1024x1024",
  "quality": "auto",
  "response_format": "b64_json",
  "seed": 1234,
  "allow_remote": false,
  "metadata": {
    "steps": 9,
    "guidance": 0.0
  }
}

Response shape:

{
  "id": "img-...",
  "object": "image.generation",
  "created": 1782500000,
  "model": "z-image-turbo",
  "data": [
    {
      "b64_json": "...",
      "mime_type": "image/png",
      "revised_prompt": null
    }
  ],
  "metrics": {
    "steps": 9,
    "guidance": 0.0,
    "engine_queue_wait_ms": 0.2,
    "engine_total_wall_ms": 12345.0,
    "pool_total_wall_ms": 12345.5
  }
}

POST /v1/images/edits

Runs image-to-image or image edit generation. The selected model must advertise image_to_image or image_edit in capabilities.tasks.

Request shape:

{
  "model": "sdxl-base-1.0",
  "prompt": "The same dog wearing sunglasses",
  "images": [
    {
      "name": "dog.png",
      "data_url": "data:image/png;base64,..."
    }
  ],
  "n": 1,
  "size": "1024x1024",
  "quality": "auto",
  "response_format": "b64_json",
  "seed": 1234,
  "allow_remote": false,
  "metadata": {
    "steps": 30,
    "guidance": 5.0,
    "sampler": "euler",
    "strength": 0.35
  }
}

Response shape is the same as POST /v1/images/generations, with object: "image.edit".

GET /v1/training/flux-lora

Returns Flux LoRA training backend availability and current run state.

Response shape:

{
  "backend": {
    "id": "diffusers_flux2_lora",
    "label": "Flux",
    "available": true,
    "message": ""
  },
  "run": {
    "status": "idle",
    "run_id": "",
    "pid": null,
    "returncode": null,
    "started_at": "",
    "completed_at": "",
    "output_path": "",
    "log_tail": "",
    "message": "",
    "backend_id": "",
    "progress": {
      "step": 0,
      "steps": 0,
      "loss": null,
      "learning_rate": null
    }
  }
}

POST /v1/training/flux-lora

Starts a Flux LoRA training run.

Request shape:

{
  "model": "flux2-klein-base-4b",
  "dataset_path": "/home/gunnar/projects/llm-workbench/data/image_pool/training/datasets/example",
  "output_path": "/home/gunnar/projects/llm-workbench/data/image_pool/training/flux2-klein/runs",
  "trigger_word": "GFX_IMPR5N",
  "steps": 3000,
  "learning_rate": 0.000095,
  "rank": 128,
  "alpha": 64,
  "batch_size": 1,
  "checkpoint_interval": 500,
  "resolution": [256, 512, 768, 1024, 1280, 1536],
  "metadata": {}
}

Returns the same shape as GET /v1/training/flux-lora.

POST /v1/training/flux-lora/stop

Requests stop for the active training run. The worker finishes the current step before stopping when possible.

Returns the same shape as GET /v1/training/flux-lora.

GET /v1/training/z-image-lora

Returns Z-Image LoRA training backend availability and current run state.

Response shape is the same as GET /v1/training/flux-lora, with backend.id: "diffusers_z_image_lora".

POST /v1/training/z-image-lora

Starts a Z-Image LoRA training run.

Request shape:

{
  "model": "z-image-base",
  "dataset_path": "/home/gunnar/projects/llm-workbench/data/image_pool/training/datasets/example",
  "output_path": "/home/gunnar/projects/llm-workbench/data/image_pool/training/z-image/runs",
  "trigger_word": "GFX_IMPR5N",
  "steps": 500,
  "learning_rate": 0.0001,
  "rank": 4,
  "alpha": 4,
  "batch_size": 1,
  "checkpoint_interval": 500,
  "resolution": 1024,
  "metadata": {}
}

Returns the same shape as GET /v1/training/z-image-lora.

POST /v1/training/z-image-lora/stop

Requests stop for the active training run. The worker finishes the current step before stopping when possible.

Returns the same shape as GET /v1/training/z-image-lora.

Image Request Parameters

The common request fields are currently:

  • model: configured model id
  • prompt: positive prompt
  • n: output image count, currently 1 to 4 by schema and additionally limited by model max_output_images
  • size: output size string such as 1024x1024
  • quality: auto, low, medium, or high
  • response_format: currently only b64_json
  • seed: optional integer seed
  • allow_remote: reserved request flag
  • metadata: backend-specific runtime parameters

Current metadata keys used by backends:

  • steps: integer, 1 to 80
  • guidance: number, 0.0 to 20.0
  • sampler: SDXL only; one of euler, euler_a, or dpmpp_2m
  • negative_prompt: SDXL only
  • strength: image edit/img2img strength, 0.0 to 1.0
  • lora_id: optional UI/runtime identifier for metrics
  • lora_path: path to .safetensors LoRA weights
  • lora_scale: LoRA adapter weight, 0.0 to 2.0

Current backend defaults:

Backend Steps Guidance Strength Sampler Notes
diffusers_flux2_klein 4 1.0 n/a n/a Supports LoRA metadata
diffusers_sdxl recommended_steps or 30 recommended_guidance or 5.0 0.35 euler Supports negative_prompt
diffusers_z_image recommended_steps or 9 recommended_guidance or 0.0 0.35 n/a Supports LoRA metadata
diffusers_qwen_image_edit recommended_steps or 20 recommended_guidance or 4.0 n/a n/a Uses true_cfg_scale internally
diffusers_firered_gguf 40 4.0 n/a n/a Image edit backend

Parameter Schema

The workbench needs enough model-specific parameter metadata to build the image generation details section for the selected model and to implement Reset defaults without hardcoding per-model rules in the UI.

Capabilities should answer what the model can do. Parameter schemas should answer which controls the UI should show for that model.

generation_parameters is used for text-to-image requests.

edit_parameters is used for image-to-image/edit requests.

edit_parameters is a full schema for edit mode. It is not a patch on top of generation_parameters. Models usually repeat shared fields such as size, n, steps, guidance, and seed there, then add edit-only fields such as strength.

Each entry has a target:

  • request: the value is sent as a top-level image request field
  • metadata: the value is sent inside metadata

Entries may include label. The workbench uses it as the visible control name. Use model-native names when possible, for example SDXL uses Comfy-like labels such as cfg, sampler_name, and denoise.

Enum entries may also include labels. The keys are wire values. The values are option labels, for example euler -> Euler (native).

Implemented model definition shape, including an optional sampler enum:

{
  "generation_parameters": {
    "size": {
      "kind": "enum",
      "target": "request",
      "default": "1024x1024",
      "allowed_values": ["768x768", "1024x1024", "1024x768", "768x1024"]
    },
    "steps": {
      "kind": "integer",
      "target": "metadata",
      "label": "steps",
      "default": 9,
      "minimum": 1,
      "maximum": 80,
      "step": 1
    },
    "guidance": {
      "kind": "number",
      "target": "metadata",
      "label": "cfg",
      "default": 0.0,
      "minimum": 0.0,
      "maximum": 20.0,
      "step": 0.1
    },
    "sampler": {
      "kind": "enum",
      "target": "metadata",
      "label": "sampler_name",
      "default": "euler",
      "allowed_values": ["euler", "euler_a", "dpmpp_2m"],
      "labels": {
        "euler": "Euler (native)",
        "euler_a": "Euler a",
        "dpmpp_2m": "DPM++ 2M"
      }
    },
    "seed": {
      "kind": "integer_or_null",
      "target": "request",
      "default": null,
      "minimum": 0,
      "step": 1
    }
  },
  "edit_parameters": {
    "strength": {
      "kind": "number",
      "target": "metadata",
      "label": "denoise",
      "default": 0.35,
      "minimum": 0.0,
      "maximum": 1.0,
      "step": 0.05
    }
  }
}

Current API behavior:

  • GET /v1/models includes parameter schemas for loaded models
  • GET /v1/admin/models includes the same parameter schemas plus runtime state
  • the workbench renders controls from these schemas
  • Reset defaults applies the selected model's schema defaults
  • recommended_steps and recommended_guidance are still returned for compatibility

Current UI mapping:

  • integer -> stepper or numeric input
  • number -> numeric input, or slider for strength and lora_scale
  • enum -> select
  • string -> text input
  • integer_or_null -> numeric input with empty value as unset

Unload And In-Flight Requests

Current unload behavior is immediate:

  • the model executor is unregistered
  • queued requests are failed with model_not_loaded
  • worker tasks are cancelled
  • the runtime is closed
  • Torch CUDA cache is released

There is no graceful drain mode yet. If the UI needs safer unload behavior during active generation, image-pool should grow an explicit unloading state and reject new requests while allowing in-flight requests to finish.

Errors

Current explicit error handlers:

Condition HTTP status Error type
Unknown model 404 unknown_model
Model not loaded 409 model_not_loaded
Unsupported backend 400 unsupported_backend
Bad request / validation-like runtime error 400 bad_request

Training preflight errors are returned as HTTPException details. Common training error ids:

  • training_dataset_not_ready
  • training_model_not_ready
  • training_backend_unavailable
  • training_run_active