Search by

Modern Laravel AI assistant framework with multi-provider, multimodal vision, tool execution, and guardrails.

Maintainers

Package info

github.com/tobiebenezer/php-ai

pkg:composer/tobiebenezer/php-ai

Transparency log

Statistics

Installs: 7

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.9 2026-07-02 18:12 UTC

This package is auto-updated.

Last update: 2026-09-03 08:58:03 UTC


README

Packagist Version PHP Version Laravel Compatibility License

A Laravel package that connects LLMs (OpenRouter, Google Gemini) to database queries, vision models, structured JSON schemas, and safety guardrails.

Key features

  • Multi-provider support: OpenRouter and Google Gemini adapters with profile-based configuration.
  • Multimodal vision: analyze images, receipts, and documents directly through vision models.
  • Tool discovery and execution: automatically scans and runs custom tools with conversational loops and error recovery.
  • Analytical database queries: extend AnalyticalTool to query Eloquent models with column filtering, grouping, and aggregations.
  • Guardrails: run pre-prompt instructions and lifecycle checks before and after tool calls.
  • Dynamic runtime settings: change providers, API keys, and models from database settings without editing .env.
  • Structured JSON outputs: return responses matching your JSON schema.
  • Audit logging: record prompts, responses, tool calls, token usage, and latency in database tables.
  • Token budgeting: set monthly token limits to prevent runaway API spend.
  • Artisan generators: create tools, guardrails, and providers with CLI commands.
  • Livewire chat and queues: prebuilt chat component and asynchronous job handling.

Requirements and compatibility

  • PHP: 7.3, 8.0, 8.1, 8.2, or 8.3
  • Laravel / Illuminate: 8.x, 9.x, 10.x, or 11.x

Installation

Install the package with Composer:

composer require tobiebenezer/php-ai

If you are developing locally with a path repository, add this to your root composer.json:

"repositories": [
    {
        "type": "path",
        "url": "../php-ai"
    }
]

Configuration and migrations

Publish the configuration, migrations, and stubs:

# Publish everything
php artisan vendor:publish --provider="Tobiebenezer\Ai\AiServiceProvider"

# Or publish individually
php artisan vendor:publish --tag="ai-config"
php artisan vendor:publish --tag="ai-migrations"
php artisan vendor:publish --tag="ai-stubs"

Run the migrations to create the request and tool call log tables:

php artisan migrate

Configuration and environment

Add your provider keys to .env:

AI_PROFILE=openrouter

# OpenRouter
OPENROUTER_API_KEY=your-openrouter-key
OPENROUTER_MODEL=google/gemini-2.0-flash

# Google Gemini
GEMINI_API_KEY=your-gemini-key
GEMINI_MODEL=gemini-2.0-flash

For advanced settings, edit config/ai.php to define custom profiles, tools, guardrails, and limits.

Multimodal vision and document OCR

The package formats image payloads for both OpenRouter and Google Gemini without requiring separate code paths.

Basic image analysis

Use AiMessage::userWithImage($prompt, $base64Data, $mimeType):

use Tobiebenezer\Ai\AiAssistant;
use Tobiebenezer\Ai\DTO\AiMessage;
use Tobiebenezer\Ai\DTO\AiRequest;

$assistant = app(AiAssistant::class);

$imagePath = storage_path('app/receipts/receipt.jpg');
$base64Image = base64_encode(file_get_contents($imagePath));
$mimeType = mime_content_type($imagePath) ?: 'image/jpeg';

$message = AiMessage::userWithImage(
    'Describe this image and extract any visible text.',
    $base64Image,
    $mimeType
);

$response = $assistant->respond(new AiRequest([
    'messages' => [$message],
]));

echo $response->content;

Structured JSON extraction from images

Combine an image message with response_schema to extract structured data (such as bank receipts or invoices) as typed JSON:

$request = new AiRequest([
    'messages' => [
        AiMessage::userWithImage(
            'Extract the transaction reference, amount, and date from this receipt.',
            $base64Image,
            'image/jpeg'
        )
    ],
    'options' => [
        'temperature' => 0.1,
    ],
    'response_schema' => [
        'type' => 'object',
        'properties' => [
            'is_valid_receipt' => ['type' => 'boolean'],
            'bank_name'        => ['type' => ['string', 'null']],
            'session_id'       => ['type' => ['string', 'null']],
            'amount'           => ['type' => ['number', 'null']],
            'date'             => ['type' => ['string', 'null']],
        ],
        'required' => ['is_valid_receipt'],
    ],
]);

$response = $assistant->respond($request);
$data = json_decode($response->content, true);

$bankRef = $data['session_id'];
$amount = $data['amount'];

Dynamic runtime settings service

When users update API keys or switch providers from an admin dashboard, you can apply those changes at runtime without rewriting .env or restarting processes.

Set your service class in config/ai.php:

// config/ai.php
'settings_service' => \App\Services\Ai\AiSettingsService::class,

Implement an apply(): void method. The runner calls it before preparing each request:

<?php

namespace App\Services\Ai;

use App\Models\Config;
use Illuminate\Support\Facades\Cache;

class AiSettingsService
{
    public function apply(): void
    {
        $configs = Cache::remember('app_ai_configs', 3600, function () {
            return Config::where('category', 'ai')->pluck('value', 'tag');
        });

        $activeProvider = $configs['ai_provider'] ?? 'openrouter';

        config([
            'ai.default_profile' => $activeProvider,
            'ai.providers.openrouter.key' => $configs['ai_openrouter_key'] ?? env('OPENROUTER_API_KEY'),
            'ai.profiles.openrouter.model' => $configs['ai_openrouter_model'] ?? 'google/gemini-2.0-flash',
            'ai.providers.gemini.key'     => $configs['ai_gemini_key'] ?? env('GEMINI_API_KEY'),
            'ai.profiles.gemini.model'    => $configs['ai_gemini_model'] ?? 'gemini-2.0-flash',
        ]);
    }
}

Writing custom tools

The package discovers classes implementing Tobiebenezer\Ai\Contracts\Tool inside your tool directory (default: app/Ai/Tools/).

Extending AnalyticalTool

For database queries, extend Tobiebenezer\Ai\Tools\AnalyticalTool. It handles input validation, query building, joins, and aggregates:

<?php

namespace App\Ai\Tools;

use Tobiebenezer\Ai\Tools\AnalyticalTool;
use App\Models\Sale;

class QuerySalesTool extends AnalyticalTool
{
    protected function modelClass()
    {
        return Sale::class;
    }

    protected function filterableColumns()
    {
        return ['branch_id', 'staff_id', 'status_id'];
    }

    protected function groupableColumns()
    {
        return ['branch_id', 'staff_id', 'status_id'];
    }

    protected function aggregateableColumns()
    {
        return ['total', 'discount'];
    }

    protected function defaultSelects()
    {
        return [
            'sales.id',
            'sales.total',
            'sales.discount',
            'sales.created_at',
            'branches.name as branch_name',
        ];
    }

    protected function joins()
    {
        return [
            ['branches', 'sales.branch_id', '=', 'branches.id'],
        ];
    }

    public function description()
    {
        return 'Query sales transactions with totals, discounts, and branch associations.';
    }
}

Implementing Tool directly

For external API integrations (weather, maps, CRM lookups), implement Tool directly:

<?php

namespace App\Ai\Tools;

use Tobiebenezer\Ai\Contracts\Tool;
use Tobiebenezer\Ai\Guardrails\GuardrailContext;

class ExternalWeatherTool implements Tool
{
    public function name()
    {
        return 'get_weather';
    }

    public function description()
    {
        return 'Retrieve current weather for a city.';
    }

    public function schema()
    {
        return [
            'type' => 'object',
            'properties' => [
                'city' => ['type' => 'string', 'description' => 'The city name']
            ],
            'required' => ['city']
        ];
    }

    public function profiles()
    {
        return ['*'];
    }

    public function isReadOnly()
    {
        return true;
    }

    public function execute(array $arguments, GuardrailContext $context)
    {
        // Call external weather API
        return ['temp' => '27C', 'condition' => 'Sunny'];
    }
}

Guardrail pipeline

Guardrails check and restrict behavior before and during execution:

  1. InstructionGuardrail: appends instructions to the system prompt before execution.
  2. RuntimeGuardrail: intercepts execution phases (BEFORE_PROVIDER_REQUEST, BEFORE_TOOL_CALL, AFTER_TOOL_RESULT, BEFORE_FINAL_RESPONSE).

Custom guardrail example

<?php

namespace App\Ai\Guardrails;

use Tobiebenezer\Ai\Contracts\RuntimeGuardrail;
use Tobiebenezer\Ai\Guardrails\GuardrailContext;
use Tobiebenezer\Ai\Guardrails\GuardrailDecision;
use Tobiebenezer\Ai\Guardrails\GuardrailEvent;

class SensitiveDataBlockGuardrail implements RuntimeGuardrail
{
    public function appliesTo(GuardrailContext $context)
    {
        return true;
    }

    public function check(GuardrailEvent $event, GuardrailContext $context)
    {
        if ($event->phase === GuardrailEvent::BEFORE_PROVIDER_REQUEST) {
            // Inspect input for sensitive keywords
        }

        return GuardrailDecision::allow();
    }
}

Overriding default guardrails

To replace a default guardrail (such as CapabilitiesGuardrail), bind your class in AppServiceProvider:

// app/Providers/AppServiceProvider.php
use Tobiebenezer\Ai\Guardrails\CapabilitiesGuardrail as BaseCapabilities;
use App\Ai\Guardrails\CustomCapabilitiesGuardrail;

public function boot()
{
    $this->app->bind(BaseCapabilities::class, CustomCapabilitiesGuardrail::class);
}

Artisan generators

Use Artisan commands to scaffold components:

# Generate a general AI tool
php artisan make:ai-tool WeatherTool

# Generate an analytical Eloquent tool
php artisan make:ai-tool QuerySalesTool --analytical

# Generate a runtime guardrail
php artisan make:ai-guardrail BlockListGuardrail --runtime

# Generate a custom provider adapter
php artisan make:ai-provider AnthropicProvider

Audit logs

When logging is enabled in config/ai.php, the package records:

  • ai_request_logs: prompts, responses, model names, token counts, and latency.
  • ai_tool_call_logs: tool names, arguments, outputs, execution status, and timings.

Monthly token budget

Set a monthly token threshold in config/ai.php:

'budget' => [
    'monthly_token_limit' => 5000000,
],

When exceeded, requests stop and throw a GuardrailException.

Automatic error recovery

If a tool throws an error (such as an invalid column name in a query), the runner catches it and passes the diagnostic message back to the LLM. The model reads the error, adjusts its arguments, and retries the tool call.

Changelog

See CHANGELOG.md for version release notes.

License

Proprietary. All rights reserved.