PHPackages                             ami-praha/ai-item-finder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ami-praha/ai-item-finder

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ami-praha/ai-item-finder
========================

Laravel package for finding the most similar item from a list using LLM

v1.0.5(4mo ago)2177—0%MITPHPPHP ^8.1|^8.2|^8.3|^8.4|^8.5

Since Oct 29Pushed 4mo agoCompare

[ Source](https://github.com/AMI-Praha-a-s/ai-item-finder)[ Packagist](https://packagist.org/packages/ami-praha/ai-item-finder)[ RSS](/packages/ami-praha-ai-item-finder/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (6)Dependencies (5)Versions (7)Used By (0)

AI Item Finder
==============

[](#ai-item-finder)

A Laravel package that uses Large Language Models (LLM) to find the closest matching item from a list. This package leverages OpenAI's GPT models to perform intelligent matching based on semantic similarity rather than simple string matching.

Features
--------

[](#features)

- Find the most similar item from a list using AI
- Configurable OpenAI models (GPT-4.1, GPT-5, etc.)
- Easy-to-use fluent interface
- Laravel facade support
- Customizable system instructions
- Confidence scoring with AI-powered match evaluation
- Optional "no result" mode to return `null` when confidence is below threshold
- List description support for more accurate matching context

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- Laravel 10.x or 11.x
- OpenAI API key

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require ami-praha/ai-item-finder
```

Publish the configuration file:

```
php artisan vendor:publish --tag=ai-item-finder-config
```

Add your OpenAI API key to your `.env` file:

```
AI_ITEM_FINDER_OPENAI_API_KEY=your-api-key-here
```

Optionally, you can also set the model:

```
AI_ITEM_FINDER_OPENAI_MODEL=gpt-4.1-mini
```

Usage
-----

[](#usage)

### Basic Usage

[](#basic-usage)

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$list = [
    ['name' => 'Apple iPhone 14'],
    ['name' => 'Samsung Galaxy S23'],
    ['name' => 'Google Pixel 7'],
];

$result = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'iPhone 14 Pro')
    ->find();

// Returns the closest matching item from the list
```

### Using the Service Class Directly

[](#using-the-service-class-directly)

```
use AmiPraha\AiItemFinder\AiItemFinder;

$finder = new AiItemFinder();

$list = [
    ['city' => 'Praha'],
    ['city' => 'Brno'],
    ['city' => 'Ostrava'],
];

$result = $finder->setList($list)
    ->setSearchedItem('city', 'Prague')
    ->find();

// Will match 'Praha' as it's semantically similar to 'Prague'
```

### Advanced Usage with Custom Instructions

[](#advanced-usage-with-custom-instructions)

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$list = [
    ['model' => 'Tesla Model 3', 'type' => 'electric'],
    ['model' => 'BMW X5', 'type' => 'suv'],
    ['model' => 'Toyota Camry', 'type' => 'sedan'],
];

$result = AiItemFinder::setList($list)
    ->setSearchedItem('model', 'Model S')
    ->setAdditionalInstructions('Match based on brand is sufficient, no need to strictly match the model.')
    ->find();

// Will match 'Tesla Model 3' because of brand similarity
```

### Using Different Models

[](#using-different-models)

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$result = AiItemFinder::setList($list)
    ->setSearchedItem('product', 'laptop')
    ->setModel('gpt-5') // Use a more powerful model
    ->find();
```

### Custom System Message

[](#custom-system-message)

If you need complete control over the AI's behavior, you can set a custom system message:

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$result = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'search term')
    ->setCustomSystemMessage('You are an expert at matching products. Find the most similar item.')
    ->find();
```

### Using List Description for Better Accuracy

[](#using-list-description-for-better-accuracy)

You can provide a description of what the list items represent to help the AI make more accurate matches:

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$list = [
    ['code' => 'NYC', 'name' => 'New York City'],
    ['code' => 'LAX', 'name' => 'Los Angeles'],
    ['code' => 'ORD', 'name' => 'Chicago'],
];

$result = AiItemFinder::setList($list)
    ->setDescriptionOfList('Airport codes and their corresponding cities')
    ->setSearchedItem('name', 'New York')
    ->find();

// Will match 'NYC' as its New York airport
```

### Handling Low-Confidence Matches

[](#handling-low-confidence-matches)

By default, the package evaluates match confidence and returns `null` if the match confidence is below 80%. You can customize this behavior:

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

// Allow low-confidence matches above 50% to be returned
$result = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'search term')
    ->setNoResultConfidenceThreshold(50) // Set threshold to 50%
    ->find();

// $result will be null if confidence score is below 50%
```

```
// Always return the closest match, even if confidence is low
$result = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'search term')
    ->setAllowNoResult(false)
    ->find();

// $result will always contain the closest match, never null
```

Configuration
-------------

[](#configuration)

The configuration file `config/ai-item-finder.php` contains the following options:

```
return [
    // Your OpenAI API key
    'openai_api_key' => env('AI_ITEM_FINDER_OPENAI_API_KEY'),

    // The model to use (default: gpt-4.1-mini)
    'model' => env('AI_ITEM_FINDER_OPENAI_MODEL', 'gpt-4.1-mini'),

    // The API endpoint URL
    'api_url' => env('AI_ITEM_FINDER_OPENAI_API_URL', 'https://api.openai.com/v1/chat/completions'),
];
```

Performance Considerations
--------------------------

[](#performance-considerations)

### Two-Step Confidence Evaluation

[](#two-step-confidence-evaluation)

By default, when confidence scoring is enabled (`setAllowNoResult(true)`), the package uses a **two-step approach**:

1. **First API call**: Finds the best matching item from your list
2. **Second API call**: Evaluates the confidence score for that specific match

This separation provides more accurate and unbiased confidence scores because:

- The matching task focuses solely on finding the best relative match
- The confidence evaluation provides an independent, absolute quality assessment
- Better calibrated scores (avoiding confirmation bias where the LLM justifies its own choice)

However, this comes with performance tradeoffs:

### Cost Implications

[](#cost-implications)

- **With confidence scoring enabled** (`setAllowNoResult(true)`): 2 API calls per search
- **With confidence scoring disabled** (`setAllowNoResult(false)`): 1 API call per search

**Example costs with gpt-4o-mini** (approximate):

- Single search with confidence: ~$0.0002 - $0.002
- Single search without confidence: ~$0.0001 - $0.001
- 1,000 searches/day with confidence: ~$0.20 - $2.00/day
- 1,000 searches/day without confidence: ~$0.10 - $1.00/day

### Latency Implications

[](#latency-implications)

- **With confidence scoring**: ~2-4 seconds (two sequential API calls)
- **Without confidence scoring**: ~1-2 seconds (single API call)

### Optimizing for High-Volume Scenarios

[](#optimizing-for-high-volume-scenarios)

If you're processing many searches or need faster response times, disable confidence scoring:

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$result = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'search term')
    ->setAllowNoResult(false) // Disable confidence scoring for better performance
    ->find();

// Result will always return the best match, using only 1 API call
```

### When to Use Confidence Scoring

[](#when-to-use-confidence-scoring)

**Enable confidence scoring when:**

- Quality and accuracy are more important than speed/cost
- You need to filter out poor matches (e.g., data validation)
- You're making high-value decisions based on matches
- Search volume is low to moderate
- You need access to confidence scores for logging/analytics

**Disable confidence scoring when:**

- You need fast response times (real-time user interactions)
- You're processing high volumes (thousands of searches)
- You trust the AI to always pick the best available match
- Cost optimization is a priority
- You're operating in a latency-sensitive environment

### Accessing Confidence Data

[](#accessing-confidence-data)

When confidence scoring is enabled, you can access the score, reasoning, and the matched item that was evaluated after finding a match:

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;

$finder = AiItemFinder::setList($list)
    ->setSearchedItem('name', 'search term')
    ->setAllowNoResult(true);

$result = $finder->find();

if ($result !== null) {
    $score = $finder->getConfidenceScore(); // 0-100
    $reasoning = $finder->getConfidenceReasoning(); // Explanation text
    $matchedItem = $finder->getConfidenceEvaluationMatchedItem(); // The matched item that was evaluated

    Log::info("Match found with {$score}% confidence: {$reasoning}");
    Log::info("Matched item: " . json_encode($matchedItem));
}
```

Available Methods
-----------------

[](#available-methods)

### `setList(array $list)`

[](#setlistarray-list)

Set the list of items to search through.

### `setSearchedItem(string $key, mixed $value)`

[](#setsearcheditemstring-key-mixed-value)

Set the item you're searching for. The key is used for context, and the value is what will be matched.

### `setDescriptionOfList(string $description)`

[](#setdescriptionofliststring-description)

Set a description of what the list items represent. This provides additional context to the AI for more accurate matching.

### `setAdditionalInstructions(string $instructions)`

[](#setadditionalinstructionsstring-instructions)

Add additional instructions to guide the AI's matching behavior.

### `setCustomSystemMessage(string $message)`

[](#setcustomsystemmessagestring-message)

Override the default system message entirely.

### `setAllowNoResult(bool $allowNoResult = true)`

[](#setallownoresultbool-allownoresult--true)

Set whether to allow `null` to be returned if the confidence score is below the threshold. When set to `true` (default), the `find()` method will return `null` if the best match has a confidence score below the threshold set by `setNoResultConfidenceThreshold()`. When set to `false`, the closest match will always be returned regardless of confidence.

### `setNoResultConfidenceThreshold(int $threshold)`

[](#setnoresultconfidencethresholdint-threshold)

Set the minimum confidence threshold (0-100) required for returning a match. Default is 80%. This threshold only applies when `setAllowNoResult(true)` is set. If the best match has a confidence score below this threshold, `null` will be returned instead.

### `setModel(string $model)`

[](#setmodelstring-model)

Set the OpenAI model to use (e.g., 'gpt-4.1', 'gpt-4.1-mini', 'gpt-5').

### `find()`

[](#find)

Execute the search and return the closest matching item. Returns `array|null` - the matched item from the list, or `null` if no sufficiently confident match is found (when `allowNoResult` is `true` and confidence is below the threshold).

### `getConfidenceScore()`

[](#getconfidencescore)

Get the confidence score (0-100) of the last match. Returns `int|null` - the confidence score, or `null` if no match has been performed yet or if confidence scoring was disabled (`setAllowNoResult(false)`). This method should be called after `find()`.

### `getConfidenceReasoning()`

[](#getconfidencereasoning)

Get the reasoning behind the confidence score of the last match. Returns `string|null` - a text explanation of why the confidence score was assigned, or `null` if no match has been performed yet or if confidence scoring was disabled. This method should be called after `find()`.

### `getConfidenceEvaluationMatchedItem()`

[](#getconfidenceevaluationmatcheditem)

Get the matched item that was used for the confidence evaluation. Returns `array|null` - the matched item that was evaluated for confidence scoring, or `null` if no match has been performed yet or if confidence scoring was disabled (`setAllowNoResult(false)`). This method should be called after `find()`. Note that this returns the same item as `find()` when a match is found, but provides access to it even when examining confidence data.

### `getModel()`

[](#getmodel)

Get the OpenAI model being used. Returns `string` - the OpenAI model name (e.g., 'gpt-4.1-mini', 'gpt-4o', 'gpt-5').

### `getApiUrl()`

[](#getapiurl)

Get the OpenAI API URL. Returns `string` - the API endpoint URL.

### `getList()`

[](#getlist)

Get the list of items to search through. Returns `array` - the array of items that was set via `setList()`.

### `getDescriptionOfList()`

[](#getdescriptionoflist)

Get the description of the list. Returns `string|null` - the description of what the list items represent, or `null` if not set.

### `getSearchedItemKey()`

[](#getsearcheditemkey)

Get the searched item key. Returns `string|null` - the key name for the searched item, or `null` if not set.

### `getSearchedItemValue()`

[](#getsearcheditemvalue)

Get the searched item value. Returns `mixed` - the value being searched for, or `null` if not set.

### `getAdditionalInstructions()`

[](#getadditionalinstructions)

Get the additional instructions. Returns `string|null` - the additional instructions for the AI, or `null` if not set.

### `getSystemMessage()`

[](#getsystemmessage)

Get the complete system message that will be sent to the AI. Returns `string|null` - the constructed system message including custom message, list description, and additional instructions.

### `getAllowNoResult()`

[](#getallownoresult)

Get whether no result is allowed. Returns `bool` - whether `null` results are allowed when confidence is low.

### `getNoResultConfidenceThreshold()`

[](#getnoresultconfidencethreshold)

Get the no result confidence threshold. Returns `int` - the confidence threshold (0-100) below which `null` is returned.

Error Handling
--------------

[](#error-handling)

The package provides specific exception classes for different error scenarios:

### Exception Types

[](#exception-types)

**`InvalidConfigurationException`** - Thrown when configuration is invalid:

- Missing OpenAI API key
- Invalid confidence threshold (not between 0-100)

**`InvalidInputException`** - Thrown when input data is invalid:

- Empty list
- Missing searched item

**`InvalidApiResponseException`** - Thrown when the API returns unexpected data:

- API request errors (authentication, rate limits, etc.)
- Empty or malformed responses
- AI returns item not in the provided list

All exceptions extend the base `AiItemFinderException` class, which extends PHP's standard `Exception`.

### Usage Examples

[](#usage-examples)

```
use AmiPraha\AiItemFinder\Facades\AiItemFinder;
use AmiPraha\AiItemFinder\Exceptions\InvalidConfigurationException;
use AmiPraha\AiItemFinder\Exceptions\InvalidInputException;
use AmiPraha\AiItemFinder\Exceptions\InvalidApiResponseException;

try {
    $result = AiItemFinder::setList($list)
        ->setSearchedItem('name', 'search term')
        ->find();

    if ($result === null) {
        // No confident match found
        Log::info('No confident match found for the search term');
    } else {
        // Process the matched item
        Log::info('Found match: ' . json_encode($result));
    }
} catch (InvalidConfigurationException $e) {
    // Handle configuration errors (API key, invalid thresholds)
    Log::error('Configuration error: ' . $e->getMessage());
} catch (InvalidInputException $e) {
    // Handle input validation errors (empty list, missing search item)
    Log::error('Invalid input: ' . $e->getMessage());
} catch (InvalidApiResponseException $e) {
    // Handle API errors (authentication, rate limits, malformed responses)
    Log::error('API error: ' . $e->getMessage());
}
```

You can also catch all package exceptions using the base exception class:

```
use AmiPraha\AiItemFinder\Exceptions\AiItemFinderException;

try {
    $result = AiItemFinder::setList($list)
        ->setSearchedItem('name', 'search term')
        ->find();
} catch (AiItemFinderException $e) {
    // Handle any exception from the package
    Log::error('Item search failed: ' . $e->getMessage());
}
```

Use Cases
---------

[](#use-cases)

- Matching user input to predefined options
- Finding similar products in a catalog
- Mapping inconsistent data to standardized values
- Intelligent autocomplete and suggestions
- Data normalization and deduplication
- Fuzzy matching with confidence thresholds for quality control
- Semantic search across categorized data with contextual descriptions

License
-------

[](#license)

MIT License

Author
------

[](#author)

AMI Praha a.s.

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance77

Regular maintenance activity

Popularity17

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~14 days

Total

6

Last Release

126d ago

PHP version history (2 changes)v1.0.0PHP ^8.1|^8.2|^8.3

v1.0.1PHP ^8.1|^8.2|^8.3|^8.4|^8.5

### Community

Maintainers

![](https://www.gravatar.com/avatar/d45ab5a4ad71dfe6e6c93f8b2735aceea7ca59361d6b4b3e5835f46443b3d5b2?d=identicon)[AmiPraha](/maintainers/AmiPraha)

---

Top Contributors

[![desthercz](https://avatars.githubusercontent.com/u/25509616?v=4)](https://github.com/desthercz "desthercz (8 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ami-praha-ai-item-finder/health.svg)

```
[![Health](https://phpackages.com/badges/ami-praha-ai-item-finder/health.svg)](https://phpackages.com/packages/ami-praha-ai-item-finder)
```

###  Alternatives

[glhd/conveyor-belt

14797.0k](/packages/glhd-conveyor-belt)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)[ashallendesign/favicon-fetcher

A Laravel package for fetching website's favicons.

190272.4k3](/packages/ashallendesign-favicon-fetcher)[beyondcode/laravel-favicon

Create dynamic favicons based on your environment settings.

37345.5k](/packages/beyondcode-laravel-favicon)[laragear/preload

Effortlessly make a Preload script for your Laravel application.

119363.5k](/packages/laragear-preload)[ankurk91/laravel-ses-webhooks

Handle AWS SES webhooks in Laravel php framework

2534.2k](/packages/ankurk91-laravel-ses-webhooks)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
