PHP code example of bycerfrance / llm-api-lib

1. Go to this page and download the library: Download bycerfrance/llm-api-lib library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

bycerfrance / llm-api-lib example snippets


use ByCerfrance\LlmApiLib\Provider\Generic;

$provider = new Generic(
    uri: 'https://my-local-server.com/v1/chat/completions',
    apiKey: 'my-api-key',
    model: 'my-model',
    client: $httpClient, // PSR-18 ClientInterface
);

use ByCerfrance\LlmApiLib\Model\ModelInfo;
use ByCerfrance\LlmApiLib\Model\QualityTier;
use ByCerfrance\LlmApiLib\Model\CostTier;
use ByCerfrance\LlmApiLib\Model\Capability;
use ByCerfrance\LlmApiLib\Provider\OpenAi;

$model = new ModelInfo(
    name: 'gpt-4o',
    capabilities: [Capability::TEXT, Capability::IMAGE, Capability::TOOLS, Capability::JSON_OUTPUT],
    qualityTier: QualityTier::PREMIUM,
    costTier: CostTier::HIGH,
    inputCost: 2.50,   // $ per million tokens
    outputCost: 10.00,  // $ per million tokens
    maxContextTokens: 128_000,
    maxOutputTokens: 16_384,
);

$provider = new OpenAi(
    apiKey: 'sk-...',
    model: $model,       // ModelInfo or plain string
    client: $httpClient,
);

use ByCerfrance\LlmApiLib\Model\ModelInfo;

$gpt5 = new ModelInfo(name: 'gpt-5', tunable: false);
$opus47 = new ModelInfo(name: 'claude-opus-4-7', tunable: false);
$o3 = new ModelInfo(name: 'o3-mini', tunable: false);

$custom = new ModelInfo(
    name: 'my-endpoint',
    stripFields: ['service_tier', 'response_format.strict'],
);

// Combine both: a non-tunable reasoning model with an extra restriction
$gpt5Azure = new ModelInfo(
    name: 'gpt-5',
    tunable: false,
    stripFields: ['service_tier'],
);

$llm = new \ByCerfrance\LlmApiLib\Llm($provider);

$completion = $llm->chat('Salut !');
print $completion->getLastMessage()->getContent(); // "Salut ! Comment allez-vous ?"

$completion = $llm->chat($completion->withNewMessage('Bien merci et toi ?'));
print $completion->getLastMessage()->getContent(); // "Bien, merci. Comment puis-je vous aider ?"

use ByCerfrance\LlmApiLib\Completion\Completion;
use ByCerfrance\LlmApiLib\Completion\Message\SystemMessage;
use ByCerfrance\LlmApiLib\Llm;

$completion = new Completion(new SystemMessage(
    'Tu es un assistant comptable, presentes toi comme tel.',
));

$llm = new Llm($provider);

$completion = $llm->chat($completion->withNewMessage('Salut !'));
print $completion->getLastMessage()->getContent();
// "Bonjour, je suis votre assistant comptable. Comment puis-je vous aider ?"

use ByCerfrance\LlmApiLib\Completion\Message\SystemMessage;
use ByCerfrance\LlmApiLib\Completion\Message\UserMessage;
use ByCerfrance\LlmApiLib\Completion\Message\Message;
use ByCerfrance\LlmApiLib\Completion\Message\RoleEnum;

// Typed classes (recommended)
$system = new SystemMessage('You are a helpful assistant.');
$user = new UserMessage('Hello!');

// Or using the generic Message class with explicit role
$system = new Message('You are a helpful assistant.', role: RoleEnum::SYSTEM);

use ByCerfrance\LlmApiLib\Completion\Completion;

$completion = (new Completion(['Explain quantum computing']))
    ->withModel('gpt-4o')           // Override the provider's default model
    ->withMaxTokens(2000)           // Maximum tokens in the response
    ->withTemperature(0.7)          // Creativity (0 = deterministic, 2 = very creative)
    ->withTopP(0.9)                 // Nucleus sampling
    ->withSeed(42);                 // Reproducible outputs (provider-dependent)

use ByCerfrance\LlmApiLib\Completion\Completion;
use ByCerfrance\LlmApiLib\Completion\ServiceTier;

$completion = (new Completion(['Summarize this report']))
    ->withServiceTier(ServiceTier::FLEX);

use ByCerfrance\LlmApiLib\Completion\Completion;
use ByCerfrance\LlmApiLib\Completion\ReasoningEffort;

$completion = (new Completion(['Solve this math problem step by step']))
    ->withReasoningEffort(ReasoningEffort::HIGH);

$content = new ArrayContent(
    new TextContent('First message'),
    'Second message'
);

$content = new DocumentUrlContent('https://example.com/document.pdf');

$content = DocumentUrlContent::fromFile('/path/to/document.pdf', 'custom-name.pdf');

$content = new ImageUrlContent('https://example.com/image.jpg');

$content = ImageUrlContent::fromGdImage($gdImage, 'high');

$content = ImageUrlContent::fromFile('/path/to/image.png', 'low');

$content = new InputAudioContent('base64encodeddata', 'wav');

$text = new TextContent('Hello, world!');
$json = new JsonContent(['key' => 'value']);

$content = new TextContent('Hello {name}, you are {age} years old.', ['name' => 'John', 'age' => 30]);
echo $content; // Outputs: "Hello John, you are 30 years old."

$content = TextContent::fromFile('/path/to/text.txt', ['name' => 'John', 'age' => 30]);

use ByCerfrance\LlmApiLib\Completion\ResponseFormat\TextFormat;

$completion = (new Completion(['Explain gravity']))
    ->withResponseFormat(new TextFormat());

use ByCerfrance\LlmApiLib\Completion\ResponseFormat\JsonObjectFormat;

$completion = (new Completion(['List 3 colors as a JSON array']))
    ->withResponseFormat(new JsonObjectFormat());

$response = $llm->chat($completion);
$data = json_decode($response->getLastMessage()->getContent(), true);

use ByCerfrance\LlmApiLib\Completion\ResponseFormat\JsonSchemaFormat;

$completion = (new Completion(['Describe a person']))
    ->withResponseFormat(new JsonSchemaFormat(
        name: 'person',
        schema: [
            'type' => 'object',
            'properties' => [
                'name' => ['type' => 'string'],
                'age' => ['type' => 'integer'],
            ],
            '

use ByCerfrance\LlmApiLib\Completion\Tool\Tool;

$weatherTool = new Tool(
    name: 'get_weather',
    description: 'Get the current weather for a location',
    parameters: [
        'type' => 'object',
        'properties' => [
            'location' => [
                'type' => 'string',
                'description' => 'The city name',
            ],
        ],
        '

use ByCerfrance\LlmApiLib\Completion\Completion;
use ByCerfrance\LlmApiLib\Llm;

$completion = (new Completion(['Quel temps fait-il a Paris ?']))
    ->withTools($weatherTool)
    ->withMaxToolIterations(5); // Optional, default is 10

$llm = new Llm($provider);
$response = $llm->chat($completion);

print $response->getLastMessage()->getContent();
// "Il fait actuellement 20°C a Paris avec un temps ensoleille."

use ByCerfrance\LlmApiLib\Completion\Tool\ToolCollection;

$completion = (new Completion(['...']))
    ->withTools($weatherTool, $calculatorTool, $searchTool);

// Or using a collection
$tools = new ToolCollection($weatherTool, $calculatorTool);
$completion = (new Completion(['...']))->withTools($tools);

use ByCerfrance\LlmApiLib\Completion\Tool\FilteredToolCollection;

// Only expose specific tools
$filtered = new FilteredToolCollection($toolCollection, ['get_weather', 'search']);

// Exclude specific tools (expose everything else)
$filtered = new FilteredToolCollection($toolCollection, ['!delete_user', '!drop_table']);

use ByCerfrance\LlmApiLib\Completion\Completion;
use ByCerfrance\LlmApiLib\Completion\ToolChoice;

$completion = (new Completion(['What is the weather in Paris?']))
    ->withTools($weatherTool)
    ->withToolChoice(ToolChoice::REQUIRED);

$completion = (new Completion(['...']))
    ->withTools($weatherTool, $calculatorTool)
    ->withParallelToolCalls(false); // Force sequential tool calls

use ByCerfrance\LlmApiLib\Mcp\McpServer;
use ByCerfrance\LlmApiLib\Mcp\Transport\HttpStreamable;

// Create transport
$transport = new HttpStreamable(
    uri: 'https://my-mcp-server.com/mcp',
    client: $httpClient,
    headers: ['Authorization' => 'Bearer my-token'],
);

// Create MCP server client
$mcp = new McpServer($transport);

// Use MCP tools in a completion (tools are discovered automatically via lazy initialization)
$completion = (new Completion(['Search for documents about PHP']))
    ->withTools($mcp);

$response = $llm->chat($completion);

use ByCerfrance\LlmApiLib\Mcp\OpenApi;
use cebe\openapi\Reader;

$spec = Reader::readFromJsonFile('/path/to/openapi.json');

$openApi = new OpenApi(
    spec: $spec,
    client: $httpClient,
    headers: ['Authorization' => 'Bearer api-token'],
    baseUrl: 'https://api.example.com', // Optional, overrides spec servers
);

// Use OpenAPI operations as tools
$completion = (new Completion(['List all users']))
    ->withTools($openApi);

// Or filter specific operations
$filtered = new FilteredToolCollection($openApi, ['listUsers', 'getUser']);
$completion = (new Completion(['List all users']))
    ->withTools($filtered);

$response = $llm->chat($completion);

use ByCerfrance\LlmApiLib\Completion\Tool\LlmTool;
use ByCerfrance\LlmApiLib\Completion\Completion;

$analysisTool = new LlmTool(
    name: 'analyze_code',
    description: 'Analyze code for security vulnerabilities',
    parameters: [
        'type' => 'object',
        'properties' => [
            'code' => ['type' => 'string', 'description' => 'The code to analyze'],
            'language' => ['type' => 'string', 'description' => 'Programming language'],
        ],
        'lysisTool->getLlm()->getUsage();
$subModelCost = $analysisTool->getLlm()->getCost();

use ByCerfrance\LlmApiLib\Model\SelectionStrategy;
use ByCerfrance\LlmApiLib\Completion\Completion;

$completion = (new Completion(['Complex reasoning task']))
    ->withSelectionStrategy(SelectionStrategy::BEST_QUALITY);

$response = $llm->chat('Hello');

// Access the response content
$content = $response->getLastMessage()->getContent();

// Per-request token usage
$usage = $response->getUsage();
echo $usage->getPromptTokens();
echo $usage->getCompletionTokens();
echo $usage->getTotalTokens();

// Finish reason
$finishReason = $response->getFinishReason(); // FinishReason::STOP, LENGTH, TOOL_CALLS, CONTENT_FILTER

// Continue the conversation (CompletionResponseInterface extends CompletionInterface)
$response = $llm->chat($response->withNewMessage('Follow up question'));

use ByCerfrance\LlmApiLib\Retry;

$retryableProvider = new Retry(
    provider: $provider,
    time: 5000,          // Base wait time in milliseconds (default: 5000)
    retry: 3,            // Maximum retry attempts (default: 2)
    multiplier: 2.0,     // Exponential backoff multiplier (default: 1 = constant delay)
    retryOnGuard: false, // Retry on GuardException (default: false)
);

// Wait times: 5s, 10s, 20s (time * multiplier^attempt)
$response = $retryableProvider->chat('Hello');

use ByCerfrance\LlmApiLib\Guard\Guard;
use ByCerfrance\LlmApiLib\Guard\GuardException;

$guarded = new Guard(
    provider: $provider,
    guard: function (\ByCerfrance\LlmApiLib\Completion\CompletionResponseInterface $response): void {
        $content = $response->getLastMessage()->getContent();
        if (str_contains($content, 'I cannot')) {
            throw new \RuntimeException('Response contains a refusal');
        }
    },
);

try {
    $response = $guarded->chat('...');
} catch (GuardException $e) {
    $rejectedResponse = $e->getResponse(); // Access the rejected response
    echo $e->getMessage();
}

use ByCerfrance\LlmApiLib\Guard\FinishReasonGuard;
use ByCerfrance\LlmApiLib\Completion\FinishReason;

// Default: rejects LENGTH and CONTENT_FILTER
$guarded = new FinishReasonGuard($provider);

// Custom: only reject LENGTH
$guarded = new FinishReasonGuard($provider, FinishReason::LENGTH);

use ByCerfrance\LlmApiLib\Guard\FinishReasonGuard;
use ByCerfrance\LlmApiLib\Retry;

// Retry up to 3 times if the response is truncated (LENGTH) or filtered
$robust = new Retry(
    provider: new FinishReasonGuard($provider),
    retry: 3,
    retryOnGuard: true, // Required to retry on GuardException
);

$response = $robust->chat('...');

use ByCerfrance\LlmApiLib\Llm;

$llm = new Llm($openAiProvider, $mistralProvider, $googleProvider);

// If OpenAI fails, Mistral is tried. If Mistral fails, Google is tried.
$response = $llm->chat('Hello');

use Psr\Log\LoggerInterface;

/** @var LoggerInterface $logger */
$response = $llm->chat($completion, logger: $logger);

$usage = $llm->getUsage();
echo $usage->getPromptTokens();      // Total input tokens
echo $usage->getCompletionTokens();  // Total output tokens
echo $usage->getTotalTokens();       // Total tokens

$cost = $llm->getCost();           // Total cost in dollars (4 decimal precision)
$cost = $llm->getCost(precision: 6); // Higher precision

$maxTokens = $llm->getMaxContextTokens();  // e.g. 128000, or null if undefined
$maxOutput = $llm->getMaxOutputTokens();   // e.g. 16384, or null if undefined