displace/ai-contracts

Stable, dependency-free PHP interfaces for local-first AI primitives: embeddings, generation, vector search, reranking, transcription.

Maintainers

Package info

github.com/DisplaceTech/ai-contracts

pkg:composer/displace/ai-contracts

Statistics

Installs: 13

Dependents: 0

Suggesters: 1

Stars: 0

Open Issues: 0

v0.1.0 2026-06-11 17:53 UTC

This package is auto-updated.

Last update: 2026-06-11 19:00:27 UTC


README

Stable PHP interfaces for local-first AI primitives.
Embeddings, generation, vector search, reranking, transcription — zero dependencies, zero implementations.

CI Packagist PHP 8.3 / 8.4 / 8.5 Zero dependencies MIT License

What is ai-contracts?

Five interfaces under Displace\AI\Contracts that describe what AI primitives do, without saying how:

Interface Contract
Embedder text in → packed float32 vector out
VectorIndex id-addressed similarity search with allowlist filtering
Generator prompt in → completion out
Reranker query + candidates → best-first relevance scores
Transcriber audio file in → transcript + timestamped segments

The package contains no implementations and no dependencies — it is the integration surface between things that provide AI primitives (ext-infer, ext-turbovec, hosted APIs, anything else) and things that consume them (LLPhant, NeuronAI, Prism, your application). Frameworks ship the drivers; applications code against the interfaces; providers can be swapped without touching either.

composer require displace/ai-contracts

The packed-vector contract

Every vector crossing these interfaces is a packed little-endian float32 binary string — the output of pack('g*', ...$floats) — never a PHP float array. Arrays inflate every coordinate into a zval; packed strings are a single contiguous buffer that moves through FFI and extension boundaries with zero per-element overhead, and batches compose by plain string concatenation:

use Displace\AI\Contracts\Embedder;
use Displace\AI\Contracts\VectorIndex;

function indexPosts(Embedder $embedder, VectorIndex $index, array $posts): void
{
    // One packed buffer for the whole batch, one add() call.
    $index->add(
        $embedder->embedBatch(array_column($posts, 'content')),
        array_column($posts, 'id'),
    );
}

function searchPosts(Embedder $embedder, VectorIndex $index, string $query, array $visibleIds): array
{
    // The allowlist composes with a SQL pre-filter:
    //   SELECT id FROM posts WHERE status = 'publish'  →  $visibleIds
    return $index->search($embedder->embed($query), k: 10, allowlist: $visibleIds);
}

Nothing above names a concrete engine. Wire in a llama.cpp-backed embedder and an in-process quantized index today, swap either side tomorrow.

Writing an adapter

Implementations are intentionally easy to write — here is a complete Embedder over ext-infer:

use Displace\AI\Contracts\Embedder;
use Displace\Infer\Model;

final class InferEmbedder implements Embedder
{
    public function __construct(private readonly Model $model) {}

    public function embed(string $text): string
    {
        // ext-infer ≥ 0.2 emits the packed contract natively; on 0.1,
        // bridge with pack('g*', ...$vector) instead.
        return $this->model->embed($text)->normalize()->packed();
    }

    public function embedBatch(array $texts): string
    {
        return implode('', array_map($this->embed(...), $texts));
    }

    public function dimensions(): int
    {
        return $this->model->embed('')->dimensions();
    }
}

The test suite ships in-memory reference fakes (tests/Fake/) that double as executable documentation of each contract's semantics — the InMemoryVectorIndex is a brute-force oracle you can test your own adapter against.

Versioning

Interfaces are forever-contracts: methods are never removed or re-signatured within a major version, and new methods only arrive with a major bump (an interface addition is a BC break for every implementor). Pre-1.0, minor versions may still adjust the surface — pin accordingly.

Deliberately out of scope

Implementations (this package never gains a class) · an orchestration framework — chains, agents, pipelines, prompt templates belong to the frameworks integrating these contracts · chat-message abstractions — every framework already has one; Generator is the lowest common denominator they adapt down to · streaming interfaces — premature until the underlying local engines ship streaming · training / fine-tuning.

License

MIT © 2026 Eric Mann / Displace Technologies