Repository navigation
Maintenance pass (2026-10): upstream bumps, scheduled CI, honest status - #1
Merged
Merged
Conversation
- setup-php 2.36.0 pin -> @v2 and the PHP 8.5 x macos-14 leg restored (ext-template#1, previously unsynced; clears the setup-php <2.37.1 advisories). - Weekly scheduled CI + workflow_dispatch. - ZTS test legs (Linux, PHP 8.4/8.5; continue-on-error for now). - Managed .github/dependabot.yml: grouped weekly cargo minor/patch; versions.conf pins and the primary upstream crate are excluded.
Cascaded pins: ext-php-rs =0.15.13 -> =0.16.3, thiserror =2.0.18 -> =2.0.21. Binding adaptation (no PHP-visible change): ext-php-rs 0.16 converts #[php(defaults(...))] values into the parameter type, so Model::chat() and Model::raw() defaults are now typed literals (128_u32, 0.0_f32, ...). Reflection confirms the PHP defaults are unchanged. Surface check: identical ReflectionExtension dump except trailing nullable array params now report their null default (the documented 0.16 fix aligning arginfo with the stubs).
Bundles a llama.cpp from mid-October 2026 (four months of upstream:
new model architectures, kernel and tokenizer fixes).
Binding adaptation, no PHP-visible change:
- Tokenization moved from LlamaModel to LlamaVocab (`model.vocab()`):
str_to_token -> vocab.tokenize(bytes, add_bos, parse_special=true),
is_eog_token -> vocab.is_eog, token_to_piece_bytes -> the growable
vocab.token_to_piece_into (the old fixed 32-byte buffer could error on
a long piece).
- The new tokenizer takes raw bytes, so it no longer rejects an interior
NUL. A shared `tokenize()` helper keeps refusing it with the exact
message the CString path produced ("nul byte found in provided data at
position: N"), so behavior is unchanged.
- AddBos is gone; callers pass a bool.
Re-verified against v0.2.0 with a probe covering chat (greedy), the
reasoning split, raw completion, JSON-Schema-constrained output and
unsupported-keyword rejection, raw GBNF, a ~1.4k-token prompt (past the
old 512 batch), NUL rejection, last- vs mean-pooled Qwen3-Embedding
cosine rankings with and without the query instruction prefix, packed()
parity, and Qwen3-Reranker yes/no scores + rank() rows: output is
byte-identical to v0.2.0. sample() still wraps llama_sampler_sample
(which accepts), so the double-accept fix holds. 15/15 PHPTs with real
models; clippy -D warnings clean; cargo-about license gate passes.
llama.cpp's context n_batch defaults to 2048, and llama_decode asserts (GGML_ASSERT(n_tokens_all <= cparams.n_batch) -> abort(), not an error code) when one batch exceeds it. ext-infer decodes the whole prompt as one batch, and its own guard only compares against n_ctx — so in v0.2.0 any chat()/raw() prompt between 2049 tokens and nCtx killed the interpreter: no exception, no shutdown functions, a dead FPM worker. RerankModel was exposed with *default* options (n_ctx 4096): a query + document of 2049..4096 tokens aborted. Reproduced on v0.2.0 and on llama-cpp-2 0.1.160; embeddings were safe (capped at n_ctx 2048). Set n_batch = n_ctx for the completion, rerank and embedding contexts. n_ubatch (the physical chunk that sizes compute buffers) keeps its default, so memory is unchanged (1k-token prompt, max RSS v0.2.0 vs this: 948MB vs 949MB at nCtx 2048, 1639MB vs 1637MB at nCtx 8192). No PHP-visible API change; the over-n_ctx path stays a clean InferenceException. Regression test 017-long-prompt.phpt (model-gated): fails with the abort on v0.2.0, passes here.
- models.md: Nanbeige4.2-3B (nanbeige, llama.cpp #25994) verified for chat, reasoning split and schema output; Spark-X2.5-1.7B (spark2_5, #27868) loads but its Jinja-only template needs raw(). Both fail to load on 0.2.0. Granite 4.2 3B noted as working on both. - chat.md: 'apply_chat_template failed' also means an unrecognised Jinja template (ffi error -1), not only an unsupported role sequence. - Qwen3 has no 7B: bartowski/Qwen3-7B-Instruct-GGUF and Qwen/Qwen3-7B-Instruct-GGUF don't exist (HF 401). Point at the official Qwen/Qwen3-8B-GGUF (Q4_K_M 5.0 GB) instead.
introduction.md and installation.md still described ext-infer as pre-release with the first tag pending; v0.1.0, v0.1.1 and v0.2.0 have all shipped with PIE binaries. Status now reads as maintained and points at the CHANGELOG, the future issues, and PLAN.md's design notes.
make stubs now writes target/<stubs file> for a signature diff against the curated stubs/ copy (cargo-php never had the --stubs flag the target passed), with STUBS_PRELOAD for Linux. RELEASE.md and the stub header point at the new workflow.
ubuntu-26.04 runners mix libclang 22 with LLVM 21 builtin headers, which fails ext-php-rs's bindgen on every Linux leg (main included); see ext-template@3978271.
- README: a Status section (feature-complete, maintained, last verified 2026-10-29 with real values) replaces the pre-release badge; the roadmap's Next list moves to `future` issues with their reasoning; every repo in the stack is linked from one table; the blog series is linked from the README and the mdbook introduction. - PLAN.md: design notes and decision history (this pass's decisions recorded) instead of a what's-left list; remaining work links the issues. Stale ext-php-rs / ZTS / OpenBLAS notes updated. - Deliberately-out-of-scope sections unchanged.
Cargo.toml version and CHANGELOG.md (Keep a Changelog, backfilled from the existing tags and their release notes). Not tagged: tags trigger the binary release workflow and are cut by the maintainer after merge.
ericmann
marked this pull request as ready for review
October 29, 2026 17:01
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.
Part of the 2026-10 maintenance pass. Merge after ext-template#2. Prepares v0.2.1 (not tagged).
Release headline: "Now loads Nanbeige4.2 and Spark-X2.5", plus a fix for long prompts aborting the PHP process.
What changed and why (one bump per commit)
chore: sync scaffold from ext-template@442aa21: setup-php unpinned (clears the <2.37.1 advisories), the PHP 8.5 × macos-14 leg restored, weekly schedule, ZTS legs, managed Dependabot.chore: sync scaffold from ext-template@339f16f: ext-php-rs 0.15.13 → 0.16.3, thiserror 2.0.21. Binding adaptation: 0.16 converts#[php(defaults(...))]values into the parameter type, sochat()/raw()defaults became typed literals (128_u32,0.0_f32). PHP defaults are unchanged (Reflection-verified).chore(deps): llama-cpp-2 =0.1.146 → =0.1.160(April → mid-October llama.cpp). Tokenization moved toLlamaVocab. A sharedtokenize()keeps rejecting NUL bytes with the exact old message, since the new byte-slice tokenizer would otherwise accept them.fix: size n_batch to n_ctx: llama.cpp's contextn_batchdefaults to 2048 andllama_decodeasserts (abort, not an error) when a batch exceeds it. In v0.2.0, anychat()/raw()prompt between 2049 tokens andnCtxkilled the process, andRerankModeldid so with default options (n_ctx 4096). Reproduced on v0.2.0 and on 0.1.160; new model-gated PHPT017-long-prompt.phptfails on v0.2.0 and passes here. RSS unchanged (948 vs 949 MB at nCtx 2048; 1639 vs 1637 MB at 8192). This also breaks rag-over-markdown on larger corpora (bin/rag ask --k 8over ~28 KB aborts).Qwen3-7B-Instructlinks (Qwen3 has no 7B; HF 401) now point atQwen/Qwen3-8B-GGUF; "v0.1.0 still in flight" notes removed;make stubsworkflow; ZTS claims updated; Status section, stack table, writing links, a deliberately-out-of-scope section sourced from ARCHITECTURE.md's non-goals (the README had none); PLAN.md as design notes; CHANGELOG.md backfilled (v0.1.0-rc.1 → v0.2.0).Re-verification of the load-bearing behaviours
A probe was run on the v0.2.0 build and on this branch, and the output is byte-identical: greedy chat text, reasoning split, raw completion, JSON-Schema output plus hard-fail on an unsupported keyword (
pattern), raw GBNF, a ~1.4k-token prompt (past the old 512 batch), NUL rejection, last- vs mean-pooled Qwen3-Embedding cosine rankings with and without the query instruction prefix (last pooling ranks the right doc first; the prefix asymmetry holds), raw embedding floats,packed()parity, and Qwen3-Reranker yes/no scores andrank()rows.sample()still wrapsllama_sampler_sample(which accepts), so the double-accept fix holds; grammar generation runs 160 tokens without the oldGGML_ASSERT.New architectures (llama.cpp PRs merged after mid-June; both fail to load on v0.2.0):
nanbeige, [Model] Add support for Nanbeige4.2 ggml-org/llama.cpp#25994, Jul 27):chat(), reasoning split (824-byte reasoning, answer "51"),schemaoutput. ✅spark2_5, #27868, Sep 6): loads and runsraw(). Its Jinja-only chat template isn't recognised by llama.cpp's built-in matcher, sochat()throwsapply_chat_template failed: ffi error -1. Documented; Jinja rendering is Render chat templates with Jinja (models llama.cpp's built-in matcher doesn't recognise) #2.Release-build performance on a Ryzen 5 3400G: prompt processing 12.66 s → 13.10 s for ~1k tokens; generation 34.4 → 33.7 tok/s. No regression. (The absolute prompt rate looks low; see #13.)
Gates
-D warnings+ fmt clean; cargo-about gate passes.PHP_ZTS=trueconfirmed). CI is model-free by design, so 12 of 16 PHPTs skip there; the model tier is the local run above.ReflectionExtensiondump of every class, method and parameter matches pre-pass except as noted below. cargo-php 0.2.1 stub output matches 0.1.21's except for fully-qualifiedextends.\TypeError(e.g.maxTokens: 'x'), where 0.2.0 threw a plain\Exception(ext-php-rs 0.16.3 #820). It matches PHP's internal functions, butcatch (\Exception)no longer catches it. If you'd rather not ship that in a patch, pinningEXT_PHP_RS_PINto=0.16.2avoids it.?array $optionsparameters now report theirnulldefault via Reflection (isDefaultValueAvailable()), matching the stubs. Harmless, but visible.Pre-existing findings (reported, not changed)
finalonEmbedding,Message,PromptandResponse, but the runtime classes aren't final (same in 0.2.0). The stubs also sayarray $options = []where the runtime has?array $options = null, andselfwhere the runtime reports the class name. Matching them means either making the classesfinalat runtime (a PHP API change, out of scope here) or droppingfinalfrom the stubs. That's your call. Same pattern in ext-turbovec and ext-whisper.getFile() === ''/getLine() === 0(Exceptions thrown from the extension report an empty file and line 0 #14).Draft release notes — v0.2.1