Repository navigation
Add LiteRT-LM as a local runtime (add-litert-lm-runtime) - #50
Merged
Merged
Conversation
Manifest parsing (strict per field, lenient per record, ABI and min core version gates, PyPI-only URLs), .whl extraction keeping only the native part of litert_lm/ under lib/, host platform rule (macOS 12+, glibc 2.27+, Windows x64), install through litert-host probe, active build by published_at among supported ABIs. Engine-builds host platform and probe become per-engine tables. Also the provider id, its data layout and settings schema, which the engine row needs to typecheck (part of 6.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hidden litert-host subcommand (probe, inspect, serve) in both binaries and the ./litert-host package export. bun:ffi bindings stream through a TinyCC shim that copies chunks on the library thread into a mutex-guarded FIFO drained from JS (spike S2), behind a LiteRtLibrary interface. The host serves /v1/models and /v1/chat/completions (stream and non-stream, tools, JSON schema, thinking as reasoning_content, images, stop, usage, 400s), one generation at a time with up to four warm conversations. Integration tests against the real library in a new test:integration project. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
litert-lm joins the local provider lists (search order last, owned_by, external sessions, cloud registry refusal) with LITERT_LM_* error codes and statuses. model.yml gains a strictly read litert_lm block. LiteRtLmRuntime runs one litert-host serve per model on the sidecar table behind a keyed gateway: platform and NPU generation checks, OpenVINO readiness, inspect cached by path and mtime for the newer-engine check, the file's backends and the context cut, GPU claim for gpu variants, host exit codes mapped to the provider errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /runtimes/litert-lm/accelerators and POST .../prepare in Foundry Local's shape (plus proxy): OpenVINO of the active release on an Intel NPU LiteRT-LM targets (generation by CPU model string), downloaded with sha256 and size, only openvino/libs kept, litert-lm:accelerators progress, one prepare at a time, no second download. POST /models/litert-lm/check answers for a variant without the network, and for a file by path inside the models root or by model id with its inspect metadata. create.ts registers the runtime where the platform rule holds, with GPU residency, engine-builds host and the checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An integration test installs the build from conf's release (wheel from PyPI), loads Gemma 4 E2B on CPU, streams and calls a tool through :1337 and unloads. docs/contracts.md gains the LiteRT-LM provider row, the ./litert-host export and the data layout; two ADRs (host mode with its own OpenAI server; PyPI wheels gated by our ABI number) are indexed; AGENTS.md and the Foundry Local ADR now name two bun:ffi exceptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e as a helper The accelerator service, runtime and check are made under the same condition, so the runtime takes the service's methods directly instead of fallbacks that never ran; the engine-builds probe of a LiteRT-LM build is liteRtHostProber. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, linked paths - A conversation gets no sampler unless the request names one, so the file's own sampling applies; a request's sampler gets a fresh seed unless it gives one (a fixed seed 0 answered every regenerate with the same text). - The host's server cancels running generations and waits for the library's last chunk before it deletes conversations and the engine. - The check resolves links before judging a path inside the models root. - compareVersions replaces a second version comparator; the accelerator reads the cached manifest before the network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…luded The runtime loads a model whose file is a link; the check by model_id now does the same instead of refusing it. Explicit paths still resolve links against the models root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r Bun Bun's node:http emits no close on the response or the socket once the request body is read, so a dropped stream ran to its end and held the generation lock (10.7 s on the RTX 4070). The host now also asks core's requestAborted, which reads Bun's native handle flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SetDllDirectoryW drops the working folder from the Windows DLL search, so a relative --lib failed with "litert-lm.dll does not export ...". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the Windows GPU (RTX 4070) temperature 0 answered gibberish and 0.0001 nothing: the sampler divides logits by it. Below 0.01 the host now asks for one candidate at temperature 1, the same choice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A windowless process counts as background work: Windows ran the host on efficiency cores at low clocks. On an i7-14700HX CPU decode of Gemma 4 E2B went from 6 to 28 tok/s and the RTX 4070 from 68 to 100. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve decoding Drafted tokens skip the grammar: with speculative decoding on, which is core's default, a JSON-schema answer came back malformed on GPU and failed with "compute_mask() called after stop" on CPU (0.18.1, macOS and Windows). The session config turns it off for such conversations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…runtime # Conflicts: # docs/decisions/INDEX.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
OpenSpec change
add-litert-lm-runtime, core part: run Google's LiteRT-LM (.litertlmfiles such as Gemma 4 E2B/E4B) as a local engine next to llama.cpp, on CPU, GPU and, on Windows, the Intel NPU through OpenVINO.What changes
Engine builds. New kind
litert-lm: Google's PyPI wheels, listed with sha256 in conf's manifest (ATOMIC_LITERT_LM_MANIFEST_URL). Each release carries anabinumber; core installs only the ABIs it binds (SUPPORTED_LITERT_LM_ABI = [1]). A.whlunpacks as a zip, and only the native part oflitert_lm/is kept under<build>/lib/. The host probes a build before it is switched in. ADR2026-10-21-litert-lm-builds-are-pypi-wheels-with-an-abi-number.Host mode
litert-host probe|inspect|serveof the core binary, onbun:ffi.ffi-bun.tsis the second file allowed to importbun:*. Chunks are copied on the library's thread by a small C shim, compiled with Bun's TinyCC, into a mutex-guarded FIFO that JS drains every 2 ms. A threadsafeJSCallbackread freed memory and crashed in about 1 run in 5. The host runs its own OpenAI server:stop;ADR
2026-10-21-litert-lm-runs-in-a-host-mode-with-its-own-openai-server.Runtime.
LiteRtLmRuntimeplus the following:litert_lmblock ofmodel.ymland thelitert-lmsettings schema;GET /runtimes/litert-lm/acceleratorsandPOST …/prepare, which download OpenVINO for a supported Intel NPU;POST /models/litert-lm/check, the verdict for a variant or a file;Fixes from running it on real hardware:
node:httpgives nocloseonce the body is read, so the host now uses core'srequestAborted;--libon Windows:SetDllDirectoryWdrops the working folder from the DLL search;Checked
npm run verifysteps after mergingmain:test:contract:bun— pass;test:e2e— 217 passed, 10 skipped;src/diffusion/server-process.test.ts › kills a server that does not finish loading in time. It failed in both full runs after the merge and in one before it, and passes on its own (3/3 each time). The fake engine has 1.5 s to record its pid, which a loaded machine misses. This PR does not touch diffusion;main, none of them a LiteRT-LM file.Integration on Google's real library (
npm run test:integration), with the wheel installed by core from PyPI and its sha256 checked:Hardware runs. Checked on each: correctness against CPU, streaming, tool call, JSON schema, conversation isolation, and cancel on disconnect.
Same machine, llama.cpp b11463 on E4B: CUDA 65, Vulkan on the iGPU 7.5, CPU 16. Prefill on CUDA is 7× faster than LiteRT-LM.
Network. No sockets other than loopback, and no DNS lookups, from
probe,inspectorserveon any OS.Not checked or not done:
VK_ERROR_DEVICE_LOSTon the Intel iGPU, so conf keeps GPU closed forlinux-x64.usage.completion_tokens: it counts stream chunks, so it is under-counted with speculative decoding. Left as is by decision.🤖 Generated with Claude Code