# RelAI PHP SDK

**PHP SDK for RelAI — Personal AI Relay**

`relai/sdk-php` est un client PHP 8.1+ léger pour appeler une instance RelAI Core depuis
PHP ou Laravel. Il fournit un transport Guzzle authentifié, des ressources lisibles,
des exceptions stables et une intégration Laravel optionnelle.

RelAI Core reste la source de vérité pour les conversations, messages, runs, providers,
structured outputs et tool calls. Le SDK ne stocke rien, n'exécute aucun tool et
n'appelle jamais directement Ollama, OpenAI, Claude, Mistral ou Gemini.

> **Sécurité :** ne jamais exposer un token applicatif RelAI dans un frontend public.
> Ce package est destiné aux backends PHP.

## Fonctionnalités

| Ressource | API | Route Core |
|---|---|---|
| Chat | `$relai->chat()->send()` | `POST /api/v1/chat` |
| Structured output | `$relai->structured()->run()` | `POST /api/v1/structured` |
| Tools | `$relai->tools()->execute()` | `POST /api/v1/tools/:name/execute` |
| Conversations | `list()`, `get()`, `create()` | `/api/v1/conversations` |
| Runs | `list()`, `get()` | `/api/v1/runs` |

- PHP 8.1 minimum et PSR-4;
- Guzzle 7, JSON automatique et timeout configurable;
- authentification bearer et headers réservés protégés;
- PHPUnit et PHPStan;
- service provider Laravel auto-découvert;
- aucune dépendance runtime à Laravel.

## Installation

Après publication :

```bash
composer require relai/sdk-php
```

Avant publication, ajoutez un path repository :

```json
{
  "repositories": [
    { "type": "path", "url": "../path/to/relai-php" }
  ],
  "require": {
    "relai/sdk-php": "*"
  }
}
```

```bash
composer require relai/sdk-php:*
```

Voir [Installation](docs/installation.md).

## Configuration standalone

```php
<?php

declare(strict_types=1);

use Relai\Sdk\RelaiClient;

$relai = new RelaiClient(
    baseUrl: 'http://localhost:3000',
    token: getenv('RELAI_APP_TOKEN') ?: '',
    appSlug: 'demo',
    timeoutSeconds: 30,
    defaultProvider: 'ollama',
    defaultModel: null,
);
```

Ou avec un objet immuable :

```php
use Relai\Sdk\RelaiClientOptions;

$relai = new RelaiClient(new RelaiClientOptions(
    baseUrl: 'http://localhost:3000',
    token: getenv('RELAI_APP_TOKEN') ?: '',
    appSlug: 'demo',
));
```

## Chat

```php
$response = $relai->chat()->send([
    'message' => 'Summarize the selected data.',
    'tools' => ['demo.period_summary'],
    'metadata' => [
        'periodStart' => '2026-07-01',
        'periodEnd' => '2026-07-31',
    ],
]);

echo $response['assistantMessage'];
print_r($response['toolCalls'] ?? []);
$conversationId = $response['conversationId'];
```

`tools` est une allowlist de noms génériques transmise au Core. Le SDK ne connaît pas
l'application propriétaire du tool, ne valide pas son schéma métier et n'exécute pas de
boucle agentique.

Sans `conversationId`, le Core crée une conversation. Pour la reprendre :

```php
$followUp = $relai->chat()->send([
    'conversationId' => $conversationId,
    'message' => 'Détaille la deuxième recommandation.',
]);
```

## Structured output

```php
$report = $relai->structured()->run([
    'messages' => [
        ['role' => 'user', 'content' => 'Fais une analyse synthétique.'],
    ],
    'schema' => [
        'type' => 'object',
        'properties' => [
            'summary' => ['type' => 'string'],
            'recommendations' => [
                'type' => 'array',
                'items' => ['type' => 'string'],
            ],
        ],
        'required' => ['summary', 'recommendations'],
        'additionalProperties' => false,
    ],
]);

print_r($report['data']);
```

Le Core transmet le schéma au provider puis revalide la réponse avec AJV. Les tools
ne sont pas supportés par `/api/v1/structured` en V1; le SDK refuse donc `tools` avec
`Structured output does not support tools yet. Use chat.send() for tool calling.`

## Tools

```php
$result = $relai->tools()->execute('myapp.get_document', [
    'documentId' => 'doc_123',
]);

print_r($result['output']);
```

Le Core autorise, valide, exécute et audite le tool. Le SDK normalise le champ Core
`data` en `output`.

## Conversations

```php
$created = $relai->conversations()->create([
    'title' => 'Document analysis',
    'type' => 'analysis',
]);

$conversation = $relai->conversations()->get($created['id']);
$page = $relai->conversations()->list(['page' => 1, 'limit' => 25]);
```

Les listes retournent l'enveloppe réelle du Core : `items`, `total`, `page`, `limit`.

## Runs

```php
$page = $relai->runs()->list(['status' => 'failed', 'limit' => 10]);

if (isset($page['items'][0]['id'])) {
    $run = $relai->runs()->get($page['items'][0]['id']);
    print_r($run);
}
```

## Gestion des erreurs

```php
use Relai\Sdk\Exceptions\RelaiApiException;
use Relai\Sdk\Exceptions\RelaiException;
use Relai\Sdk\Exceptions\RelaiTimeoutException;
use Relai\Sdk\Exceptions\RelaiValidationException;

try {
    $response = $relai->chat()->send(['message' => 'Bonjour']);
} catch (RelaiApiException $e) {
    echo $e->getStatusCode();
    print_r($e->getResponseBody());
} catch (RelaiTimeoutException $e) {
    echo $e->getTimeoutSeconds();
} catch (RelaiValidationException $e) {
    echo $e->getField() . ': ' . $e->getMessage();
} catch (RelaiException $e) {
    echo $e->getMessage();
}
```

## Laravel

Publiez la configuration :

```bash
php artisan vendor:publish --tag=relai-config
```

Puis configurez `.env` :

```env
RELAI_BASE_URL=http://localhost:3000
RELAI_APP_TOKEN=your_app_token_here
RELAI_APP_SLUG=myapp
RELAI_TIMEOUT_SECONDS=30
RELAI_DEFAULT_PROVIDER=ollama
RELAI_DEFAULT_MODEL=
```

Le package auto-découvre son service provider et enregistre un singleton injectable :

```php
use Relai\Sdk\RelaiClient;

final class AiAnalysisService
{
    public function __construct(private RelaiClient $relai)
    {
    }

    public function analyze(): array
    {
        return $this->relai->chat()->send([
            'message' => 'Summarize the selected tasks.',
            'tools' => ['myapp.task_summary'],
        ]);
    }
}
```

L'injection est recommandée; la facade `Relai` reste disponible pour les usages courts.
Voir [Intégration Laravel](docs/laravel.md).

## Développement

```bash
composer install
composer test
composer analyse
composer check
```

## Documentation

La [documentation complète](docs/README.md) contient :

- [Introduction](docs/introduction.md), [installation](docs/installation.md),
  [configuration](docs/configuration.md) et [authentification](docs/authentication.md);
- [Laravel](docs/laravel.md), [chat](docs/chat.md),
  [structured output](docs/structured-output.md), [tools](docs/tools.md),
  [conversations](docs/conversations.md) et [runs](docs/runs.md);
- [Référence API](docs/api-reference.md), [transport HTTP](docs/http-client.md),
  [erreurs](docs/errors.md), [sécurité](docs/security.md),
  [exemples](docs/examples.md) et [dépannage](docs/troubleshooting.md).

## Compatibilité connue

Le SDK `0.1.x` cible RelAI Core V1. Les IDs publics acceptent `string|int`, mais le Core
actuel utilise des chaînes. `appSlug` est requis par chat/structured, et les structured
tools ne sont pas supportés en V1.

## Licence

UNLICENSED — package privé tant qu'une licence de distribution n'a pas été choisie.
