• v0.14.7
    Release / build (push) Successful in 5m33s
    Release / notes (push) Successful in 4s
    CI / test (push) Successful in 10m30s
    Stable

    gitea-actions released this 2026-10-16 20:16:48 +02:00 | 158 commits to main since this release

    The judge installed by server init sized for what it does. On a machine
    of fewer than eight cores v0.14.6 left every judgment past its budget, so
    fail: closed refused every request. A single-machine install of v0.14.7
    on a 4-core VM passed 20 of 20, and on 8 cores 20 of 20 under load.

    Upgrading.

    • No migration.
    • Upgrading does not change a judge already in the catalog. v0.14.6
      and earlier installed two judges, reserving no cores or one, and 2 or
      4 GB. To give them what they need:
      llmgw ctl model resources qwen3guard-0.6b --memory 2 --cores 2, then
      llmgw ctl restart --model qwen3guard-0.6b. The two then need four cores
      on a single machine; below that, a longer latency_budget or
      fail: open.
    • On a node whose cores are all reserved, ctl restart --model cannot
      start the new replica beside the old one and waits. Stop the old engine
      process, and the replica comes back with the new figures.

    Fixed

    • Both judges fit an 8 GB machine again. v0.14.6 had server init reserve 4 GB a judge replica. That figure came
      from one replica at 3.2 GB after a day of traffic. The growth was llama.cpp's prompt cache, up to 8 GB by default,
      which a judge never reuses: each text it reads is different. On a 4-core, 8 GB machine the second judge then fitted
      nowhere ("has 7 GB of memory with 1 free, needs 4"). The judge now runs with --cache-ram 0, uses about 1.6 GB and
      stays there, and reserves 2.
    • One machine runs one judge, on up to four cores. A second replica on the same machine shares its cores and is
      lost with it, so it bought nothing but half the speed of each. server init on a single machine now serves one judge
      on up to four cores (2.3 s a judgment where two on two cores took 4.2). Where there will be other nodes, it serves two
      on two cores each, as in HA setups. --judge-replicas and the setup's Copies still choose.
    • A judge gets the two cores it needs. v0.14.6 gave each judge replica one core on machines under eight. A judgment
      of about 280 tokens takes 8.0 s on one core, 4.2 s on two and 2.3 s on four, against the 5 s budget, so on a 4-core
      machine every judgment timed out and every request was refused. Where there are two judges, each now reserves two
      cores (one only below four CPUs), and the one judge of a single machine up to four; the starter has what is left, or
      shares when nothing is. A judge on two cores stays close to its budget: give it more cores or a GPU, or raise
      latency_budget, where requests must not wait.
    Downloads
  • v0.14.6
    CI / test (push) Failing after 30m6s
    Release / build (push) Successful in 17m32s
    Release / notes (push) Successful in 4s
    Stable

    gitea-actions released this 2026-10-16 14:16:48 +02:00 | 164 commits to main since this release

    Setup that says what each part of an install is and lets every one be
    chosen and set; three machines that each run the gateway, so any one can
    be lost; a cluster that loses its store's primary without losing a
    request; one ComfyUI for an environment's models; pictures, 3D and
    workflows as MCP tools; alerts asked for at setup; extended thinking.

    Upgrading.

    • No migration.
    • Control planes: with control.lease_ttl unset the lease is now 10 s
      (was 15). Upgrade the standbys first and the leader last; a mix of
      versions meanwhile is fine.
    • The store (PostgreSQL): each member is dialled for 3 s before the
      next is tried. A store so far away that connecting takes longer needs
      connect_timeout=10 in its address.
    • Nodes notice a control plane that died without closing its
      connection in 8 s (was 15) once they run this release.
    • Judges installed by an earlier server init keep reserving no cores
      and 2 GB. To give them their own:
      llmgw ctl model resources qwen3guard-0.6b --memory 4 --cores 2, then
      llmgw ctl restart --model qwen3guard-0.6b.
    • Patroni replaces a lost primary within 20 s on a new cluster. On an
      existing one, run server init --patroni ... again from the machine with
      the authority; it changes only that.
    • A floating address keeps VRRP router id 51 until server add --vip
      runs again, which moves it to one of its own; run it for every standby.
    • A request whose judge is not there yet waits up to the policy's
      latency_budget for one before fail: closed refuses it.

    Added

    • Setup lets every part be chosen, each with its settings, and says what each is. sudo llmgw on a new machine lists
      the parts of an install: the control plane, the store, the model mirror, this machine as a node, the starter model,
      pictures and 3D, the guardrails' judge, the firewall, notifications, limits and keys, on-demand turns, an offline
      bundle. Beside each is what it will be or what is missing without it. Space turns a part on or off, and Enter opens
      its settings in a dialog that explains it at more length. The settings are: which starter model (six Qwen sizes, or
      any GGUF on Hugging Face) and its context; the mirror's directory or another machine's mirror; the judge's
      categories, copies, wait and what happens when it does not answer; the portal's language; the sign-up form; the
      default limits; the longest a key lives; the on-demand waits. A plan page then lists what is installed and what is
      left out, with the same install as a command. Every setting is a server init flag too (--starter,
      --judge-fail, --media, --max-key-ttl, ...).

    • Three machines are high availability for the gateway, not only the store. With three machines, setup makes the two
      database machines control planes too, standing by while the first leads, with an optional floating address
      (keepalived) that follows whichever leads. Any one machine can now be lost. By command: server init --patroni ... --standbys --vip 10.0.0.160/24. The mirror's management API answers the three control planes, so a standby that
      comes to lead can still fetch models.

    • Pictures uploaded to work from: /v1/files takes purpose=vision or
      user_data besides batch, kept as long as job results, so a picture is
      sent once and named by its file-… id in the MCP tools and a workflow's
      inputs.

    • One ComfyUI for an environment's models: ComfyUI models in one
      comfyui.environment (two or more) share one replica — the catalog
      makes it a hidden entry, comfyui-env-<name>, on the first member's
      profiles with the most memory any member needs, carrying every member's
      weights and custom nodes. Asking for any member starts it; it answers for
      each with that member's workflow and checkpoint, and the members get no
      replicas of their own. A picture model and a 3D model on one card stop
      handing it to each other with a cold start each way.

    • Pictures, 3D models, vision and workflows as MCP tools at /mcp,
      beside the memory notes, each offered only where the gateway has a model
      for it: generate_image (with concept, side and texture styles),
      generate_mesh (from a picture or a prompt), review_images (a vision
      model, to a JSON schema), run_workflow, job_status and
      gateway_status. Each makes its request under the caller's key, so
      allowlists, limits, licences and budgets apply and it is charged like
      any request; what it makes is kept as the caller's file and answered by
      id and download URL. A client that takes a stream gets
      notifications/progress while a mesh is made and waits up to ten
      minutes; one that does not, 50 seconds, then the job id.

    • Setup asks where alerts go: llmgw server init --webhook URL
      (repeatable) and a Notification webhooks field on both setup screens.
      Slack's, Mattermost's and Rocket.Chat's addresses are sent Slack's text,
      any other the event as JSON (slack: or json: in front says which).
      They are added to the cluster's notify.webhooks beside those already
      there, and each is sent a test once the control plane runs on them.

    • Extended thinking on /v1/messages: thinking: {type: "enabled"}
      tells a model that thinks by its chat template to (enable_thinking),
      and its reasoning comes back as thinking blocks before the text,
      streamed as thinking_delta events; disabled tells it not to. Not
      asked for, the reasoning is left out as before.

    • How long on-demand models hold a card is a setting, in force on the
      leader within seconds: on_demand.evict_after_idle (2m unless set) and
      on_demand.min_lifetime (5m). On one card shared by a picture model and
      a 3D model, 1m each brings the wait between a picture and its mesh to
      about a minute.

    Fixed

    • The judges and the starter model divide a machine's CPUs instead of each taking all of them. server init gave the
      judge and the starter no cores of their own, so every llama.cpp engine ran on every core: two judges and the starter
      on a 4-core control plane competed, and under load 38 of 60 requests were refused for want of a judgment. Moved to a
      node of their own with 2 cores each, the same test refused 2. When this machine is the node, init now gives each judge
      replica 1 core (2 from 8 cores) and the starter what is left (at most 8); judges bound for other nodes get 2 each. A
      judge also reserves 4 GB, not 2: it uses about 3.2.

    • A lost store machine stops costing requests most of a minute. The store's connections try each member for 3 seconds,
      not until the kernel gives up, and a connection to a machine that is gone is closed within about 10 seconds (TCP
      keepalive, and TCP_USER_TIMEOUT on Linux). Patroni replaces a lost primary within 20 seconds (ttl 20, loop_wait
      5, retry_timeout 5). Running server init --patroni again applies this to an existing cluster. Measured on three
      VMs with the primary's power cut: about 45 seconds of hanging requests before.

    • Leadership stays put while the store's primary is replaced. Replacing a lost primary takes longer than the
      leadership lease lives, so the lease had lapsed for everyone by the time the store answered again. Whoever asked first
      took it, as often a standby as the leader. A standby that saw the store out of reach now leaves the lease alone for one
      round after it returns, so the leader renews it, and a leader that is truly gone is replaced a round later. With that,
      the default lease is 10 seconds (was 15). A node notices a control plane that died without closing its connection in
      8 seconds (was 15), so it is on its way to the new leader as that one takes over.

    • A judge replica that hangs no longer costs the request. All the tries of a judge request shared the policy's
      budget, so a replica that hung used it all and the try on another replica had none left: under fail: closed the
      request was refused while a judge stood idle. When the first replica has not answered by half the budget, the same
      question now goes to a replica on another machine too, and the first answer is taken. The first replica is not cut
      short, which on a slow machine would refuse answers it was about to give. Replicas on one machine whose CPUs are taken
      are slow together, so asking both only doubles the work there: measured that way, as many requests failed as before.
      On such a machine the remedy is room, not a second question: judges on more than one node, more cores, or the judge
      on a GPU.

    • A new leader waits for the judge instead of refusing at once. The moment a control plane takes the lead it knows no
      judge yet. When every judge fails at once, they are asked again every 250 ms while the policy's latency_budget lasts,
      so fail: closed no longer answers every request in those seconds with 503 (about 17 seconds of them in the test). A
      judge that is slow is not asked again, and an alarm is raised once per request, not once per try.

    • Node discovery for Prometheus listed only the control plane asked and the
      store, so a machine that is both was a control plane to one and a store
      member to the other — scraped twice — and a standby asked alone lost every
      node. Every control plane lists all of them alike; deploy/monitoring's
      prometheus.yml says to scrape and ask each.

    • A model whose weights and context pool did not fit its card was added
      and then failed to make its context on every start, taking the card from
      others each time (Apertus 70B with a 65536-token pool, 8B with 131072, on
      24 GB). ctl model serve shrinks the pool to what the card holds, saying
      so, and refuses weights that do not fit at all; the portal's serve form
      refuses such a pool and says the largest that fits.

    • Removing weights from the mirror that a catalog model still loads is
      refused, naming the model: removed from the mirror alone, the model stayed
      in the catalog and its replicas went on loading the node's copy.

    • Jobs ran one at a time, so a 3D job waiting for the card held up another
      model's job that could have run. They run side by side, one per model at
      a time and eight in all, on the leader as before.

    • llmgw ctl model serve served an embedding model as a chat model: no
      --embedding, four slots, a 512-token batch, embeddings unset and a
      "say hello" smoke test it could not pass. A model whose GGUF says it
      pools (or --embeddings) is served for embedding: 32 slots of 1024
      tokens unless sized otherwise, a batch that holds a whole passage, and
      one vector asked for as its smoke test.

    • llmgw ctl upgrade checks every node's version once the waves are done
      and fails naming any not on the release, and says when the control plane
      has no nodes registered; llmgw upgrade on a control plane says how many
      nodes it left behind and the command that upgrades them. A site's nodes
      stayed a release behind with nothing to say so.

    • The cluster and node pages swapped their live part every few seconds,
      forms and all, so filling one in raced the clock: what was typed, an
      open choice and the focus were lost. The refresh now waits while a field
      in it has the focus or a form in it has been typed into and not sent;
      the mirror page's reload during a download waits the same way.

    Downloads
  • v0.14.5
    Release / build (push) Successful in 5m42s
    CI / test (push) Successful in 11m27s
    Release / notes (push) Successful in 4s
    Stable

    gitea-actions released this 2026-10-14 01:34:00 +02:00 | 189 commits to main since this release

    Picture and 3D models that add themselves where a card fits them, with a
    card on the portal and a screen in the console to see and add them; a
    mesh's colour brought to the picture it was made from; and the whole
    licence written into every mesh.

    Upgrading.

    • The store gains a table (migration 0047: preset_state); nothing to do.
    • presets.auto is on: once upgraded, the leading control plane adds
      flux-schnell and triposr, on demand, where a node's card fits one, the
      cluster installs ComfyUI (engines.comfyui) and the catalog makes
      nothing of that kind. Nothing is fetched until somebody asks for one.
      Turn it off with llmgw ctl model presets auto off.
    • Nodes need this release for albedo_match (it is in the save node
      the gate puts in each replica); a ComfyUI replica takes it when it
      starts again.

    Added

    • A mesh's base colour brought to its picture's: LLMGWSaveMesh takes
      albedo_match with the picture and its mask, and scales the texture in
      linear light to the object's brightness in the picture. Stable Fast 3D
      takes the light out of what it sees and read a dark basalt (0.17) as a
      light stone dimly lit (0.67); matched, it is 0.17. On sf3d it is the
      albedo_match input (off unless asked).
    • Picture and 3D models by themselves: once a node has a card that fits
      flux-schnell or triposr and the catalog makes nothing of that kind, the
      leading control plane adds the preset on demand — so a new cluster's
      portal has its Images and 3D pages without a definition written — where
      the cluster installs ComfyUI. presets.auto turns it off; a preset
      somebody removes is not added back. GET /api/v1/presets says how each
      stands and why one is not added; POST /api/v1/presets/{name} adds one
      (and sets engines.comfyui if the cluster has none); PUT /api/v1/presets/auto. Each automatic addition is in the audit log.
    • Presets on the portal and in the console: the cluster page's
      Pictures and 3D models card lists each preset — what it makes, the
      classes it fits here, whether and by whom it was added, why the leader
      has not — with an Add button that says first when it would install
      ComfyUI and restart the control planes, and the switch for presets.auto.
      The console's models screen shows the same; A adds one, P flips
      the switch. ctl model add PRESET and ctl model presets go through the
      API (and ctl model presets auto on|off), falling back to the entry as
      v0.14.4 sent it on an older control plane.

    Fixed

    • A mesh carried only its licence's name, URL and notice; it carries the
      whole licence as /v1/models gives it — id, commercial use, excluded
      regions, revenue cap, attribution — for a pipeline that sorts assets by
      licence.
    • A workflow's {{name|false}} default filled a BOOLEAN input with the
      word rather than a boolean.
    Downloads
  • v0.14.4
    Release / build (push) Successful in 2m4s
    Release / notes (push) Successful in 4s
    CI / test (push) Successful in 9m28s
    Stable

    gitea-actions released this 2026-10-13 10:37:18 +02:00 | 194 commits to main since this release

    Any ComfyUI workflow as a job, with typed inputs; picture and 3D models
    added by name; profiles that fit whatever card a site has, and ComfyUI on
    a V100; licences a machine can match and a person can leave out; a job
    waiting for the one card says what holds it. Many fixes to on-demand
    models taking turns on one card.

    Upgrading.

    • The store gains a table (migration 0046: user_excluded_licences);
      nothing to do.
    • Nodes need this release for ComfyUI on Volta or Pascal cards (the
      cu126 build), for a driver from NVIDIA's open kernel module to be read,
      and for a node that loads its weights by repository name (sf3d); a
      ComfyUI already installed is kept.
    • Control planes before nodes: an entry using the built-in triposr
      workflow, or a gpu: need instead of a class, is refused by an older
      control plane.
    • Models added with llmgw ctl model add <preset> take the preset's name;
      remove an entry of that name first.

    Added

    • The client API described: OpenAPI 3.1 at /v1/openapi.json, no key
      needed, generated from a route table a test holds to the router's own;
      and /docs/api, Swagger UI (served from the gateway) over it and, for
      staff, over the admin API's document.
    • A job waiting for the card says so: progress is
      {"state":"waiting_gpu","held_by":…,"eta_seconds":…} while another
      on-demand model holds it; a direct request's 503 carries
      X-Llmgw-Waiting-For and X-Llmgw-Eta-Seconds.
    • Any ComfyUI workflow, as a job: POST /v1/workflows/runs with
      {model, inputs}. A model's catalog entry declares its inputs
      (comfyui.inputs: text, int, float, bool, choice, image, mask, audio,
      video, file, with bounds and defaults) and its output (comfyui.output:
      pictures, meshes, video, audio or files); a request is checked against
      them before a job is written, file inputs are base64, a data URL or a
      /v1/files id, and /v1/models lists each model's workflow so a
      client can build its form. A PNG result carries the licence in an
      iTXt chunk, a GLB in asset.extras as before.
    • Environments: comfyui.environment names ComfyUI models that agree
      on their custom nodes and may share one replica; the catalog refuses
      one that cannot.
    • Profiles that fit the hardware: a profile may say gpu: {min_compute_capability, min_memory_gb} instead of gpu_class, and
      stands for every class in the cluster that meets it, as
      <profile>@<class>; a ComfyUI profile may carry its own comfyui
      workflow, checkpoint and nodes, and any profile its own source. One
      catalog entry then runs fp8 where the card has it and fp16 on Turing.
    • Custom nodes that compile: nodes[].build names directories of a
      node that are C++/CUDA packages; the gate builds each as a wheel for the
      card the replica runs on, keeps it on the node, and installs it beside
      the replica. comfyui.huggingface_repo shows a model's weights to a node
      that loads them by repository name.
    • A licence a machine can match: license.id, commercial,
      excluded_regions, revenue_cap_usd, attribution.
    • On-demand models in /v1/models, with loaded saying whether one is
      up now; a picture is cut out and put on grey before TripoSR sees it
      (LLMGWPrepareForMesh, rembg when it has no alpha).
    • Licences a person leaves out: on the profile page, by licence, or by
      rule — every licence forbidding commercial use, every licence granting
      nothing in a region — so a model under one is neither listed to nor
      served for that person's keys and sessions (403 licence_excluded).
    • Presets: a picture or 3D model added by name. llmgw ctl model add flux-schnell or triposr adds an entry that comes with llmgw — its
      workflow (TripoSR's now built in), custom nodes, packages and licence,
      and a profile by card need rather than class — and has the nodes install
      ComfyUI when the cluster's settings name none. llmgw ctl model presets
      lists them; the console's A on the models screen offers them beside
      a definition file.

    Fixed

    • A model waiting for another on-demand model to give up the card failed at
      once as "cannot be placed"; it now waits, and a job with it.
    • A model evicted for being idle was placed again on the next pass, ahead of
      the one it made room for.
    • A job or batch leased by a standby control plane, whose router has no
      endpoints, waited on "loading" for good; they are served only by the
      leader. A job told its model fits nowhere asks again for five minutes
      before failing, which an agent re-registering makes true for seconds.
    • An on-demand replica adopted after a restart was never idle enough to
      evict, so a model waiting for its card waited out its whole idle timeout;
      it counts as used at adoption.
    • A picture in a prompt was counted as its base64 by the context check, so
      one 1280x720 PNG was "138 thousand tokens" and a vision model refused it;
      a picture or a sound now counts as about a thousand.
    • An on-demand model of a higher class was evicted, idle, for a lower-class
      one two minutes into a lull; it keeps the card through its idle timeout,
      and a model waiting on it is told so.
    • A model removed from the catalog stayed wanted, so added again it loaded
      unasked.
    • A node that loads its weights by repository name (sf3d) went to Hugging
      Face for a gated repository and got a 401: the agent did not hand the
      gate comfyui.huggingface_repo, so the weights on the node were never
      linked into its cache.
    • ComfyUI on a V100. torch's CUDA 12.8 and newer builds have no Volta
      kernels, but its cu129 build was taken to run from compute capability
      7.0, so on a V100 both ComfyUI builds were installed, about 10 GB each,
      and failed with "no kernel image". Cards older than Turing now get
      ComfyUI on PyTorch's CUDA 12.6 torch (build cu126); a Turing or newer
      node keeps the build it has.
    • The open NVIDIA driver read by its version. With the open kernel
      module, the default since 560, the agent read the driver as "for" and so
      as unknown, and tried every engine build whatever driver it needs.
    Downloads
  • v0.14.3
    Release / notes (push) Successful in 5s
    CI / test (push) Failing after 6m50s
    Release / build (push) Successful in 2m6s
    Stable

    gitea-actions released this 2026-10-10 19:22:58 +02:00 | 221 commits to main since this release

    3D models from a picture, as jobs: made by ComfyUI on the cluster's cards,
    with their progress, their licence written into the mesh, a price per mesh
    and a page to make them. Also: a profile's own weights, a band for bulk
    work, and fixes for control planes behind a balancer and agents that
    restart.

    Upgrading.

    • The store gains tables and columns (migrations 0043–0045: notify_sent,
      jobs, usage meshes); nothing to do.
    • Nodes need this release for their engines' output to survive an
      agent restart and for ComfyUI's custom nodes and progress; engines
      started by an older agent keep its pipe until they are restarted.
    • A 3D model needs a model that makes them (see install.md, "A model that
      makes them"); none is added.

    Added

    • Jobs: 3D models asked after, not waited for. POST /v1/3d/generations answers 202 with a job id; GET /v1/3d/generations/{id} says its status, progress and, when done, the
      file its result is in (/v1/files/{id}/content); POST .../cancel stops
      it, in the engine's queue too. The request is replayed by whichever
      gateway holds the job's lease through the same path a client's request
      takes — key, allowlist, limits, admission, guardrails, accounting — so
      it is charged like any request, tagged job=<id>, and a model still
      loading is waited out for as long as the job has (24 h) rather than
      refused. A job survives the gateway that took it; results are kept for
      router.batch.retain_days. Migration 0044.
    • ComfyUI models that make meshes (comfyui.output: meshes). The
      picture is uploaded to the replica's ComfyUI and the workflow's
      {{image}}; the mesh the workflow saves is the job's file, an OBJ turned
      into a GLB. TripoSR (MIT) is the tested model; on Ampere and newer cards
      stronger ones can be added the same way.
    • Custom nodes (comfyui.nodes): a repository at a full commit, put in
      each replica's own custom_nodes by the gate, with its packages
      installed beside the replica, never into ComfyUI's environment.
    • A job's progress: the gate listens to ComfyUI's websocket and the job
      says queued and how many ahead, then the node running, its step and
      percent.
    • A model's license (name, url, notice), in /v1/models, in the job,
      on the portal and in the GLB's asset.extras.
    • per_mesh in auth.pricing; usage rows, sums and exports count
      meshes.
    • /3d on the portal, beside /images: a picture chosen, the job
      followed, the mesh in a viewer with a download and its licence; and
      llmgw ctl 3d list|get|cancel for every owner's jobs.
    • A profile's own source: the weights it loads instead of the
      model's, so an embedding model can run on vLLM from its repository on a
      card and on llama.cpp from a GGUF on CPUs as one model. Placement prefers
      nodes holding each profile's weights, ctl model add checks every
      source, and ctl weights collects none a profile loads.
    • A band's own queue_ttl (router.admission.bands[].queue_ttl): how
      long its requests may wait for room, instead of the admission's 30 s. A
      band for bulk work — a knowledge-base import — waits behind people rather
      than being turned away.

    Fixed

    • An engine's output no longer breaks when its agent restarts. Engines
      wrote to a pipe of the agent's, which an agent that restarted and adopted
      them left with no reader; an engine's next large write failed, and in a
      Python engine took the request it was reporting on with it. Each process
      now writes to its own file in the agent's work directory, and an adopted
      engine's log keeps what it wrote before the restart.
    • Single sign-on behind a balancer. The cookie that carries a sign-in
      across the identity provider was signed with a key of each process's own,
      so with two control planes a callback that reached the other one was
      refused. The key is now a secret in the store, the same on every control
      plane.
    • Notifications sent once by two control planes. Each kept its own
      hour of quiet per event, so a budget spent on a team both served was
      announced twice. The control plane that claims an event in the store
      (migration 0043) sends it; with the store unreachable it is sent anyway.
    • A request turned away for a busy cluster says when to come back. A
      request that waited as long as it may (504) or found the queue full (503)
      had no Retry-After, so llmgw-kb counted the passage as failed instead
      of sending it again; both now say 10 seconds.
    • The usage sums and exports count pictures (images), as the request
      records did; pictures made before are added to the sums.
    Downloads
  • v0.14.2
    Release / notes (push) Successful in 4s
    Release / build (push) Successful in 2m3s
    CI / test (push) Successful in 8m44s
    Stable

    gitea-actions released this 2026-10-10 02:12:09 +02:00 | 239 commits to main since this release

    Pictures: OpenAI's /v1/images/generations, made by ComfyUI on the
    cluster's own cards. The agents install ComfyUI themselves, the gateway
    prices pictures by the picture, and the portal has a page to make them.
    Also: on-demand models that survive a failover, and knowledge-base
    embeddings fixes.

    Upgrading.

    • The store gains a column (usage.images); nothing to do.
    • ComfyUI is installed only where asked: llmgw ctl config set engines.comfyui 0.39.2 has every node with an NVIDIA GPU build it (about
      10 GB under /opt/llmgw/engines). Nodes need this release to install it
      or run engine comfyui.

    Added

    • Pictures at /v1/images/generations, OpenAI's shape, through the same
      key, allowlist, limit, admission and guardrail checks as any request. A
      model with profiles on engine comfyui is placed on a GPU node like any
      other. A ComfyUI server outside the cluster can be an external model with
      api: comfyui instead. The workflow is a built-in (flux-schnell, the
      default, or flux-dev) or a JSON file in ComfyUI's API format, with
      {{prompt}}, {{width}}, {{seed}} and the like where the request's
      values go. Requests may also send seed, steps, negative_prompt and
      guidance. The seed used comes back in x-llmgw-seed. Pictures come back
      as b64_json; ComfyUI's workflow is stripped from each PNG, and a client
      that leaves takes its workflow off the queue. /v1/models says images
      for such a model.
    • Engine comfyui. ComfyUI asks for no key, so the agent starts it
      behind llmgw comfyui-gate. ComfyUI listens on the loopback only. The
      gate listens on the replica's port, demands the replica's key, and lets
      through only what the router asks. The model's comfyui: block names
      the workflow and, for a source that is a set of files, the checkpoint.
    • The agents install ComfyUI (engines.comfyui). The release's source is
      checked against a digest llmgw holds, since GitHub publishes none, and a
      version without one is refused. Its requirements are resolved as PyPI
      stood the day after the release, with PyPI's CUDA 13 torch or PyTorch's
      CUDA 12.9 one for an older driver, and the result is checked on the card.
      A ComfyUI installed by hand is named by engines.comfyui.dir in
      agent.yaml.
    • A price per picture: per_image in auth.pricing, on top of the
      prompt's tokens. A model priced only per picture is not free, so a spent
      budget still stops it. Each usage row keeps how many pictures were made,
      shown by llmgw ctl request.
    • /images on the portal, beside the chat, for the models that make
      pictures. The chat no longer offers them. The page offers size, how many,
      seed, steps and a negative prompt, and shows each picture with its seed
      and a download link. Nothing is kept but the usage.
    • An embedding model's query_prefix, put before the question a
      knowledge-base search embeds and never before documents, for Qwen3-Embedding
      and models trained like it.

    Fixed

    • An awake on-demand model survived no restart or change of leader of the
      control plane.
      A control plane that had heard no requests read every
      on-demand replica its nodes reported as wanted by nobody, and drained them
      all. An adopted replica now gets a whole idle timeout.
    • ctl models said not placed for an on-demand model that had been woken
      and was serving; it now says ready (on demand).
    • A batch of embeddings was refused as one over-long prompt when the
      passages together exceeded the context, though each one fit. The longest
      input is what has to fit.
    • The chat's kept conversation sat in the browser under one key for
      everybody, so the next person to sign in on the same browser opened the
      last one's conversation. It is kept per account, and signing out clears
      what the pages stored.
    Downloads
  • v0.14.1
    Release / build (push) Successful in 1m59s
    Release / notes (push) Successful in 5s
    CI / test (push) Successful in 8m37s
    Stable

    gitea-actions released this 2026-10-09 10:11:23 +02:00 | 259 commits to main since this release

    Adapters trained on the nodes from a knowledge base and kept private to
    their owners; the cluster's and the agents' settings edited in one place;
    the tenancy tree and keys over the admin API and in Terraform; knowledge
    bases that hold up with many documents; the firewall setting on every
    machine.

    Upgrading.

    • Nodes need this release to follow agents.* in the cluster's
      settings; until a node is upgraded its agent.yaml is what it runs on.
    • A separate mirror machine and store members that run nothing else
      follow the firewall setting once what reads it is installed: run sudo llmgw mirror init on the mirror again with the flags it was set up with,
      and llmgw server init --patroni again for the store's members. A control
      plane set up before v0.12.0 needs sudo llmgw firewall sync --install.
    • The store's PostgreSQL memory applies after each member is restarted,
      one at a time, the leader last; until then it runs as before.
    • The store gains tables and columns; nothing to do. Existing knowledge-base
      collections get their full-text index when a control plane starts, and
      keep vector storage; only new collections use halfvec, and only on
      pgvector 0.7 or later.
    • Cached knowledge-base passages in vLLM are made again once, under the new
      cache_salt.

    Added

    • The cluster's settings on the portal (BACKLOG E4): /admin/settings
      for administrators. It edits the document llmgw ctl config edit opens:
      checked as a control plane would load it, refused when made from a
      version since replaced, with the history to view and roll back. Changes
      are audited by size, never by contents.
    • The nodes' agent settings in the cluster's settings (BACKLOG E3):
      agents.default and agents.nodes.<id> hold everything agent.yaml can
      say but the agent's id, control planes, address and certificate. They are
      sent to the agents as they change; an agent starts again on new settings,
      or on an edited agent.yaml, with its engines serving throughout. Settings
      that would not load are refused on save, and set aside on a node rather
      than keep its agent from starting. Nodes need this release to follow
      them.
    • LoRA adapters trained on the nodes (BACKLOG F4): llmgw ctl kb dataset
      writes question/answer pairs from a collection with a model of the
      cluster. llmgw ctl train start trains an adapter on a node with its
      installed vLLM's torch and a small overlay, on a card with enough memory
      free (beside an engine, if it leaves that much), converting to GGUF for
      llama.cpp. llmgw ctl train publish keeps it on the mirror (which now
      accepts uploads) and makes it the model's adapter.
    • Adapters with owners. An adapter's owner (org, team, project, user
      or key) is the only part of the tree that sees it; to anyone else it is a
      model that does not exist. An adapter trained on a knowledge base belongs
      to a part of the tree that may read it, and is never made public.
    • The knowledge bases' vLLM cache_salt follows the collections searched,
      not the organization, so two teams of one organization no longer share
      cached passages.
    • Organizations, teams, projects, API keys and memberships in the admin
      API
      : /api/v1/orgs (the tree), POST /api/v1/teams and projects,
      PATCH to rename or move, DELETE for what is empty; /api/v1/keys to
      issue (the key is in the answer, once), list and revoke; /api/v1/users
      and /api/v1/users/{id}/memberships. They were only llmgw org, keys
      and users on a control plane before. The Terraform provider manages them
      as llmgw_org, llmgw_team, llmgw_project, llmgw_key (the key in a
      sensitive secret) and llmgw_membership.
    • The firewall setting reaches every machine. A separate mirror and the
      store's members follow firewall.enabled and firewall_networks as nodes
      and control planes did: the leading control plane tells the mirror over
      its API, and keeps the state in the store for members that run no llmgw,
      which a timer on each reads. The mirror also lets in the network of every
      node the leader knows. mirror init and server init --patroni install
      what follows it; run them again on a cluster set up before.

    Changed

    • The store's PostgreSQL is sized to its machine's memory. Before, it ran
      on PostgreSQL's defaults (128MB cache) under Patroni, or on 96MB on one
      machine. Now each member gets a share of its own RAM, a smaller one where
      a control plane or agent shares the machine, and the share is read again
      every time server init --patroni runs. The new cache is applied by
      restarting one member at a time, the leader last.
    • Knowledge-base searches hold up with many documents. New collections
      store their index as halfvec where pgvector is 0.7 or later (half the
      memory, and embeddings up to 4000 dimensions instead of 2000); older
      collections, and clusters on pgvector 0.6, keep vector. Each collection
      has its own full-text index, as it has its own vector index, made for
      existing collections when the control plane starts. The text arm ranks by
      the cheap measure first and scores only those candidates.

    Fixed

    • A knowledge-base search asking for more than 10 passages
      (X-Llmgw-Knowledge-K) got at most 40 candidates per collection from the
      vector index, whatever it asked for: pgvector's hnsw.ef_search is now
      raised to the number wanted, up to 1000.
    • llmgw server backup and restore against a replicated store over TLS
      failed looking for ~/.postgresql/root.crt: pg_dump was given
      sslmode=verify-full without the cluster's authority.
    Downloads
  • v0.14.0
    Release / build (push) Successful in 6m21s
    Release / notes (push) Successful in 3s
    CI / test (push) Successful in 9m34s
    Stable

    gitea-actions released this 2026-10-08 16:22:07 +02:00 | 276 commits to main since this release

    Knowledge bases: a company's documents, put in by people from their own
    machines, kept apart per tenant, and answered from.

    Upgrading.

    • pgvector on a cluster set up before this release. Knowledge bases
      need pgvector, which server init and the high-availability database
      setup now install, but a rerun on a running cluster does not:
      1. Install postgresql-<version>-pgvector on every database machine.
      2. As postgres, run CREATE EXTENSION vector in llmgw's database.
      3. Restart the control planes.
        Until then everything else works, and llmgw ctl kb list says why
        knowledge bases are off. On a SQLite store they stay off.
    • An embedding model. A collection needs a catalog model with
      capabilities.embeddings, for example bge-m3, whose weights are in the
      mirror.
    • The client. People download llmgw-kb for their system from this
      release.

    Added

    • Collections and who may read them. A collection is owned by an
      organization, team, project or person. Grants add up down the tenancy
      tree and never cross an organization. A collection a key may not read is
      answered as one that does not exist. Chunks are kept in PostgreSQL with
      pgvector, partitioned by organization, with an HNSW index per collection,
      and row-level security on the table as a second line behind every
      query's own filter. A project, team or organization that owns collections
      is not deleted; a person's collections go with their account.
    • llmgw-kb, its own download for Windows, macOS and Linux, run by
      people with their own key. It reads documents where they are and sends
      text and vectors, never the files, for what changed only; a SQLite ledger
      on the machine remembers what it did.
      • It reads PDF, Word, Excel, PowerPoint, OpenDocument, RTF, EPUB, HTML,
        MHTML, mail with attachments, notebooks, subtitles, ZIP archives, and
        text and source code in any common encoding.
      • Legacy Office files are read with LibreOffice, and scanned pages with
        Tesseract, when those are installed.
      • --dry-run says what would leave the machine; --delete removes only
        what that machine sent.
    • Answers from documents. A chat request names collections in
      X-Llmgw-Knowledge, or its part of the tree has a default (llmgw ctl kb default). The best passages, chosen by meaning plus a little for the
      question's words, are added to the system message, numbered, and
      x-llmgw-sources says which were used. This happens before the
      guardrails judge the request, and the answer is never served from the
      answer cache. On vLLM replicas the organization's cache_salt keeps
      tenants' prefix caches apart. It works through the Responses and Messages
      APIs too.
    • Administration: llmgw ctl kb (create, list, grant, revoke, default,
      docs, rm, search, delete), /api/v1/kb, the console's Knowledge screen,
      and a Documents picker on the portal's chat page.
    • Metrics llmgw_kb_retrievals_total and llmgw_kb_retrieval_seconds;
      docs/knowledge.md.
    Downloads
  • v0.13.0
    CI / test (push) Failing after 4m47s
    Release / build (push) Successful in 4m54s
    Release / notes (push) Successful in 3s
    Stable

    gitea-actions released this 2026-10-08 11:55:34 +02:00 | 295 commits to main since this release

    The whole system stopped, started and restarted from one command, a
    guardrails judge that no longer refuses ordinary coding requests, and the
    rest of the field report from v0.10.6.

    Upgrading. A cluster keeps the judge its policies name: one installed
    before this release goes on asking Llama Guard 3 1B until llmgw ctl guard judge use qwen3guard-0.6b, which the leader then fetches and serves by
    itself, and which replaces the policies' Llama Guard category codes.

    Added

    • A judge on a GPU and one on CPUs together: a policy's judge.models
      is a list asked in turn, the first that answers giving the verdict; the
      next is asked when one has no replica serving, fails, or misses its
      latency_budget. llmgw ctl guard judge use qwen3guard-4b qwen3guard-0.6b sets the order for every policy with a judge, and
      guard judge status says which judge stands in for which. A judge on a
      GPU is served as a standard model, so it takes a card only from what
      may give one up.
    • A judge a policy names is fetched if it is missing: the leader
      fetches the weights of a judge llmgw knows (qwen3guard-0.6b, -4b,
      -8b, llama-guard-3-1b, -8b) into the mirror and serves it, when a
      policy names it and the catalog does not have it — and again if it is
      removed. guard judge enable --gpu serves Qwen3Guard-Gen 4B on the class
      with the most free cards.
    • The whole system stops, starts and restarts from one command: llmgw stop takes every model down and answers every request for one with 503
      (system_stopped), keeping the control planes, agents and mirror running
      and no model's definition changed; llmgw start brings every model back.
      It is the cluster's setting serving.stopped, live on every control plane.
      llmgw restart restarts every node's agent and every control plane, the
      leader last, with the engines serving throughout. llmgw status says
      whether the system serves. One machine's services are llmgw service status|start|stop|restart.
    • A replica says what it computes on: ctl replicas (ON), the console
      and the admin API show CUDA with the GPUs its processes hold memory on
      and how much, as nvidia-smi sees them, or CPU — for every engine, kept by
      the agent across its own and the control plane's restarts. llama.cpp's
      layer count is added when it says it, which it does only at -lv 4.
    • ctl engine-check llamacpp computes a token on the GPU: a small GGUF
      (--model, or the smallest in the weights cache) is loaded with every layer
      on the GPU at -lv 4 and asked for one token; a build whose CUDA backend
      does not load fails, by llama.cpp's own account of its layers. With no
      --bin it checks the build the agent installed.
    • Engine versions and compute capability on nodes: ctl nodes shows
      llamacpp b10991 and 6000-24g (7.5); the admin API gives
      engine_versions and compute_capabilities. A profile's engine_version
      restricts it to nodes whose engine version starts with it, and
      explain-placement says which version a node has, or which engines.
    • ctl restart --max-surge N and --max-unavailable N: that many of a
      model's replicas restart at once, beside themselves or stopped first.
    • Multi-node readiness apart from the node's: doctor gives both, with
      fabric findings in a section of their own; node add says the multi-node
      one; ctl nodes shows a declared fabric's adapters that are down.
    • A forgotten password is set through a link: /forgot mails one when
      the cluster's mail settings say how (SMTP, sender, the public
      base_url it points at), saying the same whether or not the address has an
      account; /reset sets the password once and closes every session. Without
      mail, llmgw users reset-link makes the link for an operator to hand over.

    Fixed

    • A replica restarted in place keeps its room: the replica was stopped
      and the pass went on to other models before placing its replacement, so a
      model waiting for room took the freed cards. On the reference cluster a
      GPU judge waiting for the only card took it from the model being
      restarted there. The replacement is now placed in the same step.
    • The judge no longer refuses ordinary coding requests: Llama Guard 3
      1B, the judge server init installed, could not tell "continue", "devam
      et" or "how do I kill all child processes in bash" (0.51 to 0.92 unsafe)
      from harmful requests (0.97 to 1.00), at any quantisation. The judge is
      now Qwen3Guard-Gen 0.6B (format qwen3guard), which gave the same
      ordinary requests 0.15 at the most and the harmful ones 1.00, on CPUs,
      in 0.8 GB. Its third verdict, controversial, counts as safe unless the
      policy says controversial: unsafe. guard judge use qwen3guard-0.6b
      moves an installed cluster to it, and replaces Llama Guard's category
      codes, which Qwen3Guard never names, with the baseline's.
    • A long message is judged rather than refused: a judge read the whole
      last user message, and one with a file pasted into it overflowed the
      judge's context, so it gave no verdict and fail: closed refused the
      request. It now reads the start and the end, about 6000 characters.
    • A replica no longer stays "starting" while it serves after a restart:
      an agent that registered before its adopted engines had answered their
      health check never said they had, so the control plane kept them starting
      and routed nothing to them — on the reference cluster, after llmgw restart, the only replica of a model. The agent now reports each shard
      the registration called not ready once it is.
    • A failover no longer replaces healthy replicas: a standby that took
      over placed at once, before the agents had reconnected and said what runs,
      so it started a second copy of replicas it did not know of and then drained
      the healthy ones as surplus — on the reference cluster the guardrails'
      judge and a model's replica, within twenty seconds of a leader restarting.
      Placement now waits after a takeover as it does at start.

    Changed

    • A GPU placement llama.cpp says it cannot compute on is refused when it
      warns that it found no usable GPU, even where nvidia-smi cannot be asked;
      where it can, holding no memory on its GPUs refuses it as before.
    Downloads
  • v0.12.2
    Release / build (push) Successful in 4m41s
    CI / test (push) Successful in 4m43s
    Release / notes (push) Successful in 3s
    Stable

    gitea-actions released this 2026-10-06 13:35:16 +02:00 | 316 commits to main since this release

    The guardrails turned off and on as a whole, and a judge that refuses
    ordinary requests explained: the hazard it saw is told to the client and
    kept with the request, and llmgw ctl guard judge ask shows what it says of
    any text. Nothing to do to upgrade; the store gains a column.

    Added

    • The guardrails turn off and on as a whole: llmgw ctl guard off /
      on, which is guard.enabled in the cluster's settings — in force within
      seconds, the policies and their attachments kept. ctl status, ctl guard list and the guardrails page say when they are off.
    • llmgw ctl guard judge ask TEXT has the judge read a text and shows
      its answer as given and as read — for finding out why ordinary requests
      are refused. Nothing of the text is kept, not even in the audit.

    Changed

    • A judge's refusal names the hazard: the client is told (judge: S6,Specialized Advice) rather than (judge), and the verdict is kept with
      the request (guard_rule): ctl request, the server's log and the
      guardrails page show it. Llama Guard 3's codes come with their names,
      S9 Indiscriminate Weapons, without a categories list.
    Downloads