PHPackages                             papi-ai/papi-core - 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. papi-ai/papi-core

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

papi-ai/papi-core
=================

Core classes for PapiAI - A simple but powerful PHP library for building AI agents

v0.9.1(2mo ago)41.3k13MITPHPPHP ^8.2CI passing

Since Jul 6Pushed 2mo agoCompare

[ Source](https://github.com/papi-ai/papi-core)[ Packagist](https://packagist.org/packages/papi-ai/papi-core)[ RSS](/packages/papi-ai-papi-core/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (9)Dependencies (8)Versions (27)Used By (13)

PapiAI Core
===========

[](#papiai-core)

[![CI](https://github.com/papi-ai/papi-core/workflows/CI/badge.svg)](https://github.com/papi-ai/papi-core/actions?query=workflow%3ACI) [![Latest Version](https://camo.githubusercontent.com/82d209274a2dabd63a2e0d9e5e04a48c246d4799fc23535bf1e3fcd31fb06242/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f706170692d61692f706170692d636f72652e737667)](https://packagist.org/packages/papi-ai/papi-core) [![Total Downloads](https://camo.githubusercontent.com/a837bd369ff0482e7f8911b66f4fe0d4c2b2ef7f505e36fcda8e0b804295eca8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f706170692d61692f706170692d636f72652e737667)](https://packagist.org/packages/papi-ai/papi-core) [![PHP Version](https://camo.githubusercontent.com/275ae85c1df69fe3a6b7fd6a86ed6bdc026607a7b757d419c9c509ed2ad2d7c6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f706170692d61692f706170692d636f72652e737667)](https://packagist.org/packages/papi-ai/papi-core) [![License](https://camo.githubusercontent.com/185459490f644e1b151e4a8241f4ed2f1ed8bce29b7d01a47db8090aeb8b7662/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f706170692d61692f706170692d636f72652e737667)](https://packagist.org/packages/papi-ai/papi-core)

A simple but powerful PHP library for building AI agents. Framework-agnostic, type-safe, and designed for real-world applications.

Features
--------

[](#features)

- **Framework-agnostic** - Works standalone, with Laravel, Symfony, or any PHP project
- **Multi-provider** - Supports Anthropic Claude, Google Gemini, and more
- **Tool calling** - Define tools as functions or class methods with attributes
- **Structured output** - Zod-like schema validation for LLM responses
- **Streaming** - First-class streaming support with events
- **Hooks** - Observability hooks for logging, metrics, and error handling
- **Type-safe** - Full PHP 8.2+ type hints and strict types

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

[](#installation)

```
composer require papi-ai/core
```

For providers, install the ones you need:

```
composer require papi-ai/anthropic  # For Claude
composer require papi-ai/google     # For Gemini
```

Quick Start
-----------

[](#quick-start)

```
use PapiAI\Core\Agent;
use PapiAI\Anthropic\AnthropicProvider;

$agent = new Agent(
    provider: new AnthropicProvider(apiKey: $_ENV['ANTHROPIC_API_KEY']),
    model: 'claude-sonnet-4-20250514',
    instructions: 'You are a helpful assistant.',
);

$response = $agent->run('What is 2 + 2?');
echo $response->text; // "4"
```

Adding Tools
------------

[](#adding-tools)

Tools let the agent perform actions and retrieve information.

### Function-based Tools

[](#function-based-tools)

```
use PapiAI\Core\Tool;

$weatherTool = Tool::make(
    name: 'get_weather',
    description: 'Get current weather for a city',
    parameters: [
        'city' => ['type' => 'string', 'description' => 'City name'],
    ],
    handler: fn(array $args) => fetchWeather($args['city']),
);

$agent = new Agent(
    provider: $provider,
    model: 'claude-sonnet-4-20250514',
    tools: [$weatherTool],
);

$response = $agent->run('What is the weather in London?');
```

### Class-based Tools with Attributes

[](#class-based-tools-with-attributes)

```
use PapiAI\Core\Tool;
use PapiAI\Core\Attributes\Tool as ToolAttr;
use PapiAI\Core\Attributes\Description;

class WebTools
{
    #[ToolAttr('Fetch content from a URL')]
    public function fetchUrl(
        #[Description('The URL to fetch')] string $url,
        #[Description('Timeout in seconds')] int $timeout = 30,
    ): string {
        return file_get_contents($url);
    }

    #[ToolAttr('Search the web')]
    public function search(string $query, int $limit = 10): array
    {
        // Implementation
    }
}

$agent = new Agent(
    provider: $provider,
    model: 'claude-sonnet-4-20250514',
    tools: Tool::fromClass(WebTools::class),
);
```

Structured Output
-----------------

[](#structured-output)

Use schemas to validate and parse LLM responses:

```
use PapiAI\Core\Schema\Schema;

$schema = Schema::object([
    'sentiment' => Schema::enum(['positive', 'negative', 'neutral']),
    'confidence' => Schema::number()->min(0)->max(1),
    'keywords' => Schema::array(Schema::string()),
]);

$response = $agent->run(
    prompt: 'Analyze: "Great product, highly recommend!"',
    options: ['outputSchema' => $schema],
);

// $response->data is validated
$response->data['sentiment'];   // 'positive'
$response->data['confidence'];  // 0.95
$response->data['keywords'];    // ['great', 'recommend']
```

### Schema Types

[](#schema-types)

```
Schema::string()              // String values
Schema::string()->min(1)      // Min length
Schema::string()->max(100)    // Max length
Schema::string()->pattern('/regex/')

Schema::number()              // Float values
Schema::integer()             // Integer values
Schema::number()->min(0)      // Minimum value
Schema::number()->max(100)    // Maximum value

Schema::boolean()             // Boolean values

Schema::array(Schema::string())      // Array of strings
Schema::array($itemSchema)->minItems(1)->maxItems(10)

Schema::object([              // Object with properties
    'name' => Schema::string(),
    'age' => Schema::integer()->optional(),
])

Schema::enum(['a', 'b', 'c']) // Enum values

// Modifiers
->nullable()                  // Allow null
->optional()                  // Not required in objects
->default('value')            // Default value
->description('...')          // Description for LLM
```

Streaming
---------

[](#streaming)

### Simple Text Streaming

[](#simple-text-streaming)

```
foreach ($agent->stream('Tell me a story') as $chunk) {
    echo $chunk->text;
    flush();
}
```

### Event Streaming

[](#event-streaming)

```
foreach ($agent->streamEvents('Use tools to help me') as $event) {
    match ($event->type) {
        'text' => echo $event->text,
        'tool_call' => echo "Calling: {$event->tool}\n",
        'tool_result' => echo "Result: " . json_encode($event->result) . "\n",
        'done' => echo "\nComplete!\n",
        'error' => echo "Error: {$event->error}\n",
    };
}
```

Hooks
-----

[](#hooks)

Add observability to your agent:

```
$agent = new Agent(
    provider: $provider,
    model: 'claude-sonnet-4-20250514',
    tools: $tools,
    hooks: [
        'beforeToolCall' => function (string $name, array $input) {
            Log::info("Calling tool: {$name}", $input);
        },
        'afterToolCall' => function (string $name, mixed $result, float $duration) {
            Metrics::timing("tool.{$name}", $duration);
        },
        'onError' => function (Throwable $error) {
            Sentry::captureException($error);
        },
    ],
);
```

Messages &amp; Conversation
---------------------------

[](#messages--conversation)

Build conversations manually:

```
use PapiAI\Core\Message;
use PapiAI\Core\Conversation;

// Individual messages
$message = Message::user('Hello');
$message = Message::system('You are helpful');
$message = Message::assistant('Hi there!');
$message = Message::userWithImage('What is this?', $imageUrl);

// Conversation helper
$conversation = new Conversation();
$conversation->setSystem('You are a helpful assistant');
$conversation->addUser('Hello');
$conversation->addAssistant('Hi! How can I help?');
$conversation->addUser('Tell me a joke');

$messages = $conversation->getMessages();
```

Configuration Options
---------------------

[](#configuration-options)

```
$agent = new Agent(
    provider: $provider,
    model: 'claude-sonnet-4-20250514',
    instructions: 'System prompt here',
    tools: [...],
    hooks: [...],
    maxTokens: 4096,        // Max response tokens
    temperature: 0.7,       // 0.0 = deterministic, 1.0 = creative
    maxTurns: 10,           // Max tool-use loops before stopping
);
```

Providers
---------

[](#providers)

### Anthropic (Claude)

[](#anthropic-claude)

```
use PapiAI\Anthropic\AnthropicProvider;

$provider = new AnthropicProvider(
    apiKey: $_ENV['ANTHROPIC_API_KEY'],
    defaultModel: 'claude-sonnet-4-20250514',
);
```

### Google (Gemini)

[](#google-gemini)

```
use PapiAI\Google\GoogleProvider;

$provider = new GoogleProvider(
    apiKey: $_ENV['GOOGLE_API_KEY'],
    defaultModel: GoogleProvider::MODEL_3_0_PRO,
);

// Available models
GoogleProvider::MODEL_3_1_PRO   // gemini-3.1-pro
GoogleProvider::MODEL_3_0_PRO   // gemini-3.0-pro
GoogleProvider::MODEL_2_0_FLASH // gemini-2.0-flash-exp
GoogleProvider::MODEL_1_5_PRO   // gemini-1.5-pro
GoogleProvider::MODEL_1_5_FLASH // gemini-1.5-flash
```

Creating Custom Providers
-------------------------

[](#creating-custom-providers)

Implement `ProviderInterface`:

```
use PapiAI\Core\Contracts\ProviderInterface;
use PapiAI\Core\Response;

class MyProvider implements ProviderInterface
{
    public function chat(array $messages, array $options = []): Response
    {
        // Make API call and return Response
    }

    public function stream(array $messages, array $options = []): iterable
    {
        // Yield StreamChunk objects
    }

    public function supportsTool(): bool { return true; }
    public function supportsVision(): bool { return true; }
    public function supportsStructuredOutput(): bool { return false; }
    public function getName(): string { return 'my-provider'; }
}
```

Testing
-------

[](#testing)

```
composer test
```

License
-------

[](#license)

MIT

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity26

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity49

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 ~27 days

Recently: every ~0 days

Total

10

Last Release

71d ago

PHP version history (2 changes)v0.1PHP ^8.1

v0.3PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/70713?v=4)[md](/maintainers/md)[@md](https://github.com/md)

---

Top Contributors

[![MarcelloDuarte](https://avatars.githubusercontent.com/u/144535?v=4)](https://github.com/MarcelloDuarte "MarcelloDuarte (18 commits)")

###  Code Quality

TestsPest

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/papi-ai-papi-core/health.svg)

```
[![Health](https://phpackages.com/badges/papi-ai-papi-core/health.svg)](https://phpackages.com/packages/papi-ai-papi-core)
```

###  Alternatives

[thenextweb/passgenerator

A Laravel package to create Apple Wallet (old Passbook) compatible tickets.

297435.6k](/packages/thenextweb-passgenerator)[theseer/autoload

A tool and library to generate autoload code.

393193.5k7](/packages/theseer-autoload)[ramytalal/label-printer

An implementation of the Brother label printer API.

6641.8k](/packages/ramytalal-label-printer)[gillesgoetsch/acf-smart-button

A simple, clean and lean ACF Field for internal and external links.

7625.5k](/packages/gillesgoetsch-acf-smart-button)

PHPackages © 2026

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