• v0.16.3
    Release / build (push) Successful in 5m11s
    Release / notes (push) Successful in 4s
    CI / test (push) Successful in 10m10s
    Stable

    gitea-actions released this 2026-11-03 09:56:29 +01:00 | 41 commits to main since this release

    Replicas counted per profile — seven on cards and four on CPUs under one name, the cards filled first — and every
    setting of a served model from the mirror page; how fast a model answers on the CPU, said before it is served, by
    the threads it is given rather than the whole machine's memory.

    Upgrading.

    • No migrations. llmgw ctl upgrade v0.16.3: the control planes carry the replicas per profile, the model card and
      the speed in the serve form and in ctl model plan; the agents carry the measurement of one core's memory rate,
      without which the speed is reckoned the old way, from the whole machine's.

    Added

    • Replicas counted per profile: seven on cards and four on CPUs, under one
      name.
      llmgw ctl model replicas <model> gpu=7 cpu=4, the console's
      Replicas per profile… and the mirror page's model card set how many of a
      model's replicas run on each of its profiles, rather than every replica
      on the best profile that fits. For a GGUF model with no profile on CPUs
      one is added (--cpu-memory), preferred below the cards, so the router
      fills the cards first and sends to the CPUs once they are full. A change
      starts the replicas short before stopping those over: the model never
      runs fewer. ctl models shows the counts; a scaling of another total
      ends them.
    • Every setting of a served model from the mirror page. A served
      model's row has a Settings link, and its card now changes the context
      and longest answer, the cards a replica takes, the replicas on each
      profile (with the CPU's speed beside), stops and starts it, restarts its
      replicas, and takes it out of the catalog with its files or without —
      beside the replicas, class, owner, memory and adapters it held before.
      Stopped on the row is now the model stopped and kept, started again as
      it was, rather than its scaling set to nothing.
    • How fast a model answers on the CPU, before it is served. The serve
      form says, as the cores are changed, about how many tokens a second one
      reply gets on the CPU and on one card of the fleet's fastest class, and
      warns when it is slow (under 15) or very slow (under 5, a reply of 500
      tokens taking minutes). Very slow on the CPU is refused until "run on the
      CPU anyway" is ticked, GPUs free or not. llmgw ctl model plan and the
      console's Hugging Face screen show the same figure for every way a model
      can run, and ctl model serve --cpu says it in its log.
    • The speed follows the threads a replica is given. Each agent now
      measures what one core reads beside what all of them read: a replica of
      four threads on a large server reads at about four cores' rate, not the
      server's, which the estimate used to assume and overstated several times.
      Calibrated on the PoC by llama-bench at 1, 2, 4 and 8 threads (within 15%),
      and a model of experts reads its experts' share, from the GGUF header.
      Agents need upgrading to report one core's rate; until then the old
      estimate is used.
    Downloads
  • v0.16.2
    Release / build (push) Successful in 5m43s
    Release / notes (push) Successful in 4s
    CI / test (push) Successful in 11m11s
    Stable

    gitea-actions released this 2026-11-02 08:42:29 +01:00 | 45 commits to main since this release

    Standby control planes that answer clients themselves, with rate limits and budgets kept across them; FreeToken, an
    engine for models larger than the card; a served model's longest answer changed in place; and fixes found putting
    the router under load and fetching large models: a multi-file model fetched once per node however many replicas
    start there, downloads that carry on where they were cut off, and a router that does twice the work on two cores.

    Upgrading.

    • No migrations. llmgw ctl upgrade v0.16.2: the control planes carry active-active routing (off until
      llmgw ctl config set router.active_active true) and the router fixes; the agents carry the set fetched once
      per node and the set's progress and checking; the mirror carries the resumed downloads.

    Added

    • Standbys that answer the client API themselves. With
      router.active_active: true in the cluster's settings, a standby control
      plane routes /v1 to the leader's replicas instead of passing every
      request to the leader, so more machines carry the traffic. It hears the
      replicas from the leader every two seconds over the agent port (no engine
      keys travel) and reports back what it used, so the leader does not evict
      a replica busy behind a standby, and the on-demand models it was asked
      for. Rate limits are the cluster's: each router's request and token
      counts reach the others through the leader every two seconds, so a key
      spread over several control planes gets its limit, not that many times
      it. Budgets are brought up to the store's every 30 seconds. Off by
      default; the portal, /admin and /mcp still go to the leader.
    • A served model's longest answer, changed in place. llmgw ctl model context <model> --max-output N (and max_output on POST /api/v1/models/{name}/context, and the console's Context length… form)
      sets the longest answer on its own: the catalog's limit, in force at once,
      with no replica started again. With a context it is set with it. Strata's
      context can now be changed too (its setup's --context), so neither
      needs the model removed and added again.
    • FreeToken, an engine for models larger than the card. Mixture-of-experts
      models several times a card's size, from one card, with the experts in RAM
      and streamed over PCIe — read from Hugging Face safetensors checkpoints as
      published (DeepSeek V4 Flash, GLM-5.x, Qwen3.6/3.8, gpt-oss, Gemma 4,
      MiniMax), with batching. Installed like vLLM on every node it runs on: a
      card of compute capability 8.0 or newer, driver 580+, x86-64, and the CUDA 13
      toolkit it compiles kernels with; older cards are passed over (tried on a
      Turing card, where 0.2.1 has no kernels). Profiles are one card
      (engine: freetoken); the key goes in its environment, and flags that read
      files or fetch are refused. Readiness now also waits for an engine whose
      /health answers 200 while saying loading in its body. FreeToken is
      followed release by release: a weekly workflow fails when PyPI has a version
      newer than DefaultEngines installs.
    • How much of each GPU works, beside whether anything does. The busy
      figure is nvidia-smi's utilization.gpu — the share of time any kernel
      ran, which a kernel on one SM fills — and it is now said to be that: on
      the dashboard, the node page, the metric's help and Grafana. Beside it,
      each card's draw against its power limit (the efficiency table, the fleet
      and node charts, the console's GPU work chart), and where the card's
      profiling counters can be read, its SMs, SM occupancy, tensor cores and
      memory interface: llmgw_gpu_sm_active_percent,
      llmgw_gpu_sm_occupancy_percent, llmgw_gpu_tensor_active_percent,
      llmgw_gpu_dram_active_percent. The agent reads them from NVML's GPM on
      Hopper and later, and from a dcgm-exporter on the node elsewhere
      (discovery.dcgm_exporter, default http://127.0.0.1:9400/metrics, off
      for none). The GPU map colours by this "work" by default — SMs active
      where read, power against the limit otherwise — and keeps busy as a mode.
      On cuda01 decode read 92% busy with the SMs at work 69% of the time.

    Fixed

    • Several replicas of a multi-file model on one node fetched it once
      each, over one another.
      Four replicas placed on a node's four cards
      wanted the same safetensors set at the same moment, each wrote the same
      .part files, and all but one failed to start ("rename … no such file")
      until a retry found the set finished. A set is now fetched once per node
      and every replica waiting on it gets it, as single files always were.
    • A model of several files showed one file's download as the whole. A node fetching a set reported the part it
      was on — "59% of 46.1 GB" for a 188 GB model — instead of the set: it now reports the set's bytes against its
      whole size.
    • A set's files checked with nothing said, one at a time. After the last byte arrived a node checked each file
      against the mirror's checksum, 195 GB on one core for eight minutes, while the replica still read "fetching 100%".
      It now checks four at once and says so: checking 40% of 195 GB in ctl replicas, "Checking the weights" in the
      chat page; and a check that takes long is not taken for a stuck download.
    • A mirror download the registry cut off started again from nothing, or not at all. Hugging Face's CDN ended a
      60 GB transfer at 23 GB (stream error … CANCEL), the mirror deleted what had arrived, and ctl model serve
      failed. The mirror now tries again up to six times, carrying on from where it got to (Range), keeps what arrived
      when it gives up, and carries on from it after a restart too.
    • A model from an outside provider took three or four requests at once. Admission read every request in flight to
      an endpoint it does not measure as a queue, so an external model (OpenAI, another llmgw cluster) was held to what a
      saturation of 0.85 allows of "in flight / 4"; the rest waited and timed out with 504. An outside provider queues for
      itself and says 429 when full, which its breaker already hears: it no longer counts towards saturation.
    • A valid key told to slow down under load, every thirty seconds. When a busy key's cached entry expired, every
      request in flight missed the cache together and each took one of the address's allowance for unknown keys: past a
      hundred or so streams at once, a valid key got 429 "too many unrecognised api keys". Requests missing the same key
      at once now share one read and one allowance.
    • A streamed answer was one write to the client a token. Each event was flushed alone, a system call each: a
      third of a busy router's CPU. Events already received from the engine now go out in one write, with no token
      waiting for another; on the PoC's two-core leader the ceiling rose from ~575 to ~700 streams' requests a second,
      and the first token at 128 at once from 91 to 45 ms.
    Downloads
  • v0.16.1
    CI / test (push) Successful in 12m33s
    Release / build (push) Successful in 6m32s
    Release / notes (push) Successful in 4s
    Stable

    gitea-actions released this 2026-10-30 13:26:47 +01:00 | 63 commits to main since this release

    The mirror page's Strata tab, Strata's ready-made packs served with Strata fetching them, and two fixes found
    serving Strata's models: repositories that keep each size in a directory, and DeepSeek on cards its engine does not
    run on.

    Upgrading.

    • No migrations. The control planes carry the tab, the plan and the serving; the agents carry the refusal of a
      Strata profile its card cannot run (llmgw ctl upgrade v0.16.1). Mirrors need nothing new.

    Added

    • The mirror page's Strata tab. Every family and size Strata runs, with its download, the RAM it keeps and where
      it would run here; a GGUF size fetched into the mirror in one press (checked against Hugging Face's checksums),
      and the packs Strata fetches itself — NVFP4, DeepSeek V4.1 — served at once, their repository the source.
      ctl model plan and ctl model serve take those repositories too, and the explorer names a strata-ple
      repository for what it is: part of a pack Strata knows, or one Strata 0.2.7 does not.

    Fixed

    • A repository that keeps each quantisation in a directory of its own (ISTA-DASLab's IQ2_XS/, IQ3_S/) listed
      no weights: its file tree is now read recursively, by the explorer, the plan and ctl model serve.
    • DeepSeek V4.1 offered on a card its engine was not built for. Strata's DeepSeek engine runs on RTX 30 (compute
      8.6) and newer; on a Turing card the tab and the plan offered it, and Strata's setup refused it once placed. Now
      neither offers it there, the tab says why, and the agent refuses such a profile before starting it.
    Downloads
  • v0.16.0
    Release / build (push) Successful in 6m6s
    Release / notes (push) Successful in 5s
    CI / test (push) Successful in 11m14s
    Stable

    gitea-actions released this 2026-10-30 11:30:03 +01:00 | 68 commits to main since this release

    Weights pinned to the commit they were added at and checked, by the nodes and the mirror, against the sha256
    Hugging Face published; the documents llmgw-kb sends embedded by the gateway; Strata offered by the plan, ctl model serve, the explorer and the serve forms; and cluster upgrades that tell the nodes together, say how far they
    are, and do not fail a node that came back on the version.

    Upgrading.

    • No migrations. Upgrade the control planes, then the nodes (llmgw ctl upgrade v0.16.0), then each mirror's host
      (sudo llmgw upgrade v0.16.0 --service llmgw-mirror).
    • A node checks what it fetches against the catalog's checksums only once it runs this version; until then it
      fetches as before.
    • A model added from Hugging Face is now pinned when it is added, which needs the registry reachable from the
      control plane; one it cannot be looked up in is refused, saying why. Models already in the catalog are left as
      they are.
    • A Strata profile must say --size as well as --family, and both must be ones Strata runs.
    • llmgw-kb sends text alone to a gateway of this version; an older llmgw-kb keeps working, sending vectors.

    Added

    • Strata offered where models are chosen. For a repository of a family Strata runs, ctl model plan offers "one
      card with Strata, experts in RAM" with the RAM it takes, ctl model serve --engine strata (or the plan's choice)
      builds its profile, the Hugging Face explorer lists it as running here, and the mirror's serve form serves such a
      version on Strata. Strata's families and sizes are known: the catalog refuses one it does not run and reserves the
      RAM a size keeps its experts in.
    • The gateway embeds the documents llmgw-kb sends. A document may be sent as its passages' text alone; the
      gateway embeds them through its own /v1/embeddings as the caller, 32 at a time and four batches at once,
      waiting out a busy engine rather than failing the document. About a kilobyte a passage leaves the machine instead
      of a vector down and up again, some thirty times more. llmgw-kb does so with a gateway that says it can
      (server_embeds in a collection's answer); --embed-here keeps the old way.
    • Weights pinned to a commit, and checked against what Hugging Face published. A model added from Hugging Face
      (hf:, a resolve URL on a branch, or a repository vLLM or SGLang downloads) is pinned to the commit it is at
      then — the file's URL at that commit, or --revision — and each file's published sha256 goes in its origin. A node
      refuses and deletes a copy that is not it, naming both checksums; the mirror does the same for what ctl model serve fetches, and llmgw mirror add for a Hugging Face URL (--sha256 for others). Nodes need this version to
      check; older ones fetch as before.

    Fixed

    • A cluster upgrade that sat silent, or stopped on a node that had upgraded.

      • The nodes of a wave are told together, each saying when it is done, and what is still awaited is said every 30
        seconds; before, each was told in turn, up to five minutes each, with nothing on the screen.
      • The control plane takes the node registering again on the version as done: the agent's answer, sent a moment
        before it exits, could be lost, and a node that had upgraded was failed as "older than v0.10.4".
      • The agent's download no longer ends with its connection (a control plane restarting, leadership moving), and the
        control plane waits longer than the download may take; a second request while one runs is refused.
      • A cordoned or draining node upgraded is done; a node whose idle model stopped meanwhile is done once nothing on it
        is starting; a node not connected is passed over and named at the end rather than stopping every node after it.
      • The console's node upgrade waits as long as the control plane, and offers no development version.
      • systemd never gives up restarting the agent (StartLimitIntervalSec=0), in units written from now on.
    • Strata. One named by hand in agent.yaml was never reported, so never placed on; its engine, which leaves the
      server's process group, is now stopped with it; Pascal and Volta, which its CUDA 12 build runs, were refused.

    • A name with a slash in it, asked through a standby control plane. llmgw ctl kb delete Demo/hr on a standby
      was answered 405: the standby passed the request on to the leader with Demo%2Fhr unescaped, two segments instead
      of one. The path now reaches the leader as it was sent.

    Downloads
  • v0.15.3
    CI / test (push) Successful in 11m44s
    Release / build (push) Successful in 5m43s
    Release / notes (push) Successful in 4s
    Stable

    gitea-actions released this 2026-10-29 21:07:44 +01:00 | 77 commits to main since this release

    A model stopped and kept, or removed with its files; the second mirror fixed where it was tried.

    Upgrading.

    • No migrations. The control planes carry stop, start and purge; the nodes need nothing new.
    • The mirrors carry the fixes: on each mirror's machine, sudo llmgw upgrade v0.15.3 --service llmgw-mirror.
      A mirror with a peer then works out the checksums its older files lack, once, which reads every such file.

    Added

    • A model stopped and kept, and a model removed with its files. llmgw ctl model stop|start MODEL: none of its
      replicas run, it stays in the catalog with its files, and start runs it again scaled as it was; requests are told an
      administrator stopped it. llmgw ctl model remove MODEL --purge (DELETE /api/v1/models/{name}?purge=true) also
      deletes every version of it on the mirror, a second mirror following, and the nodes' copies once its replicas have
      stopped; what another model uses stays. Both are in the console's model menu.

    Fixed

    • llmgw mirror add-peer on a mirror with a machine of its own. It set the second mirror up and then
      failed for want of the control plane's server.yaml; now it prints the llmgw ctl config set mirror.peers
      to run on a control plane.
    • Models fetched before checksums were kept reach the second mirror. A mirror kept in step copies only what it
      can check, and those files had no sha256; a mirror with peers now works theirs out and records them, once.
    • A file removed with llmgw mirror remove is gone from the mirror's answers too. The running mirror kept
      reporting a file it had fetched as present for a week after it was removed, so a control plane could place a
      model on a file that was not there; and the removal was not recorded, so a mirror kept in step copied it back.
    Downloads
  • v0.15.2
    Release / build (push) Successful in 4m44s
    Release / notes (push) Successful in 4s
    CI / test (push) Successful in 9m18s
    Stable

    gitea-actions released this 2026-10-29 16:58:02 +01:00 | 83 commits to main since this release

    A served model moved to more cards, or fewer, while it is served.

    Upgrading.

    • No migrations, and nothing changes until the command is used. The control
      planes carry it; the nodes need nothing new.

    Added

    • A served model moved to more cards, or fewer. llmgw ctl model gpus MODEL N [--profile P] [--in-place], POST /api/v1/models/{name}/gpus, and
      GPUs… in the console's model menu. How many cards of one node each replica
      uses: tensor parallel for vLLM, SGLang and TensorRT-LLM, checked against
      the model's KV heads, and the layers split for llama.cpp. Refused for a
      count no node has, or whose cards would not hold the model and its
      context, with the figures; its replicas are then started again on it, one
      by one, beside the old — or, with --in-place when there is no room
      beside them, each stopped first.
    Downloads
  • v0.15.1
    Release / build (push) Successful in 6m4s
    Release / notes (push) Successful in 5s
    CI / test (push) Successful in 11m42s
    Stable

    gitea-actions released this 2026-10-29 16:09:12 +01:00 | 85 commits to main since this release

    A 125B model on one card, a second mirror, and weights that run code
    refused. Strata runs as an engine: a mixture-of-experts model with its
    experts in RAM, served from one 24 GB card. A second model mirror keeps in
    step with the first, so an HA install has no single machine left. Pickled
    weights are refused unless a model says otherwise. A served model's context
    can be changed in place. The explorer says how fast a model would answer
    here before it is fetched, and a card in the wrong PCIe slot is said so.

    Upgrading.

    • No migrations. Upgrade the control planes before the nodes, for the
      pickle refusal below.
    • A model whose weights are in a pickle format (.bin, .pt, .pth,
      .ckpt, .pkl) stops being placed: its node refuses the source. Give it
      its safetensors or GGUF, or add it again with allow_pickle: true
      (llmgw ctl model add -f) if its files were checked. Look at the
      models' sources before upgrading the nodes: one ending in
      .ckpt or .bin, or a mirror version holding pytorch_model.bin. Upgrade the control planes first: a node on
      this version refuses those weights until its control plane can say
      allow_pickle.
    • Strata is opt-in: nothing installs it until engines or an
      engine_overrides entry names a version (0.2.7).

    Added

    • Strata as an engine: a 125B model of experts on one card. Strata
      (github.com/Niko1221/Strata) keeps a model's experts in RAM and the ones
      used most in VRAM. On an RTX 6000 24 GB with 251 GB of RAM it serves
      Qwen3.8-Flash-Next 125B (IQ2_XS) at 70–87 tokens a second to one client;
      more clients share about that, its slots buying fairness rather than
      throughput on one card. It runs a few families only — Qwen3.8-Flash-Next
      and its variants, Qwen3.6-35B-A3B, DeepSeek V4.1-Flash — each named in a
      profile's args with --family and --size
      (--family qwen --size IQ2_XS --context 32768). The source is the
      directory of the model's GGUF files, or the repository, which Strata then
      downloads itself, pinned. The agent installs it on an NVIDIA node of
      x86-64: its source pinned by digest, in a Python environment, and its
      release's engine checked against the digest GitHub publishes — the CUDA 13
      build for Turing and newer on driver 580, the CUDA 12.9 build for Pascal
      to Ada, a V100 included, on driver 575. A replica prepares its model the
      first time (minutes; its draft layer is a 5 GB download) and loads in
      about a minute and a half after. One card of one node per replica, for
      now; not in an offline bundle, its packages coming from PyPI.

    • A served model's context, changed. llmgw ctl model context MODEL TOKENS [--slots N], POST /api/v1/models/{name}/context, and Context
      length… in the console's model menu. The catalog, the engine's arguments and
      the memory change together, checked against what the model was trained for
      and what its cards hold; its replicas are then started again one by one. The
      models API shows context_length and max_output.

    • A second model mirror, kept in step. In a three-machine install the setup screens put one on a database machine
      (--second-mirror 1|2). An install with one mirror gets one with sudo llmgw mirror add-peer user@host.

      • In step: each mirror reads the other every thirty seconds. It copies what it lacks, checksum checked, and
        removes what the other removed, even if it was down at the time (llmgw mirror serve --peer).
      • Control planes: they name both (mirror.peers) and use whichever answers.
      • Nodes: while one mirror is down, a node is given a model's file on the other.

      This takes away the last single point of an HA install: with the first machine off, models are still added and
      fetched.

    Fixed

    • An engine that computes in a process of its own group was not its
      replica's.
      A GPU replica read as running on the CPU, and the memory it
      held on its card as somebody else's, which placement then kept free of
      other work. A process the replica started, at any remove, is now the
      replica's. And what a replica computes on, once sent, was never sent
      again: a wrong reading kept from before an agent's restart stayed wrong.
      It is sent again when it changes, and read again when a GPU replica was
      kept as the CPU.

    • VS Code's sign-in lost its place through the sign-in page, so the
      prompt to confirm came every time; a link to the same gateway by plain
      http could take an https setting down to http; and a new build of the
      extension was not installed over an old one (the command now says
      --force).

    • The install guide's recordings were not checked for secrets, only
      the page; every frame is now.

    • MCP server fields could not be emptied, an address could carry a
      password, the cluster's own server could not be changed from the
      console, and a grant to a key that does not exist was said done.

    • A guardrail service two policies named was asked as the first said,
      not the stricter; a null answer read as safe; and with restore the
      request log kept the answer with the redacted values back in it.

    • What an engine downloads by a Hugging Face id was not held to the
      pickle refusal
      — an adapter by its id, a TensorRT-LLM model's; a model
      with either now needs allow_pickle.

    • server init run again on a machine already set up was refused its
      memory
      , as on an 8 GB install fixing its certificate with --host, and
      judges that would not be installed (no mirror) were counted.

    • Removing an MCP server left its sealed credential in the store, and
      replacing a credential under another header kept the old one sent. A
      server's secrets now go when it no longer reads them.

    • A cloned repository could point the VS Code extension at another
      gateway.
      llmgw.gatewayUrl is a machine setting now, never taken from a
      workspace.

    • A node's memory bandwidth could be measured in its cache on a busy
      node, and was 0 after a reconnect until the next heartbeat. The buffer is
      at least eight times the processor's last cache.

    • A project kept in the cluster could have its questions embedded outside
      it.
      The similar-question cache now asks the embedding model as the
      question's project would, external fallbacks refused, and skips a model
      that is itself outside. Its comparing no longer holds the cache's lock,
      which every project's lookups waited on.

    • Restored values were left as placeholders in streamed tool calls, in
      completions and in reasoning, and a value with a quote in a whole answer's
      tool-call arguments broke their JSON.

    • An outside guardrail service read what redact had taken out, and a
      streamed answer was sent to it twice. The docs now say a service stops a
      streamed answer only under stream: buffer_all.

    • A replica found slow once could stay unused. Its time to first token
      is forgotten after five minutes without an answer, so it is tried again.

    • Weights that run code got past the refusal in four ways. A mirror
      version already on a node was used without being looked at again; a
      ?query or #fragment on an hf: source hid the file's name; .pt2 was
      not on the list; and a profile could write --load_format,
      --trust_remote_code or an abbreviation such as --trust-remote, which
      vLLM and argparse read as the flag, or give SGLang --load-format. All
      refused now. TripoSR's preset says allow_pickle, without which it could
      not be added, and a shared ComfyUI environment takes its first member's.

    • A temperature written as a string was refused. Since the GenAI traces
      read it, "temperature": "0.7" got a 400 before reaching the engine; it
      is read loosely again, for the trace only.

    • A reused answer could carry another caller's personal data. With
      restore: true and the answer cache on, two callers' requests that
      redacted to the same text shared one answer — with the first caller's
      address put back in it. Such requests are now never answered from the
      cache nor kept in it.

    • Granting a server's tools could take others away; revoking could close
      a level to every tool.
      A level with no tool list sees what the levels
      above grant; giving it its first list made it see only that list. And a
      level left with an empty list was closed to every tool, which the screens
      did not show. Now a first list starts from the nearest list above, taking
      a server away leaves the rest or goes back to no list, the screens say
      when a level above keeps granted tools out of reach and which levels are
      closed, and the console reads and writes the level at once rather than
      from what it loaded.

    • Similar questions about different pictures got each other's answer.
      A question with a picture, a sound or a file in it is reused only when it
      is the same request.

    Added

    • A card in the wrong PCIe slot is said so. Each agent reads how every
      card is linked — the fastest generation card and slot allow together, and
      the lanes in use — against the card's own most, from the kernel (the
      driver's figure is what card and slot allow together); a card on fewer lanes or
      an older generation is a node warning, with what it carries and what it
      could, on the dashboard, the console and in llmgw doctor.
    • And on a web page, /admin/mcp. For administrators: the same list,
      adding a server with its credential sealed, testing it and listing its
      tools, granting them in the tree beside that level's other grants and
      limits, revoking, changing, replacing the credential, turning it off,
      removing; and the line that points an agent at this gateway's /mcp.
      In English and Turkish; every change in the audit log, the secret not.
    • The company's MCP servers in the console. A screen of its own: each
      server with its state, who has its tools, its rate, the names of the
      credentials it is sent and its address; add one with a bearer token or
      a header kept sealed, test it and list its tools, grant them to a team,
      project, organization, person or key beside that level's other grants,
      revoke, change, replace the credential, turn off, remove.
    • And on the cards, as this cluster would lay it out. The explorer's
      GPU tok/s and a repository's files give one reply's speed for each
      layout the planner offers here — one card, tensor parallel across a
      node's cards, llama.cpp's layer split, a pipeline across nodes on a
      fabric — the fastest of them, with the layout in the tooltip. From each
      card's memory bandwidth by its model name, NVLink or PCIe between a
      node's cards, and InfiniBand or RoCE between nodes; a cluster of mixed
      classes is judged class by class. A model of experts reads 1.6 times its
      active share, as Qwen3-Coder-30B-A3B did on cuda01's RTX 6000 (144
      tok/s; qwen3-vl-8b Q8: 62).
    • How fast a model would answer on a CPU, before it is fetched. Each
      agent measures how fast its node's memory reads when it starts (Host
      gets mem_bandwidth_gbs, the node's view mem_bandwidth_gbs); the
      explorer's CPU tok/s column and a repository's GGUF files say about
      how many tokens a second one reply gets on the fastest node that holds
      the model — the bandwidth over the bytes a token reads, its active part
      for a model of experts. Calibrated on the PoC against llama.cpp's own
      timings: within 15% from 1.5B to 14B.
    • The explorer lists what this cluster can run. Judged by the nodes
      as they are now — every card's memory and compute capability, every
      node's RAM — through the same planner as serving a model, rather than a
      size picked from a list. A model too big for any node, in FP8 without a
      card of 8.9, in MLX, or in neither GGUF nor safetensors, is left out and
      counted; the page says what it judged by. With cards, safetensors repositories are
      listed beside GGUF. On a repository's page a file too big for the
      cluster is shown with what it needs and no Fetch button.
    • Weights that run code are refused. Python pickles — .bin, .pt,
      .pth, .ckpt — run whatever they hold when loaded, which is how the
      malicious models on Hugging Face work. The node refuses such a source,
      or a mirror version with one in it, before fetching it; adding such a
      model is refused; vLLM and SGLang downloading a model by its id read its
      safetensors only; the mirror page marks the file. allow_pickle: true
      on a model lets it through on purpose. The mirror's explorer no longer
      lists a repository with nothing a node loads (no GGUF, no safetensors)
      while "runs here" is ticked, and a repository's pickle files are not
      offered, only counted. BACKLOG M lists what is next:
      checksums against Hugging Face everywhere, its own scan, trusted
      publishers, engines without the internet.
    Downloads
  • v0.15.0
    CI / test (push) Successful in 9m0s
    Release / build (push) Successful in 4m23s
    Release / notes (push) Successful in 3s
    Stable

    gitea-actions released this 2026-10-25 18:17:57 +01:00 | 116 commits to main since this release

    The company's tools and editors through the gateway, and guardrails that
    can lean on others. Its own MCP servers sit behind llmgw's /mcp under the
    guardrails, a rate and a breaker. VS Code gets the gateway's models from an
    extension the gateway serves. A guardrail policy can ask an outside
    service, and can put redacted values back in the answer. Chat goes to the
    replica answering soonest. A similar question can get the answer already
    given. A cached prompt can be priced apart. Traces follow the OpenTelemetry
    GenAI conventions, and Kong is written down from trying it. Also an install
    guide with every screen, at /install.

    Upgrading.

    • Migrations 0049 (mcp_servers), 0050 (their rate and breaker) and 0051
      (projects.cache_similar), applied when the control plane starts.
    • llmgw server init now refuses a machine that would serve models without
      the memory for them: the starter model and the judges need 12 GB on one
      machine, where 8 GB was said before. --no-model, --no-guard-model or
      --no-node make it fit. A running cluster is not checked again.
    • Traces carry the GenAI conventions' gen_ai.* attributes beside the
      llmgw.* ones, which stay; docs/install.md, "Tracing", lists them.
    • Nothing else changes until it is turned on: services and restore in a
      guardrail policy, keys cache --similar with
      router.cache.embedding_model, cached_per_1k in a price.

    Added

    • Answers to similar questions. llmgw keys cache --similar 0.95 also
      gives a chat question the answer already given to one like it in the same
      conversation, compared by router.cache.embedding_model, as
      x-llmgw-cache: similar with the similarity. Held in memory beside the
      exact answers; nothing written to the store but the setting.

    • A cached prompt costs the cached rate. cached_per_1k in a price
      charges the prompt tokens an engine or provider read from its cache (the
      answer's cached_tokens) at their own rate; without it they cost what any
      prompt token does, as before.

    • Chat goes to the replica answering soonest. The router keeps each
      replica's time to first token, smoothed over its last answers, and the
      chat profile scores by it beside the queue and the KV cache: of two
      replicas of one model, the one on the slower card or the busier host gets
      less, and a replica not yet measured is tried.

    • llmgw and Kong, written down (docs/kong.md): Kong 3.9
      in front of llmgw by a plain route or its ai-proxy, each team's llmgw
      key passed on; Kong as a provider behind llmgw; and that Kong 3.9 counts
      no completion tokens of a streamed answer, so bill from llmgw's usage.

    • A guardrail that is someone else's service. A policy's services
      asks Azure Content Safety, Lakera, NeMo or a company's own classifier over
      HTTP: a body template, keys from files, dotted paths to where the answer
      says unsafe and why, on input, output or both, block or flag, and the
      policy's fail when it does not answer.

    • Redacted values put back in the answer. restore: true on a guardrail
      policy's input: the model reads [EMAIL_1], the client reads its own
      address in the reply, streamed or not. Only the request's own values, held
      in memory for its life; the output rules read the answer as the client
      will.

    • Traces in the OpenTelemetry GenAI conventions. An inference request
      and each attempt at an engine carry gen_ai.operation.name, the requested
      and the answering model, max_tokens, temperature and top_p when set,
      the session as gen_ai.conversation.id, and the input, output and cached
      tokens, and an attempt the engine that answered as gen_ai.provider.name.
      Tools that read the conventions show llmgw's calls as model calls with no
      dashboard of their own. No prompt or answer text is ever in a span.

    • llmgw for VS Code. An extension of llmgw's own, served by every gateway
      at /setup/vscode/llmgw.vsix with that gateway's address in it, made from
      the binary itself: plain JavaScript, no dependencies, no build, and so no
      marketplace needed at a site with no internet. It offers the gateway's chat
      models to VS Code's language-model API — the chat's model picker, agent
      mode, and every extension that asks VS Code for a model — with the sizes
      and the tool and vision capabilities the catalog says, streamed, with tool
      calls and their results both ways. VS Code is a tool on the account
      page's Connect your editor: a key, the one-line install, and an Open
      in VS Code
      link that hands the key to the extension, which keeps it in
      VS Code's secret storage. llmgw: Sign In opens that page with a state
      of its own; a link without it is confirmed first, the gateway named.

    • An install guide with every screen, at /install. From three bare
      Ubuntu machines to a first answer: what each machine needs and how to
      check it, the release fetched and checked, the setup screens one by one,
      the system's state, and a first request with a key from the account page.
      The screens are a real three-machine install, recorded and kept as
      terminal frames — sharp at any size and a few kilobytes — with each
      session played back on request; the administrator's password and every
      key were masked before it was kept. /about says more of what llmgw does
      (documents, guardrails, sign-in, high availability, pictures and 3D), and
      its install steps are the ones of today, leading to the guide.

    • The company's MCP tools under the guardrails, a rate and a breaker.

      • The answer: the caller's policies read a tool's answer as the prompt it becomes. An instruction planted in a
        ticket is withheld as a pasted one would be, and personal data is redacted.
      • The arguments: they are read before they leave, so a secret pasted into one does not reach the company system.
      • Rate: ctl mcp add --rate N allows each caller N calls a minute to a server.
      • Breaker: five failures in a row take a server out for a minute, its tools off the list.
    • How fast the judge is, measured. llmgw ctl guard judge check (and POST /api/v1/guard/judges/check) works
      out whether the judge keeps up with requests. It times three judgments of a 280-token prompt, then a burst at once,
      and sets both against the policies' wait. It says ok, tight or slow, about how many requests a second can be judged,
      and what to do: more cores, more judges, a GPU judge, a longer wait, or fail: open. server init runs it once the
      judge is up. If the machine is too slow it says so before the next steps, not after the first refused request.

    • The company's own MCP servers through llmgw's /mcp. llmgw ctl mcp add jira --url … --secret-file Authorization=FILE registers one; /mcp then lists its tools as jira.search beside llmgw's own. Only keys granted
      them see them: llmgw keys limits --tools 'jira.*', patterns at any level of the tenancy tree. None granted anywhere
      is none. The server's credentials stay on the control planes, kept in the store sealed or read by reference. The
      server is told the calling key, person and project. Each call is bounded in time and size, and its progress passes
      on to a streaming client. Every call is in the audit log with its arguments' size and digest, never their text. The
      admin API has /api/v1/mcp/servers.

    • The cluster as a read-only MCP server. Registered as llmgw ctl mcp add llmgw --url llmgw:admin and granted
      as llmgw.*, its tools say what llmgw ctl does:

      • cluster_status, list_nodes, list_models, list_replicas, explain_placement, node_logs;
      • nothing that changes the cluster.

      It is answered in the control plane's own process, so there is no token to keep and every control plane serves it.
      The admin API serves the same at POST /api/v1/mcp. ctl mcp add --bearer-file keeps a token, sent as
      Authorization: Bearer.

    • Large models across cards and nodes. For a model of experts on more than one card, the plan now adds vLLM's
      --enable-expert-parallel. It reads the expert count from config.json under any of its names, and sizes a model
      that also reads pictures by its text_config. Several nodes are offered in any number, not only powers of two. Each
      way now says what it needs of a card, part by part (weights, context, engine), and of all its cards together. This
      shows in ctl model plan, in the console's Serve a model, and in a serve job's first step. The serve form checks that
      a longer context still fits. ctl model serve --env KEY=VALUE puts engine variables such as NCCL_DEBUG=INFO on
      every node. docs/large-models.md has the rules, the commands, and a checklist for bringing a model up on a fabric.

    • Machines of the judges' own in a three-machine install. The setup screen asks for up to two judge machines after
      the store's two; server init takes --judge-node user@host for each. Each is added as a node labelled
      llmgw.role: judge, and the judge's profile selects it, so the judges run there alone. Under load on a 4-core
      control plane that was also a node, 38 of 60 requests were refused for want of a judgment. With the judges on a node
      of their own, 2 were. A node with an llmgw.role label takes only profiles whose node selector asks for that role,
      so no other model is placed on the judges' machine.

    Fixed

    • A machine too small for the models it would serve is said so first.
      The control plane that is also a node, serving the starter model and two
      judges, needs about 10.5 GB. On 8 GB the install finished every step and
      then had no room for the starter. Now the setup's checks fail its memory
      with what it would serve, and server init refuses before it installs
      anything, naming --no-model, --no-guard-model and --no-node.

    • Turkish pages are Turkish to the end.

      • Errors: those from the store, catalog and mirror that a page shows ("not found", "not empty", "already in the
        catalog", the settings changed meanwhile, and others) are now in the page's language.
      • Percent: a percentage is written the language's way: %93 in Turkish, 93% in English.
      • Footer: the time at the foot of a page is in the language's date form.
      • Stale forms: they are refused with a sentence in that language rather than "bad request".
    • llmgw keys limits no longer clears the limits it was not given. Setting one limit at a scope wrote the whole
      row, so --tools 'jira.*' alone took away that scope's rate, budget and model list. Only what is given changes now.
      --replace writes every limit, as the console's limits form does.

    • The plan refused tensor parallelism past a model's KV heads. Qwen3-235B, with four KV heads, had no way to run
      on eight cards, though vLLM gives each card a copy of a head. A width that is a multiple of the KV heads is now
      offered, as is one that divides them.

    • A big model on a slow mirror was failed while it downloaded. The startup timeout counted the download, so
      hundreds of gigabytes at a few hundred MB/s ran past twenty minutes and the replica was started again from nothing.
      The timeout now runs from when a node's download last moved.

    • The judge init installs is no longer replaced by the leader's own. Just after the control plane started, the
      leader saw a policy naming a judge that init was still fetching, and served one itself. Its entry replaced init's, so
      a second judge ran with the leader's sizing, and with judge machines it ran off them. The leader now waits ten minutes
      after it starts before it serves a judge itself. If someone adds one while it fetches, theirs stays.

    • A judge the leader serves by itself is sized as init's is. When a guard policy names a judge the catalog lacks,
      the leader serves it. That judge reserved 3 GB and no cores, and ran llama.cpp's prompt cache, which grew one to
      3.2 GB. Without cores of its own, a judgment on a busy node missed its 5 s budget, and fail: closed refused the
      request. It now gets what server init gives its judges: 2 GB, two cores each (fewer on a small node), and no
      prompt cache. Every judge served through modelserve runs with --cache-ram 0.

    Downloads
  • v0.14.9
    Release / build (push) Successful in 5m51s
    Release / notes (push) Successful in 3s
    CI / test (push) Successful in 6m24s
    Stable

    gitea-actions released this 2026-10-19 16:17:43 +02:00 | 144 commits to main since this release

    Sign-in set up from the console, with Active Directory tried before it is
    kept. A node's console now shows what the machine is doing, and a node's web
    page shows what it runs. Also fixes found trying Active Directory against a
    Samba domain controller on the test cluster. Sign-in through it, nested
    groups and a disabled account suspended by the sync all worked there.

    Upgrading.

    • No migration.
    • auth.ldap and auth.oidc are in the cluster's settings, as before. A
      block written into server.yaml on a cluster with a store was never
      read; the log said so. Put it in with llmgw ctl config edit, or set it up
      again from the console's Sign-in screen.
    • A Samba DC's own autogenerated certificate is refused (a negative serial
      and no SAN). Give it one from a CA.

    Added

    • Sign-in set up from the console. Under People → Sign-in, sudo llmgw shows how people get in: single sign-on,
      Active Directory and SCIM, each on or off. It also shows the directory's last sync. E sets up Entra ID from the
      tenant and client IDs. D sets up Active Directory, and the directory is tried before it is kept: each server
      reached and bound, how many people may sign in, how many are in each group. Nothing is kept if a step fails. The
      password, client secret and SCIM token are kept in the store as store:auth/…, sealed by the master key. The
      domain's CA goes into the settings (auth.ldap.ca), so no file has to be put on each control plane. The admin API
      has the same: GET /api/v1/auth, POST /api/v1/auth/ldap/test and PUT /api/v1/auth/secrets/{name}.
    • A node's page says what it runs. It now shows the agent's version, marked when the control plane runs another,
      and the engines with their versions. Each GPU row now names the models loaded on that card, and each engine row its
      GPUs.
    • sudo llmgw on a node opens on what the machine is doing. A new Overview screen shows the agent's state and
      version, each control plane it dials and whether it answers, every GPU with the models on it, the engines running
      here, the free space for weights and engines, and the agent's warnings of the last hour. It is read on the node
      itself, every three seconds, so it needs no admin token and works while the control plane is out of reach.

    Fixed

    • A broken auth.ldap no longer stops every control plane. It is the cluster's setting. A CA file that one
      machine could not use made llmgw server exit on every control plane, and no ctl config could then put it right.
      Directory sign-in is now turned off on that control plane, with an error in its log. Local accounts still sign in.
    • A second llmgw server on a machine stops before it competes to lead. llmgw server check (no such command)
      ran a whole server. Its ports were taken, yet it went on to hold the leader's lease for three hours while the real
      control planes stood by. server now refuses words it does not know and takes its ports before anything else.
    • The single sign-on and Active Directory guides said server.yaml. On a cluster with a store, auth is in the
      cluster's settings and the file's block is not read. They now say llmgw ctl config edit, and what certificate a
      domain controller needs.
    Downloads
  • v0.14.8
    CI / test (push) Successful in 9m12s
    Release / build (push) Successful in 4m20s
    Release / notes (push) Successful in 3s
    Stable

    gitea-actions released this 2026-10-18 11:18:14 +02:00 | 149 commits to main since this release

    Sign-in with an on-premises Active Directory, and Entra ID made safe for
    people in many groups. Also several keys for one outside provider, and a
    measure of what the gateway adds to a request.

    Upgrading.

    • Migration 0048 adds the user_directory table, applied when the control
      plane starts.
    • Nothing changes until auth.ldap is set; docs/install.md, "Active
      Directory", has an example. It has been tested against a fake directory,
      not yet against a real domain controller.
    • A judge installed by an earlier server init is refitted by the leader
      by itself (below); the steps given for v0.14.7 are no longer needed.

    Added

    • Sign-in with Active Directory (auth.ldap). People sign in on the gateway's form with their AD username or mail
      and password, checked against the domain controllers over LDAPS or StartTLS. Groups, nested ones included, give
      staff roles and places in the tree, as with OIDC. Accounts are tied to the objectGUID and made at first sign-in
      when create_accounts is on. Every 15 minutes the leader reads the directory. People disabled or deleted there are
      suspended and reinstated when they come back; roles and memberships follow their groups. A sync that would suspend
      more than half the accounts is refused. See docs/install.md, "Active Directory".

    • Several keys for one outside provider. An external model takes api_key_files, each with an optional weight, in
      place of api_key_file. Each key is an endpoint of its own (external:MODEL#1, #2), so a key answering 429 is
      passed over while its breaker is open and the request goes to another. A key answering 401 is passed over too, and
      external_key_refused is sent, naming the endpoint, never the key. Usage records which key answered by that name.

    • llmgw ctl bench overhead: what the gateway adds to a request, measured against an engine that answers at
      once. Gateway and engine run in one process on loopback, so no cluster is needed. The load is sent straight to the
      engine and through the gateway: bare, with authentication and accounting, and with the guardrails' pattern checks.
      It reports p50 and p99 latency, what the gateway added, and a rough CPU per thousand requests. On an Apple M5 the
      gateway added 0.05–0.42 ms at p50 and at most 0.49 ms at p99 (README).

    Fixed

    • Entra ID's groups past 200 no longer take a role away. For someone in more than 200 groups Entra sends none in
      the token, only a pointer to Graph. That was read as no groups, so an administrator lost their role at the next
      sign-in. Their role and memberships are now left as they were, and an error is logged saying what to change in the app
      registration. docs/install.md has an Entra ID section.
    • An account made by SCIM or OIDC could not be reinstated. Setting it active was refused as "never approved".
    • An upgrade refits the judge an earlier server init installed. Upgrading to v0.14.7 left such a judge as it was:
      no cores or one, 2 or 4 GB, the prompt cache on. On fewer than eight cores every request was refused until it was
      fixed by hand. Now the leader finds a judge whose entry is exactly what an earlier init wrote and gives it 2 GB, no
      prompt cache and cores by today's rule (up to four for one judge, two each for more). It then restarts the judge's
      replicas onto that. The number of replicas is left as it is. A judge whose entry was changed by hand is left alone,
      and the log says so once.
    • A restart is no longer refused for room another restart is giving back. With every core of a node reserved, two
      models restarted together, one to need more and one less (as after ctl model resources on a judge and a starter),
      had the first refused: "fits on no node even with this replica stopped", though the second was about to give back
      its cores. A restart now waits for other restarts under way, up to the drain grace, before it is refused. A drained
      replica also counts a replica being restarted that still answers as serving, so replicas restarted one after
      another no longer wait out the five-minute grace each.
    Downloads