Laravel 8 compatible AI assistant scaffold.

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-07-02 18:12:23 UTC


README

This package provides a Laravel-compatible AI Assistant infrastructure designed to integrate LLMs (e.g., OpenRouter, Gemini) with analytical tools and guardrails.

Features

  • Multi-Provider Support: Adapter system supporting OpenRouter and Google Gemini APIs.
  • Dynamic Tool Discovery: Scans and loads analytical tools automatically.
  • Guardrail Pipeline: Pre-execution system instructions (InstructionGuardrail) and run-time safety check filters (RuntimeGuardrail).
  • Interactive Livewire Integration: Dynamic, user-friendly client-side chat interface.

Installation

Add the local repository to your host project's composer.json file:

"repositories": [
    {
        "type": "path",
        "url": "packages/Tobiebenezer/Ai"
    }
]

Then install the package:

composer require tobiebenezer/php-ai

Configuration & Migrations

Publish the configuration file, 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 configuration table:

php artisan migrate

How to Write Custom AI Tools

The package discovers all classes implementing Tobiebenezer\Ai\Contracts\Tool inside the configured path (default: app/Ai/Tools/).

Extending AnalyticalTool

For database-backed, analytical querying tools, you should extend the abstract Tobiebenezer\Ai\Tools\AnalyticalTool class. This provides built-in query building, grouping, formatting, filter validation, and aggregation.

Here is an example tool that allows the AI to query a Sales table:

<?php

namespace App\Ai\Tools;

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

class QuerySalesTool extends AnalyticalTool
{
    /**
     * Define the target Eloquent model.
     */
    protected function modelClass()
    {
        return Sale::class;
    }

    /**
     * List of columns the AI can filter results by.
     */
    protected function filterableColumns()
    {
        return ['branch_id', 'staff_id', 'status_id'];
    }

    /**
     * List of columns the AI is allowed to group results by.
     */
    protected function groupableColumns()
    {
        return ['branch_id', 'staff_id', 'status_id'];
    }

    /**
     * List of columns that support math aggregation (sum, avg, etc.).
     */
    protected function aggregateableColumns()
    {
        return ['total', 'discount'];
    }

    /**
     * Default SELECT array returned to the AI.
     */
    protected function defaultSelects()
    {
        return [
            'sales.id',
            'sales.total',
            'sales.discount',
            'sales.created_at',
            'branches.name as branch_name',
        ];
    }

    /**
     * SQL joins needed for select/filter clauses.
     */
    protected function joins()
    {
        return [
            ['branches', 'sales.branch_id', '=', 'branches.id'],
        ];
    }

    /**
     * User-facing description explaining to the LLM what this tool does.
     */
    public function description()
    {
        return 'Query sales transactions with totals, discounts, and branch associations.';
    }
}

Implementing Tool directly

If you need a tool that does not query the database (e.g. calling an external API), implement Tobiebenezer\Ai\Contracts\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 API...
        return ['temp' => '27C', 'condition' => 'Sunny'];
    }
}

Guardrails

Guardrails monitor prompt context and run-time actions:

  1. InstructionGuardrail: Prepends context-specific instructions to the LLM prompt before execution.
  2. RuntimeGuardrail: Triggers events during execution phases (BEFORE_PROVIDER_REQUEST, BEFORE_TOOL_CALL, AFTER_TOOL_RESULT, BEFORE_FINAL_RESPONSE) to allow sanitizing inputs or denying requests.

Custom Guardrail Example

<?php

namespace App\Ai\Guardrails;

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

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

    public function check(GuardrailEvent $event, GuardrailContext $context)
    {
        if ($event->phase === GuardrailEvent::BEFORE_PROVIDER_REQUEST) {
            // Check payload text...
        }
        return GuardrailDecision::allow();
    }
}

Overriding Package Guardrails

If you want to customize or replace a default package guardrail (such as CapabilitiesGuardrail), you can override it in your application using one of the following two approaches:

Method 1: Container Binding (Recommended)

You can register an override in your application's AppServiceProvider so that the Laravel container resolves your custom guardrail class whenever the package asks for the default one.

// 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);
}

Method 2: Configuration Replacement

Alternatively, publish the configuration file (config/ai.php) and swap the package guardrail class in the guardrails.global or guardrails.groups array with your custom class:

// config/ai.php
'guardrails' => [
    'global' => [
        // Replace base guardrail with custom class
        \App\Ai\Guardrails\CustomCapabilitiesGuardrail::class,
        
        \Tobiebenezer\Ai\Guardrails\MaxToolCallsGuardrail::class,
        \Tobiebenezer\Ai\Guardrails\ReadOnlyToolsGuardrail::class,
        \Tobiebenezer\Ai\Guardrails\HtmlStyleGuardrail::class,
        \Tobiebenezer\Ai\Guardrails\PromptSanitizerGuardrail::class,
    ],
];

Your custom guardrail must implement the respective contract (Tobiebenezer\Ai\Contracts\InstructionGuardrail or Tobiebenezer\Ai\Contracts\RuntimeGuardrail):

<?php

namespace App\Ai\Guardrails;

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

class CustomCapabilitiesGuardrail implements InstructionGuardrail
{
    public function appliesTo(GuardrailContext $context)
    {
        return true;
    }

    public function instructions(GuardrailContext $context)
    {
        return [
            "Your custom capabilities instructions override here...",
        ];
    }
}

🛠️ Advanced Features

1. Developer Artisan Generators

Automatically bootstrap your package assets using standard Artisan console commands:

  • Create an AI Tool: php artisan make:ai-tool {Name} (add --analytical or -a for database-backed tools).
  • Create a Guardrail: php artisan make:ai-guardrail {Name} (add --runtime or -r for runtime phase checking).
  • Create a Provider: php artisan make:ai-provider {Name}.

2. Request & Tool Call Audit Logs

Run the migrations to create the request log tables:

php artisan vendor:publish --tag="ai-migrations"
php artisan migrate

The package automatically logs:

  • ai_request_logs: Prompts, final assistant content, token counts, and request-level execution latency.
  • ai_tool_call_logs: Executed tools, input arguments, returned outputs, execution status, latencies, and exception logs.

3. Structured Outputs (JSON Schema)

Force the model to respond in structured JSON format conforming to a schema:

$response = $assistant->respond(new AiRequest([
    'profile' => 'openrouter',
    'messages' => [
        AiMessage::user('Find profile details for John Doe')
    ],
    'response_schema' => [
        'type' => 'object',
        'properties' => [
            'name' => ['type' => 'string'],
            'age' => ['type' => 'integer'],
        ],
        'required' => ['name', 'age'],
    ]
]));

4. Monthly Token Budget

Prevent cost overruns by setting monthly limits in config/ai.php:

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

When exceeded, requests are blocked automatically and throw a GuardrailException.

5. Self-Healing Exception Recovery

If a tool execution fails (e.g., database query exceptions or column errors), the package intercepts the exception and feeds the error details back to the LLM as the tool result. The LLM can analyze the schema failure, correct its query arguments, and retry successfully.