Files
archived-ai/examples/bootstrap.php
2026-01-23 21:56:05 +01:00

176 lines
6.0 KiB
PHP

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Symfony\AI\Agent\Exception\ExceptionInterface as AgentException;
use Symfony\AI\Agent\Toolbox\Source\SourceCollection;
use Symfony\AI\Platform\Exception\ExceptionInterface as PlatformException;
use Symfony\AI\Platform\Result\DeferredResult;
use Symfony\AI\Platform\TokenUsage\TokenUsageAggregation;
use Symfony\AI\Platform\TokenUsage\TokenUsageInterface;
use Symfony\AI\Store\Exception\ExceptionInterface as StoreException;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
require_once __DIR__.'/vendor/autoload.php';
(new Dotenv())->loadEnv(__DIR__.'/.env');
function env(string $var): string
{
if (!isset($_SERVER[$var]) || '' === $_SERVER[$var]) {
output()->writeln(sprintf('<error>Please set the "%s" environment variable to run this example.</error>', $var));
exit(1);
}
return $_SERVER[$var];
}
function http_client(): HttpClientInterface
{
$httpClient = HttpClient::create();
if ($httpClient instanceof LoggerAwareInterface) {
$httpClient->setLogger(logger());
}
return $httpClient;
}
function logger(): LoggerInterface
{
$output = output();
return new class($output) extends ConsoleLogger {
private ConsoleOutput $output;
public function __construct(ConsoleOutput $output)
{
parent::__construct($output);
$this->output = $output;
}
/**
* @param Stringable|string $message
*/
public function log($level, $message, array $context = []): void
{
// Call parent to handle the base logging
parent::log($level, $message, $context);
// Add context display for debug verbosity
if ($this->output->getVerbosity() >= ConsoleOutput::VERBOSITY_DEBUG && [] !== $context) {
// Filter out special keys that are already handled
$displayContext = array_filter($context, static function ($key) {
return !in_array($key, ['exception', 'error', 'object'], true);
}, \ARRAY_FILTER_USE_KEY);
if ([] !== $displayContext) {
$contextMessage = ' '.json_encode($displayContext, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
$this->output->writeln(sprintf('<comment>%s</comment>', $contextMessage));
}
}
}
};
}
function output(): ConsoleOutput
{
$verbosity = match ($_SERVER['argv'][1] ?? null) {
'-v', '--verbose' => ConsoleOutput::VERBOSITY_VERBOSE,
'-vv', '--very-verbose' => ConsoleOutput::VERBOSITY_VERY_VERBOSE,
'-vvv', '--debug' => ConsoleOutput::VERBOSITY_DEBUG,
default => ConsoleOutput::VERBOSITY_NORMAL,
};
return new ConsoleOutput($verbosity);
}
function print_sources(?SourceCollection $sources): void
{
if (null === $sources || 0 === $sources->count()) {
output()->writeln('<error>No sources available.</error>');
return;
}
$table = new Table(output());
$table->setHeaderTitle('Tool Sources');
$table->setHeaders(['Name (Reference)', 'Content']);
foreach ($sources as $source) {
$name = $source->getName();
$reference = $source->getReference();
$content = $source->getContent();
$table->addRow([
'<comment>'.(strlen($name) <= 50 ? $name : substr($name, 0, 50).'...').'</comment>'.\PHP_EOL.
'<fg=gray>'.(strlen($reference) <= 50 ? $reference : substr($reference, 0, 50).'...').'</>',
strlen($content) <= 100 ? $content : substr($content, 0, 100).'...',
]);
$table->addRow(new TableSeparator());
}
$table->render();
}
function print_token_usage(?TokenUsageInterface $tokenUsage): void
{
if (null === $tokenUsage) {
output()->writeln('<error>No token usage information available.</error>');
exit(1);
}
$na = '<comment>n/a</comment>';
$table = new Table(output());
$table->setHeaderTitle('Token Usage');
$table->setRows([
['Prompt tokens', $tokenUsage->getPromptTokens() ?? $na],
['Completion tokens', $tokenUsage->getCompletionTokens() ?? $na],
['Thinking tokens', $tokenUsage->getThinkingTokens() ?? $na],
['Tool tokens', $tokenUsage->getToolTokens() ?? $na],
['Cached tokens', $tokenUsage->getCachedTokens() ?? $na],
['Remaining tokens minute', $tokenUsage->getRemainingTokensMinute() ?? $na],
['Remaining tokens month', $tokenUsage->getRemainingTokensMonth() ?? $na],
['Remaining tokens', $tokenUsage->getRemainingTokens() ?? $na],
['Total tokens', $tokenUsage->getTotalTokens() ?? $na],
]);
$table->render();
if ($tokenUsage instanceof TokenUsageAggregation) {
output()->writeln(sprintf('<comment>Aggregated token usage from %d calls.</comment>', $tokenUsage->count()));
}
}
function print_vectors(DeferredResult $result): void
{
assert([] !== $result->asVectors());
assert(array_key_exists(0, $result->asVectors()));
output()->writeln(sprintf('Dimensions: %d', $result->asVectors()[0]->getDimensions()));
}
set_exception_handler(static function ($exception) {
if ($exception instanceof AgentException || $exception instanceof PlatformException || $exception instanceof StoreException) {
output()->writeln(sprintf('<error>%s</error>', $exception->getMessage()));
if (output()->isVerbose()) {
output()->writeln($exception->getTraceAsString());
}
exit(1);
}
throw $exception;
});