Free close socket toolbox image

ai-contracts & ai-toolkit: The Primitives a PHP AI Ecosystem Needs

Native extensions are powerful and heavy. They’re compiled per-platform, installed at the runtime level, and not something a framework can list in composer require and trust to exist. If the PHP AI story stopped at “install my three extensions,” it would remain niche.

So alongside the extensions, I shipped two intentionally thin, pure-PHP Composer packages to carry the architecture.

ai-contracts: interfaces, nothing else

displace/ai-contracts defines five capability interfaces and ships zero implementations:

  • Embedder — text in, vector out
  • Generator — prompt in, completion out
  • VectorIndex — store vectors, search by similarity
  • Transcriber — audio in, text out
  • Reranker — reorder candidates by relevance to a query

This is the same play PSR-3 ran for logging and PSR-18 ran for HTTP clients: when applications code against an interface, implementations become swappable infrastructure. Your Laravel app type-hints Embedder; whether the container resolves an ext-infer adapter, a cloud API client, or an in-memory fake for your test suite is a wiring decision.

The migration path from cloud-backed search to a fully local stack becomes a service-provider edit.

The five interfaces aren’t arbitrary — they’re the capability set behind essentially every production AI feature: embed, generate, search, transcribe, rerank. Compose them and you get RAG, semantic search, audio archives, structured extraction. The contracts package names the primitives to make it easier for the ecosystem to agree.

ai-toolkit: the code everyone writes anyway

displace/ai-toolkit collects the unglamorous utilities every embedding pipeline inevitably reinvents:

  • Chunkers — fixed-size, recursive, and sentence-aware splitting. Chunking quality quietly dominates retrieval quality, and naive substring splitting that bisects sentences poisons embeddings at the source.
  • Similarity over packed buffers — cosine and dot product computed directly against the packed float32 format from part 6, no unpack-to-array round trip.
  • MRL truncation — Matryoshka-trained models (Qwen3-Embedding included) front-load information so vectors can be truncated to lower dimensions with modest quality loss. Truncating 1024 → 256 quarters your storage; the toolkit handles the cut-and-renormalize correctly.
  • pack / unpack helpers — the bridge between PHP arrays and the packed-binary contract.

Pure PHP even without the extensions. The toolkit is useful even if your embeddings come from a cloud API and your vectors live in pgvector.

Allies, not competitors

PHP already has AI framework layers — LLPhant, NeuronAI, Prism — doing good work on orchestration: chains, agents, provider abstractions. A reasonable observer might expect a stack like mine to compete with them. The contracts package is engineered to do the opposite.

Those frameworks orchestrate providers — and today their providers are overwhelmingly HTTP APIs. ext-infer and ext-turbovec, wrapped in ai-contracts adapters, become something new in their catalogs: a local, in-process provider with zero network dependency. The frameworks keep owning developer experience; the extensions own the inference path; the contracts are the boundary where they meet. Their users gain a local option, and the extensions gain distribution through ecosystems that already have mindshare. Interfaces turn would-be competitors into a channel.

Assembling a layer cake

The architecture is bottom-up: Rust engines (llama.cpp, turbovec, whisper.cpp) → native extensions (ext-infer, ext-turbovec, ext-whisper) → pure-PHP contracts and toolkit → frameworks and applications. Each layer depends only downward. Each is also trivially replaceable.