1. Go to this page and download the library: Download errogaht/neuron-ai-bundle 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/ */
namespace App\Service;
use NeuronAI\Agent\AgentInterface;
use NeuronAI\Chat\Messages\UserMessage;
final class AnswerQuestion
{
public function __construct(private AgentInterface $agent)
{
}
public function __invoke(string $question): ?string
{
return $this->agent->chat(new UserMessage($question))->getMessage()->getContent();
}
}
use Errogaht\NeuronAiBundle\Agent\AgentRunner;
$result = $runner->chat('assistant', 'Summarize this order.', threadId: 'order-42');
$text = $result->content();
use NeuronAI\Chat\Messages\Stream\Chunks\TextChunk;
$stream = $runner->stream('assistant', 'Summarize this order.', threadId: 'order-42');
foreach ($stream as $chunk) {
// Expose only chunk types appropriate for the client boundary. Tool and
// reasoning chunks may contain implementation details or sensitive data.
if ($chunk instanceof TextChunk) {
echo $chunk->content;
}
}
$result = $stream->getReturn();
use NeuronAI\Agent\AgentInterface;
use NeuronAI\Providers\AIProviderInterface;
final class ControllerService
{
public function __construct(
private AgentInterface $supportAgent,
private AgentInterface $orderManagerAgent,
private AIProviderInterface $mainProvider,
) {
}
}
namespace App\Ai\Tool;
use App\Repository\OrderRepository;
use NeuronAI\Tools\Tool;
final class FindOrderTool extends Tool
{
public function __construct(private readonly OrderRepository $orders)
{
parent::__construct('find_order', 'Find an order visible to the current user.');
}
public function __invoke(string $number): array
{
// Authorization belongs in the application service/repository boundary.
return $this->orders->findVisibleSummary($number);
}
}
namespace App\Ai\Tool;
use App\Repository\OrderRepository;
use Errogaht\NeuronAiBundle\Tool\AbstractToolGroup;
use Errogaht\NeuronAiBundle\Tool\Attribute\Tool;
use Errogaht\NeuronAiBundle\Tool\Attribute\ToolParameter;
final class OrderTools extends AbstractToolGroup
{
public function __construct(private readonly OrderRepository $orders)
{
}
#[Tool(description: 'Find an order visible to the current user.')]
public function findOrder(
#[ToolParameter(description: 'Public order number')] string $number,
#[ToolParameter(enum: ['short', 'full'])] string $format = 'short',
): array {
return $this->orders->findVisibleSummary($number, $format);
}
#[Tool(name: 'change_delivery_address', description: 'Change delivery before dispatch.', maxRuns: 1)]
public function changeDeliveryAddress(string $number, string $address): array
{
return $this->orders->changeVisibleOrderAddress($number, $address);
}
public function guidelines(): ?string
{
return 'Always find the order before attempting a change.';
}
}
namespace App\Ai\Agent;
use App\Ai\Tool\FindOrderTool;
use App\Ai\Tool\OrderTools;
use Errogaht\NeuronAiBundle\Agent\Attribute\AsNeuronAgent;
use NeuronAI\Agent\Agent;
use NeuronAI\Providers\AIProviderInterface;
use NeuronAI\Tools\ToolInterface;
use NeuronAI\Tools\Toolkits\ToolkitInterface;
#[AsNeuronAgent('order_manager')]
final class OrderManagerAgent extends Agent
{
public function __construct(
AIProviderInterface $mainProvider,
private readonly FindOrderTool $findOrder,
private readonly OrderTools $orders,
) {
// Required whenever a subclass declares its own constructor: Neuron initializes workflow state here.
parent::__construct();
$this->setAiProvider($mainProvider);
}
protected function instructions(): string
{
return <<<'PROMPT'
You manage orders for the authenticated customer.
Read the order before proposing a change and never bypass tool authorization.
PROMPT;
}
/** @return list<ToolInterface|ToolkitInterface> */
protected function tools(): array
{
return [$this->findOrder, $this->orders];
}
}
final class OrderAssistant
{
public function __construct(private OrderManagerAgent $agent)
{
}
}
$result = $runner->chat('order_manager', 'Move my order to tomorrow.');
namespace App\Ai\Rag;
use App\Entity\Article;
use App\Repository\ArticleRepository;
use NeuronAI\RAG\DataLoader\DataLoaderInterface;
use NeuronAI\RAG\Document;
final class DocumentationLoader implements DataLoaderInterface
{
public function __construct(private ArticleRepository $articles)
{
}
public function getDocuments(): array
{
return array_map(static function (Article $article): Document {
$document = new Document($article->getSearchableText());
$document->sourceType = 'article';
$document->sourceName = (string) $article->getId();
$document->metadata = ['tenant' => $article->getTenantId()];
return $document;
}, $this->articles->findPublished());
}
}
public function __construct(
EmbeddingsProviderInterface $knowledgeEmbeddings,
VectorStoreInterface $knowledgeVectorStore,
) {
}
#[AsNeuronAgent('knowledge')]
final class KnowledgeAgent extends RAG
{
public function __construct(
AIProviderInterface $mainProvider,
EmbeddingsProviderInterface $knowledgeEmbeddings,
VectorStoreInterface $knowledgeVectorStore,
) {
parent::__construct();
$this->setAiProvider($mainProvider);
$this->setEmbeddingsProvider($knowledgeEmbeddings);
$this->setVectorStore($knowledgeVectorStore);
}
protected function instructions(): string
{
return 'Answer only from retrieved tenant-visible documents.';
}
}
use NeuronAI\Workflow\WorkflowInterface;
final class OrderController
{
public function __construct(private WorkflowInterface $orderProcessingWorkflow)
{
}
}
namespace App\Ai\Workflow;
use Errogaht\NeuronAiBundle\Workflow\Attribute\AsNeuronWorkflow;
use NeuronAI\Workflow\Workflow;
#[AsNeuronWorkflow('order_processing', persistence: 'workflow_files')]
final class OrderProcessingWorkflow extends Workflow
{
public function __construct(
private readonly Node\ReceiveOrder $receive,
private readonly Node\ApproveOrder $approve,
private readonly Node\CompleteOrder $complete,
) {
parent::__construct();
}
protected function nodes(): array
{
return [$this->receive, $this->approve, $this->complete];
}
}
use Errogaht\NeuronAiBundle\Workflow\WorkflowRunner;
use NeuronAI\Workflow\WorkflowState;
$result = $runner->run(
'order_processing',
new WorkflowState(['order_id' => 'order-42']),
);
if ($result->isInterrupted()) {
// Store workflow + workflowId against an authenticated application record.
$request = $result->interrupt;
}
$stream = $runner->stream('order_processing', new WorkflowState(['order_id' => 'order-42']));
foreach ($stream as $event) {
// Forward to Mercure, SSE, WebSocket, or a StreamedResponse.
}
$result = $stream->getReturn();
// Streaming
foreach ($analystAgent->stream(new UserMessage($prompt)) as $chunk) {
// send $chunk to a Symfony StreamedResponse, Mercure, etc.
}
// Structured output
$dto = $analystAgent->structured(new UserMessage($prompt), InvoiceDraft::class);
namespace App\Ai;
use Errogaht\NeuronAiBundle\Agent\AgentConfiguratorInterface;
use Errogaht\NeuronAiBundle\Agent\AgentContext;
use NeuronAI\Agent\AgentInterface;
final class TenantAgentConfigurator implements AgentConfiguratorInterface
{
public function configure(AgentInterface $agent, AgentContext $context): void
{
// Resolve and attach tenant-scoped history/tools here. Never trust model output as authorization.
}
}
use Errogaht\NeuronAiBundle\Async\AsyncAgentDispatcher;
$jobId = $dispatcher->dispatch('assistant', 'Prepare the report', threadId: 'report-42');
// AsyncWorkflowDispatcher uses the same bus/cache configuration.
$workflowJobId = $workflowDispatcher->dispatch('order_processing', ['order_id' => 'order-42']);