pv-source/aivory

PHP библиотека для работы с AI LLM-провайдерами (OpenAI-совместимое API). Единый интерфейс с поддержкой типизированных ответов, JSON Schema и fluent API, вдохновленным Eloquent

Maintainers

Package info

github.com/pvSource/aivory

pkg:composer/pv-source/aivory

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 14

Open Issues: 0

v0.1.0 2026-02-17 09:43 UTC

This package is auto-updated.

Last update: 2026-03-17 09:59:01 UTC


README

Aivory — PHP библиотека для работы с AI LLM-провайдерами (OpenAI-совместимое API, пример: DeepSeek, ChatGPT). Предоставляет единый интерфейс для отправки запросов к различным AI API с поддержкой типизированных ответов, JSON Schema и удобного fluent API. Реализация библиотеки вдохновлена Eloquent и стремится использовать подход API AS DB по отношению к нейросетям, а также функциональность стремящуюся к схожести c Eloquent.

🚀 Основные возможности

  • Множественные провайдеры — поддержка DeepSeek, OpenAI и легко расширяемая архитектура
  • Типизированные ответы — автоматическое преобразование JSON ответов в PHP объекты через JsonSchemaInterface
  • Fluent API — удобный builder pattern для построения запросов
  • JSON Schema — использование классов вместо ручного написания схем
  • Thinking Mode — поддержка режима размышления (DeepSeek)
  • Scope методы — создание переиспользуемых конфигураций (как в Laravel Eloquent)
  • PSR-7 совместимость — работа с стандартными HTTP интерфейсами
  • Type-safe — строгая типизация и валидация на всех уровнях

📦 Установка

composer require pv-source/aivory

Требования

  • PHP 8.3+
  • Guzzle HTTP Client 7.10+

🎯 Быстрый старт

Простой пример

<?php

use PvSource\Aivory\Client;
use PvSource\Aivory\LLM\Turn\Turn;

// Создаем клиент
$client = new Client(
    providerName: 'deepseek',
    auth: ['apiKey' => 'sk-your-api-key']
);

$provider = $client->getProvider();

// Отправляем запрос
$turn = Turn::query()
    ->withProvider($provider)
    ->setUserPrompt('Привет! Расскажи о PHP')
    ->setModel('deepseek-chat')
    ->setTemperature(0.7)
    ->send();

// Получаем ответ
$assistantMessage = $turn->getResponse()->getAssistant();
echo $assistantMessage->content;

Использование с JSON Schema

<?php

use PvSource\Aivory\LLM\Turn\Request\ResponseFormat;

// Создаем ResponseFormat с использованием класса
$responseFormat = ResponseFormat::jsonSchema(
    schema: ['type' => MathSolution::class],
    name: 'math_solution',
    strict: true
);

$turn = Turn::query()
    ->withProvider($provider)
    ->setUserPrompt('Реши: 2 + 2 * 3')
    ->setModel('deepseek-chat')
    ->setResponseFormat($responseFormat)
    ->send();

// Автоматическое преобразование в типизированный объект
$mathSolution = $turn->getResponse()->getSchemaObjects();
echo $mathSolution->answer; // "8"

📚 Документация

Основные концепции

1. Client — точка входа

Client создает и координирует работу всех компонентов библиотеки:

$client = new Client(
    providerName: 'deepseek',  // или 'openai'
    auth: ['apiKey' => 'sk-...'],
    httpClient: $customHttpClient  // опционально
);

2. Turn — базовый класс для запросов

Turn предоставляет fluent API для построения и отправки запросов:

$turn = Turn::query()
    ->withProvider($provider)
    ->setSystemPrompt('Ты помощник')
    ->setUserPrompt('Вопрос')
    ->setModel('deepseek-chat')
    ->setTemperature(0.7)
    ->setMaxTokens(1000)
    ->send();

3. Кастомные Turn классы

Создавайте свои классы, наследующие Turn, для переиспользования конфигураций:

class MathTurn extends Turn
{
    // Значения по умолчанию (как в Eloquent)
    protected static ?string $defaultSystemPrompt = 'Ты математик';
    protected static ?string $defaultModel = 'deepseek-chat';
    protected static ?float $defaultTemperature = 0.3;
    
    // Scope методы для разных режимов
    public static function scopeAdvancedSolver(TurnBuilder $builder): void
    {
        $builder->setIsThinking(true);
        $builder->setMaxTokens(2000);
    }
}

// Использование
$turn = MathTurn::query()
    ->withProvider($provider)
    ->advancedSolver()  // вызывает scopeAdvancedSolver()
    ->setUserPrompt('Реши задачу')
    ->send();

4. JsonSchemaInterface — типизированные ответы

Создавайте классы, реализующие JsonSchemaInterface, для автоматического преобразования ответов:

class MathSolution implements JsonSchemaInterface
{
    public function __construct(
        public string $answer,
        public array $steps = []
    ) {}
    
    public static function getJsonSchema(?array $rootSchema = null): array
    {
        $schema = [
            'type' => 'object',
            'properties' => [
                'answer' => ['type' => 'string'],
            ]
        ];
        
        // Адаптивная схема в зависимости от контекста
        if ($rootSchema['name'] === 'advanced_math_solution') {
            $schema['properties']['steps'] = [
                'type' => 'array',
                'items' => ['type' => MathStep::class]
            ];
        }
        
        return $schema;
    }
    
    public static function fromResponse(array $data): static
    {
        return new self(
            answer: $data['answer'],
            steps: array_map(
                fn($step) => MathStep::fromResponse($step),
                $data['steps'] ?? []
            )
        );
    }
}

Поддерживаемые провайдеры

DeepSeek

$client = new Client('deepseek', ['apiKey' => 'sk-...']);

Особенности:

  • ✅ Поддержка thinking mode (isThinking)
  • ✅ Reasoning content в ответах
  • ✅ JSON Schema через системное сообщение

OpenAI

$client = new Client('openai', ['apiKey' => 'sk-...']);

Особенности:

  • ✅ JSON Schema через отдельное поле json_schema
  • ❌ Не поддерживает thinking mode

Параметры запроса

Все параметры доступны через fluent API:

$turn = Turn::query()
    ->setModel('deepseek-chat')
    ->setTemperature(0.7)           // 0.0-2.0
    ->setMaxTokens(2000)            // Максимум токенов
    ->setFrequencyPenalty(0.5)     // -2.0 до 2.0
    ->setPresencePenalty(0.3)      // -2.0 до 2.0
    ->setTopP(0.9)                  // 0.0-1.0
    ->setIsThinking(true)            // DeepSeek only
    ->setStop(['\n\n', 'END'])       // Стоп-последовательности
    ->setStream(false)               // Streaming
    ->setResponseFormat($format)     // JSON Schema или text
    ->send();

Работа с ответами

$response = $turn->getResponse();

// Получить сообщение от ассистента
$assistantMessage = $response->getAssistant();
echo $assistantMessage->content;

// Получить reasoning content (если включен thinking mode)
if ($assistantMessage->reasoningContent !== null) {
    echo $assistantMessage->reasoningContent;
}

// Получить типизированный объект (если использовался JSON Schema)
$mathSolution = $response->getSchemaObjects();

// Получить usage информацию
$rawBody = $response->getRawBody();
$usage = $rawBody['usage'];
echo "Токенов использовано: {$usage['total_tokens']}";

📖 Примеры

Простой пример

См. examples/simple/index.php

Продвинутый пример

См. examples/advanced/index.php

Демонстрирует:

  • Использование JsonSchemaInterface
  • Создание кастомных Turn классов
  • Scope методы
  • Адаптивные схемы
  • Thinking mode и reasoning content

Пример с разными моделями

См. examples/models_example.php

🏗️ Архитектура

Библиотека следует принципам Domain-Driven Design (DDD):

src/
├── LLM/                    # Доменный слой
│   ├── Turn/              # Основные доменные модели
│   │   ├── Turn.php       # Базовый класс Turn
│   │   ├── Request/       # DTO запроса
│   │   ├── Response/      # Интерфейс ответа
│   │   └── Options/       # Настройки запроса
│   └── ProviderInterface.php
├── Providers/             # Инфраструктурный слой
│   ├── DeepSeek/         # Реализация для DeepSeek
│   └── OpenAI/           # Реализация для OpenAI
└── Transport/             # HTTP транспорт
    └── HttpClientInterface.php

Паттерны проектирования

  • Builder Pattern — для построения запросов
  • Adapter Pattern — для преобразования доменных моделей в API формат
  • Factory Pattern — для создания провайдеров
  • Strategy Pattern — для различных провайдеров

🔧 Расширение библиотеки

Добавление нового провайдера

  1. Создайте класс провайдера, реализующий ProviderInterface
  2. Создайте RequestAdapter для преобразования запросов
  3. Создайте ChatTurnResponse для обработки ответов
  4. Зарегистрируйте в ProviderFactory

Кастомный HTTP клиент

class CustomHttpClient implements HttpClientInterface
{
    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        // Ваша реализация
    }
}

$client = new Client(
    'deepseek',
    ['apiKey' => 'sk-...'],
    new CustomHttpClient()
);

📝 Конфигурация

Для того, чтобы запускать примеры из examples, необходимо создать файл .env в корне библиотеки:

PROVIDER=deepseek
API_KEY=sk-your-api-key-here

🗺️ Планы развития

Библиотека активно развивается. Вот что планируется добавить и улучшить:

  • Добавление Conversation - сущности, содержащей в себе переписку
  • Join/with - добавление join-подобного интерфейса для группы последовательных и/или параллельных запросов, в том числе с использованием разных провайдеров, концепция "отношений" между Turn классами
  • Доменные исключения - иерархия специализированных исключений для лучшей обработки ошибок
  • Валидация на уровне Request - расширенная валидация запросов перед отправкой
  • Middleware/Interceptors - система middleware для логирования, кэширования, retry и т.д.
  • Событийная модель - добавление возможности подписываться на события жизненного цикла обьектов библиотеки
  • Расширение числа провайдеров - увеличение числа возможных провайдеров, доступных "из коробки"
  • Типизация и унификация работы с системными полямя ответов - работа с usage и прочими полями ответа
  • Асинхронные запросы, ReactPHP - поддержка Promise-based API для параллельных запросов
  • Поддержка streaming - полноценная обработка потоковых ответов
  • Rate limiting - встроенная защита от превышения лимитов API
  • Тесты - покрытие тестами всех компонентов библиотеки

Если у вас есть идеи или предложения, пожалуйста, создавайте issue или пишите в Telegram!

🤝 Вклад в проект

Приветствуются любые улучшения! Пожалуйста, создавайте issue или pull request. А также пишите в telegram: @blueberryMgn

Сделано с ❤️ для PHP разработчиков