Laravel SDK

The official Laravel SDK provides seamless integration with the brinicle vector engine through a Service Provider, Facade, configurable connections, and Artisan commands.

Installation

composer require bicardinal/brinicle-laravel
The package auto-discovers the service provider and facade, so no manual registration is needed.

Configuration

Environment Variables

Add the following to your .env file:
BRINICLE_URL=http://localhost:1984
BRINICLE_TIMEOUT=30

Publish Config

Publish the configuration file for customization:
php artisan vendor:publish --tag=brinicle-config
This creates config/brinicle.php:
return [
    'default' => env('BRINICLE_CONNECTION', 'default'),

    'connections' => [
        'default' => [
            'base_url' => env('BRINICLE_URL', 'http://localhost:1984'),
            'timeout' => env('BRINICLE_TIMEOUT', 30),
        ],
    ],
];

Multiple Connections

For applications that need to connect to multiple brinicle servers, add additional connections:
'connections' => [
    'default' => [
        'base_url' => env('BRINICLE_URL', 'http://localhost:1984'),
        'timeout' => 30,
    ],
    'analytics' => [
        'base_url' => env('BRINICLE_ANALYTICS_URL', 'http://analytics:1984'),
        'timeout' => 60,
    ],
],

Usage

Via Facade

The Brinicle facade provides a clean, static-like interface to the underlying client:
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

// Create an index
Brinicle::createIndex('products', dim: 384);

// Initialize and ingest
Brinicle::init('products', 'build');
Brinicle::ingest('products', 'p1', $vector);
Brinicle::finalize('products', optimize: true);

// Search
$results = Brinicle::search('products', $queryVector, k: 5);

// Use a specific connection
Brinicle::connection('analytics')->search('events', $queryVector, k: 10);

Via Dependency Injection

Inject the client directly into your services for better testability:
use Bicardinal\Brinicle\BrinicleClient;

class ProductSearchService
{
    public function __construct(
        private BrinicleClient $brinicle
    ) {}

    public function search(string $query, int $k = 10): array
    {
        $this->brinicle->init('products', 'build');
        // ... ingest products
        $this->brinicle->finalize('products');
        return $this->brinicle->search('products', $queryVector, k: $k);
    }
}

Via Manager

The BrinicleManager provides connection management:
use Bicardinal\BrinicleLaravel\BrinicleManager;

$manager = app(BrinicleManager::class);

// Get the default connection
$client = $manager->connection();

// Get a named connection
$analyticsClient = $manager->connection('analytics');

// Available manager methods
$manager->getDefaultConnection();     // 'default'
$manager->purge('analytics');         // Remove cached connection

ItemSearch

The ItemSearch API provides structured search over items with lexical fields, combining vector similarity with metadata filtering. This is especially useful in Laravel applications that manage product catalogs, article repositories, or any Eloquent model collection where you need to search by both content and structured attributes. The Laravel facade exposes all ItemSearch methods with the same clean interface as the vector engine methods.

Creating an Item Index

Use the Brinicle facade to create an item search index with a lexical configuration. The lexical configuration determines which fields are searchable and how text is analyzed. You can define the configuration inline or store it in your config/brinicle.php file for reuse across multiple indexes.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;
use Bicardinal\Brinicle\Models\HNSWParams;
use Bicardinal\Brinicle\Models\LexicalConfig;

$lexicalConfig = new LexicalConfig(
    searchableFields: ['title', 'description', 'category'],
    tokenizer: 'standard',
    lowercase: true,
    stem: false,
    removeStopWords: true,
);

Brinicle::createItemIndex('products', dim: 384, deltaRatio: 0.10, params: new HNSWParams(M: 48, efConstruction: 1024, efSearch: 512), lexicalConfig: $lexicalConfig);

Ingesting Items

Initialize an ingest session, then ingest items with their vectors and metadata fields. Each item consists of an external ID, a vector matching the index dimension, and an associative array of string fields. Fields listed in the searchableFields of your LexicalConfig will be indexed for full-text search, while other fields are stored but not searchable. After all items are ingested, call finalize to build the index.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

Brinicle::initItemIngest('products', 'build');

Brinicle::ingestItem('products', 'p1', $vector1, [
    'title' => 'Apple iPhone 15 Pro Max',
    'description' => 'Latest iPhone with A17 Pro chip',
    'category' => 'Electronics',
    'price' => '1199',
]);

Brinicle::ingestItem('products', 'p2', $vector2, [
    'title' => 'Samsung Galaxy S24 Ultra',
    'description' => 'Flagship Android with AI features',
    'category' => 'Electronics',
    'price' => '1299',
]);

Brinicle::finalize('products', optimize: true);

Searching Items

Search for items using a text query with optional structured filters. The searchItems method combines full-text matching against your searchable fields with vector similarity, returning results ranked by relevance. You can narrow results using ItemSearchFilters to require certain field values or exclude others.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;
use Bicardinal\Brinicle\Models\ItemSearchFilters;

// Simple text search
$results = Brinicle::searchItems('products', query: 'iphone', k: 10);

// Search with filters
$filters = new ItemSearchFilters(
    must: ['category' => 'Electronics'],
    mustNot: ['price' => ['0', '100']],
);

$results = Brinicle::searchItems('products', query: 'smartphone', filters: $filters, k: 10, efs: 64);

Deleting and Monitoring Item Indexes

You can close or permanently destroy item indexes, and check their status at any time. Closing an index preserves its data on disk for later reloading, while destroying it permanently removes all data. The status check returns the index name, dimension, and whether a rebuild is needed after recent ingestions.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

// Check status
$status = Brinicle::getItemIndexStatus('products');

// Close (preserve data)
Brinicle::deleteItemIndex('products');

// Permanently destroy
Brinicle::deleteItemIndex('products', destroy: true);

Autocomplete

The Autocomplete API provides prefix-based search for type-ahead and suggestion use cases. Autocomplete indexes are optimized for fast prefix matching, making them ideal for building search suggestion dropdowns, tag autocomplete fields, and any interface where users type partial queries and expect instant results. In Laravel, you can use the facade or dependency injection to manage autocomplete indexes alongside your vector and item search indexes.

Creating an Autocomplete Index

Create an autocomplete index with an optional configuration that controls suggestion matching and ranking behavior. The configuration lets you set the minimum prefix length before suggestions appear, the maximum number of suggestions returned, and whether fuzzy matching is enabled for typo tolerance. These settings can be tuned per-index depending on the use case.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;
use Bicardinal\Brinicle\Models\HNSWParams;
use Bicardinal\Brinicle\Models\AutocompleteConfig;

$autocompleteConfig = new AutocompleteConfig(
    minPrefixLength: 1,
    maxSuggestions: 10,
    fuzzyMatch: true,
    fuzzyDistance: 2,
);

Brinicle::createAutocompleteIndex('product_suggestions', dim: 384, deltaRatio: 0.10, params: new HNSWParams(M: 48, efConstruction: 1024, efSearch: 512), autocompleteConfig: $autocompleteConfig);

Ingesting Autocomplete Entries

Initialize an autocomplete ingest session, then ingest entries consisting of a key string and its associated vector. The key is the text that users will search for by prefix — for example, a product name. The vector provides the semantic representation used to rank suggestions when multiple prefix matches are found. After all entries are ingested, call finalize to commit the data and build the prefix index.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

Brinicle::initAutocompleteIngest('product_suggestions', 'build');

Brinicle::ingestAutocomplete('product_suggestions', 'iphone 15 pro max', $vector1);
Brinicle::ingestAutocomplete('product_suggestions', 'samsung galaxy s24 ultra', $vector2);
Brinicle::ingestAutocomplete('product_suggestions', 'google pixel 8 pro', $vector3);

Brinicle::finalize('product_suggestions', optimize: true);

Searching Autocomplete Suggestions

Search for autocomplete suggestions by providing a partial query string. Results are ranked by a combination of prefix match quality and vector similarity, ensuring that the most relevant suggestions appear first. The method is designed for sub-millisecond response times, making it suitable for interactive type-ahead interfaces in your Laravel front-end.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

$results = Brinicle::searchAutocomplete('product_suggestions', query: 'iph', k: 5);
// ['iphone 15 pro max', 'iphone 15 case', 'iphone charger']

Deleting and Monitoring Autocomplete Indexes

Manage autocomplete indexes by closing or destroying them, and checking their status. The status check returns the index name, dimension, whether the prefix index is built, and whether a rebuild is needed. This is particularly important for autocomplete indexes because the prefix index must be rebuilt after ingestion to reflect new entries.
use Bicardinal\BrinicleLaravel\Facades\Brinicle;

// Check status
$status = Brinicle::getAutocompleteIndexStatus('product_suggestions');

// Close (preserve data)
Brinicle::deleteAutocompleteIndex('product_suggestions');

// Permanently destroy
Brinicle::deleteAutocompleteIndex('product_suggestions', destroy: true);

Artisan Commands

The Laravel SDK includes Artisan commands for common operations across vector, item search, and autocomplete indexes:

Create Index

php artisan brinicle:index-create products 384
php artisan brinicle:index-create products 384 --M=48 --ef-construction=1024 --ef-search=512 --delta-ratio=0.1

List Indexes

php artisan brinicle:index-list
Output:
+------------+-------+
| Index Name | Count |
+------------+-------+
| products   | 1     |
| users      | 1     |
+------------+-------+

Index Status

php artisan brinicle:index-status products
Output:
+----------------+-------+
| Property       | Value |
+----------------+-------+
| Index Name     | products |
| Dimension      | 384   |
| Has Index      | Yes   |
| Needs Rebuild  | No    |
+----------------+-------+

Delete Index

# Close the index (data preserved)
php artisan brinicle:index-delete products

# Permanently destroy the index
php artisan brinicle:index-delete products --destroy
All commands support the --connection= option for multi-connection setups:
php artisan brinicle:index-create products 384 --connection=analytics

Create Item Index

Create a new item search index with lexical configuration for full-text search over structured items. The --searchable-fields option accepts a comma-separated list of field names that should be indexed for text search. Additional options control the lexical analyzer behavior including tokenization, stemming, and stop-word removal.
php artisan brinicle:item-index-create products 384
php artisan brinicle:item-index-create products 384 --searchable-fields=title,description,category --tokenizer=standard --lowercase --remove-stop-words
Search for items in an item search index using a text query. The command prints the matching items with their IDs, scores, and matched fields. You can apply structured filters using the --must and --must-not options to narrow results by field values.
php artisan brinicle:item-search products "iphone"
php artisan brinicle:item-search products "smartphone" --must=category:Electronics --k=10
Output:
+------+-------+---------------------------+
| ID   | Score | Title                     |
+------+-------+---------------------------+
| p1   | 0.95  | Apple iPhone 15 Pro Max   |
| p5   | 0.82  | iPhone 15 Case            |
+------+-------+---------------------------+

Item Ingest

Ingest items into an item search index from a JSON file. The file should contain an array of objects, each with an id, vector, and fields key. This is useful for bulk-loading product catalogs or document collections from Eloquent model exports or external data sources.
php artisan brinicle:item-ingest products items.json
php artisan brinicle:item-ingest products items.json --mode=upsert
The JSON file format:
[
  {
    "id": "p1",
    "vector": [0.1, 0.2, 0.3],
    "fields": {
      "title": "Apple iPhone 15 Pro Max",
      "category": "Electronics"
    }
  }
]

Create Autocomplete Index

Create a new autocomplete index for prefix-based suggestion search. The --min-prefix-length option sets how many characters a user must type before suggestions appear, and --fuzzy-match enables typo tolerance with a configurable edit distance.
php artisan brinicle:autocomplete-index-create product_suggestions 384
php artisan brinicle:autocomplete-index-create product_suggestions 384 --min-prefix-length=2 --max-suggestions=10 --fuzzy-match --fuzzy-distance=2
Search for autocomplete suggestions matching a prefix query. This command is useful for testing your autocomplete indexes from the command line or for building CLI-based search tools.
php artisan brinicle:autocomplete-search product_suggestions "iph"
php artisan brinicle:autocomplete-search product_suggestions "iph" --k=5
Output:
+---------------------------+
| Suggestion                |
+---------------------------+
| iphone 15 pro max         |
| iphone 15 case            |
| iphone charger            |
+---------------------------+

Autocomplete Ingest

Ingest autocomplete entries from a JSON file. Each entry should have a key (the suggestion text) and a vector (the semantic representation). This command handles the full ingest lifecycle including initialization and finalization.
php artisan brinicle:autocomplete-ingest product_suggestions suggestions.json
php artisan brinicle:autocomplete-ingest product_suggestions suggestions.json --mode=insert
The JSON file format:
[
  {
    "key": "iphone 15 pro max",
    "vector": [0.1, 0.2, 0.3]
  }
]

Error Handling

use Bicardinal\Brinicle\Exceptions\BrinicleException;

try {
    Brinicle::createIndex('products', dim: 384);
} catch (BrinicleException $e) {
    Log::error('Brinicle error', [
        'status' => $e->statusCode,
        'message' => $e->getMessage(),
    ]);
}