{"id":15026841,"url":"https://github.com/llm-agents-php/agents","last_synced_at":"2025-03-22T01:10:14.987Z","repository":{"id":254735865,"uuid":"847361171","full_name":"llm-agents-php/agents","owner":"llm-agents-php","description":"LLM Agents abstraction","archived":false,"fork":false,"pushed_at":"2024-09-15T18:44:39.000Z","size":251,"stargazers_count":88,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-14T03:08:28.340Z","etag":null,"topics":["llm","llm-agents","llm-tool-call","php","php8","php83"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/llm-agents-php.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-25T15:57:00.000Z","updated_at":"2025-03-11T13:33:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"043a43fa-6089-4bd1-a041-7a1002cd725d","html_url":"https://github.com/llm-agents-php/agents","commit_stats":null,"previous_names":["llm-agents-php/agents"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llm-agents-php%2Fagents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llm-agents-php%2Fagents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llm-agents-php%2Fagents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llm-agents-php%2Fagents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/llm-agents-php","download_url":"https://codeload.github.com/llm-agents-php/agents/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244890092,"owners_count":20527033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["llm","llm-agents","llm-tool-call","php","php8","php83"],"created_at":"2024-09-24T20:05:13.815Z","updated_at":"2025-03-22T01:10:14.949Z","avatar_url":"https://github.com/llm-agents-php.png","language":"PHP","readme":"\u003cp align=\"center\"\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://github.com/llm-agents-php\" target=\"_blank\"\u003e\n        \u003cpicture\u003e\n            \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/llm-agents-php/.github/master/assets/logo.png\"\u003e\n            \u003cimg width=\"200\" src=\"https://raw.githubusercontent.com/llm-agents-php/.github/master/assets/logo.png\" alt=\"LLM Agents Logo\"\u003e\n        \u003c/picture\u003e\n    \u003c/a\u003e\n    \u003cbr\u003e\n\u003c/p\u003e\n\n# LLM Agents PHP SDK\n\nLLM Agents is a PHP library for building and managing Language Model (LLM) based agents. It provides a framework for creating autonomous agents that can perform complex tasks, make decisions, and interact with various tools and APIs.\n\nThe library enables developers to integrate LLM capabilities into PHP applications efficiently, allowing for the creation of intelligent systems that can understand and respond to user inputs, process information, and carry out actions based on that processing.The library enables developers to integrate LLM capabilities into PHP applications efficiently.\n\n\u003e For a comprehensive explanation of LLM agents and their applications, you can read the article: A PHP dev's dream:\n\u003e [A PHP dev’s dream: An AI home that really gets you](https://butschster.medium.com/a-php-devs-dream-an-ai-home-that-really-gets-you-dd97ae2ca0b0)\n\n[![PHP](https://img.shields.io/packagist/php-v/llm-agents/agents.svg?style=flat-square)](https://packagist.org/packages/llm-agents/agents)\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/llm-agents/agents.svg?style=flat-square)](https://packagist.org/packages/llm-agents/agents)\n[![Total Downloads](https://img.shields.io/packagist/dt/llm-agents/agents.svg?style=flat-square)](https://packagist.org/packages/llm-agents/agents)\n\n\u003e For a complete example with sample agents and a CLI interface to interact with them, check out our sample application repository https://github.com/llm-agents-php/sample-app.\n\u003e \n\u003e This sample app demonstrates practical implementations and usage patterns of the LLM Agents library.\n\nThe package does not include any specific LLM implementation. Instead, it provides a framework for creating agents that\ncan interact with any LLM service or API.\n\n## ✨ Key Features\n\n- **🤖 Agent Creation:** Create and configure **LLM-based agents** in PHP with customizable behaviors.\n- **🔧 Tool Integration:** Seamlessly integrate various tools and APIs for agent use in PHP applications.\n- **🧠 Memory Management:** Support for agent memory, enabling information retention and recall across interactions.\n- **💡 Prompt Management:** Efficient handling of prompts and instructions to guide agent behavior.\n- **🔌 Extensible Architecture:** Easily add new agent types, tools, and capabilities to your PHP projects.\n- **🤝 Multi-Agent Support:** Build systems with multiple interacting agents for complex problem-solving scenarios in\n  PHP.\n\n## 📀 Installation\n\nYou can install the LLM Agents package via Composer:\n\n```bash\ncomposer require llm-agents/agents\n```\n\n## 💻 Usage\n\n### → Creating an Agent\n\nTo create an agent, you'll need to define its behavior, tools, and configuration. Here's a basic example:\n\n```php\nuse LLM\\Agents\\Agent\\AgentAggregate;\nuse LLM\\Agents\\Agent\\Agent;\nuse LLM\\Agents\\Solution\\Model;\nuse LLM\\Agents\\Solution\\ToolLink;\nuse LLM\\Agents\\Solution\\MetadataType;\nuse LLM\\Agents\\Solution\\SolutionMetadata;\n\nclass SiteStatusCheckerAgent extends AgentAggregate\n{\n    public const NAME = 'site_status_checker';\n\n    public static function create(): self\n    {\n        $agent = new Agent(\n            key: self::NAME,\n            name: 'Site Status Checker',\n            description: 'This agent checks the online status of websites.',\n            instruction: 'You are a website status checking assistant. Your goal is to help users determine if a website is online. Use the provided tool to check site availability. Give clear, concise responses about a site\\'s status.',\n        );\n\n        $aggregate = new self($agent);\n\n        $aggregate-\u003eaddMetadata(\n            new SolutionMetadata(\n                type: MetadataType::Memory,\n                key: 'check_availability',\n                content: 'Always check the site\\'s availability using the provided tool.',\n            ),\n            new SolutionMetadata(\n                type: MetadataType::Configuration,\n                key: 'max_tokens',\n                content: 500,\n            )\n        );\n\n        $model = new Model(model: 'gpt-4o-mini');\n        $aggregate-\u003eaddAssociation($model);\n\n        $aggregate-\u003eaddAssociation(new ToolLink(name: CheckSiteAvailabilityTool::NAME));\n\n        return $aggregate;\n    }\n}\n```\n\n### → Implementing a Tool\n\nNow, let's implement the tool used by this agent:\n\n```php\nuse LLM\\Agents\\Tool\\PhpTool;\nuse LLM\\Agents\\Tool\\ToolLanguage;\n\nclass CheckSiteAvailabilityTool extends PhpTool\n{\n    public const NAME = 'check_site_availability';\n\n    public function __construct()\n    {\n        parent::__construct(\n            name: self::NAME,\n            inputSchema: CheckSiteAvailabilityInput::class,\n            description: 'This tool checks if a given URL is accessible and returns its HTTP status code and response time.',\n        );\n    }\n\n    public function getLanguage(): ToolLanguage\n    {\n        return ToolLanguage::PHP;\n    }\n\n    public function execute(object $input): string\n    {\n        $ch = curl_init($input-\u003eurl);\n        curl_setopt_array($ch, [\n            CURLOPT_RETURNTRANSFER =\u003e true,\n            CURLOPT_HEADER =\u003e true,\n            CURLOPT_NOBODY =\u003e true,\n            CURLOPT_FOLLOWLOCATION =\u003e true,\n            CURLOPT_MAXREDIRS =\u003e 10,\n            CURLOPT_TIMEOUT =\u003e 30,\n        ]);\n\n        $startTime = microtime(true);\n        $response = curl_exec($ch);\n        $endTime = microtime(true);\n\n        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n        $responseTime = round(($endTime - $startTime) * 1000, 2);\n\n        curl_close($ch);\n\n        $isOnline = $statusCode \u003e= 200 \u0026\u0026 $statusCode \u003c 400;\n\n        return json_encode([\n            'status_code' =\u003e $statusCode,\n            'response_time_ms' =\u003e $responseTime,\n            'is_online' =\u003e $isOnline,\n        ]);\n    }\n}\n```\n\nAnd the input schema for the tool:\n\n```php\nuse Spiral\\JsonSchemaGenerator\\Attribute\\Field;\n\nclass CheckSiteAvailabilityInput\n{\n    public function __construct(\n        #[Field(title: 'URL', description: 'The full URL of the website to check')]\n        public readonly string $url,\n    ) {}\n}\n```\n\n### → Linking Agents\n\nLLM Agents supports creating complex systems by linking multiple agents together. This allows you to build hierarchical\nor collaborative agent networks. Here's how you can link one agent to another:\n\n#### Creating an Agent Link\n\nTo link one agent to another, you use the `AgentLink` class. Here's an example of how to modify our\n`SiteStatusCheckerAgent` to include a link to another agent:\n\n```php\nuse LLM\\Agents\\Solution\\AgentLink;\n\nclass SiteStatusCheckerAgent extends AgentAggregate\n{\n    public const NAME = 'site_status_checker';\n\n    public static function create(): self\n    {\n        // ... [previous agent setup code] ...\n\n        // Link to another agent\n        $aggregate-\u003eaddAssociation(\n            new AgentLink(\n                name: 'network_diagnostics_agent',\n                outputSchema: NetworkDiagnosticsOutput::class,\n            ),\n        );\n\n        return $aggregate;\n    }\n}\n```\n\nIn this example, we're linking a `network_diagnostics_agent`. The `outputSchema` parameter specifies the expected output\nformat from the linked agent. The output schema is used to standardize the data format that should be returned by the\nlinked agent.\n\n#### Using a Linked Agent\n\nWe don't provide an implementation for the linked agent here, but you can use the linked agent in your agent's\nexecution.\n\nHere's an example of how you might call the linked agent:\n\n```php\nuse LLM\\Agents\\Tool\\PhpTool;\nuse LLM\\Agents\\Agent\\AgentExecutor;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\ToolCallResultMessage;\nuse LLM\\Agents\\LLM\\Response\\ToolCalledResponse;\nuse LLM\\Agents\\Tool\\ToolExecutor;\nuse LLM\\Agents\\Tool\\ToolLanguage;\n\n/**\n * @extends PhpTool\u003cAskAgentInput\u003e\n */\nfinal class AskAgentTool extends PhpTool\n{\n    public const NAME = 'ask_agent';\n\n    public function __construct(\n        private readonly AgentExecutor $executor,\n        private readonly ToolExecutor $toolExecutor,\n    ) {\n        parent::__construct(\n            name: self::NAME,\n            inputSchema: AskAgentInput::class,\n            description: 'Ask an agent with given name to execute a task.',\n        );\n    }\n\n    public function getLanguage(): ToolLanguage\n    {\n        return ToolLanguage::PHP;\n    }\n\n    public function execute(object $input): string|\\Stringable\n    {\n        $prompt = \\sprintf(\n            \u003c\u003c\u003c'PROMPT'\n%s\nImportant rules:\n- Think before responding to the user.\n- Don not markup the content. Only JSON is allowed.\n- Don't write anything except the answer using JSON schema.\n- Answer in JSON using this schema:\n%s\nPROMPT\n            ,\n            $input-\u003equestion,\n            $input-\u003eoutputSchema,\n        );\n\n        while (true) {\n            $execution = $this-\u003eexecutor-\u003eexecute($input-\u003ename, $prompt);\n            $result = $execution-\u003eresult;\n            $prompt = $execution-\u003eprompt;\n\n            if ($result instanceof ToolCalledResponse) {\n                foreach ($result-\u003etools as $tool) {\n                    $functionResult = $this-\u003etoolExecutor-\u003eexecute($tool-\u003ename, $tool-\u003earguments);\n\n                    $prompt = $prompt-\u003ewithAddedMessage(\n                        new ToolCallResultMessage(\n                            id: $tool-\u003eid,\n                            content: [$functionResult],\n                        ),\n                    );\n                }\n\n                continue;\n            }\n\n            break;\n        }\n\n        return \\json_encode($result-\u003econtent);\n    }\n}\n```\n\nAnd the input schema for the tool:\n\n```php\nuse Spiral\\JsonSchemaGenerator\\Attribute\\Field;\n\nfinal class AskAgentInput\n{\n    public function __construct(\n        #[Field(title: 'Agent Name', description: 'The name of the agent to ask.')]\n        public string $name,\n        #[Field(title: 'Question', description: 'The question to ask the agent.')]\n        public string $question,\n        #[Field(title: 'Output Schema', description: 'The schema of the output.')]\n        public string $outputSchema,\n    ) {}\n}\n```\n\nAnd just add the tool to the agent that has linked agents. When the agent is executed, it will call the linked agent\nif it decides to do so.\n\n### → Executing an Agent\n\nTo execute an agent, you'll use the `AgentExecutor` class:\n\n```php\nuse LLM\\Agents\\AgentExecutor\\ExecutorInterface;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\Prompt;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\MessagePrompt;\n\nclass AgentRunner\n{\n    public function __construct(\n        private ExecutorInterface $executor,\n    ) {}\n\n    public function run(string $input): string\n    {\n        $prompt = new Prompt([\n            MessagePrompt::user($input),\n        ]);\n\n        $execution = $this-\u003eexecutor-\u003eexecute(\n            agent: MyAgent::NAME,\n            prompt: $prompt,\n        );\n\n        return (string)$execution-\u003eresult-\u003econtent;\n    }\n}\n\n// Usage\n$agentRunner = new AgentRunner($executor);\n$result = $agentRunner-\u003erun(\"Do something cool!\");\necho $result;\n```\n\nThis example demonstrates how to create a simple agent that can perform a specific task using a custom tool.\n\n### → Agent Memory and Prompts\n\nAgents can use memory and predefined prompts to guide their behavior:\n\n```php\nuse LLM\\Agents\\Solution\\SolutionMetadata;\nuse LLM\\Agents\\Solution\\MetadataType;\n\n// In your agent creation method:\n$aggregate-\u003eaddMetadata(\n    new SolutionMetadata(\n        type: MetadataType::Memory,\n        key: 'user_preference',\n        content: 'The user prefers concise answers.',\n    ),\n    \n    new SolutionMetadata(\n        type: MetadataType::Prompt,\n        key: 'check_google',\n        content: 'Check the status of google.com.',\n    ),\n    \n    new SolutionMetadata(\n        type: MetadataType::Prompt,\n        key: 'check_yahoo',\n        content: 'Check the status of yahoo.com.',\n    ),\n    \n    //...\n);\n```\n\n## Executor Interceptors\n\nThe package includes a powerful interceptor system for the executor. This allows developers to inject data\ninto prompts, modify execution options, and handle LLM responses at various stages of the execution process. Here's a\ndetailed look at each available interceptor:\n\n```php\nuse LLM\\Agents\\AgentExecutor\\ExecutorInterface;\nuse LLM\\Agents\\AgentExecutor\\ExecutorPipeline;\nuse LLM\\Agents\\AgentExecutor\\Interceptor\\GeneratePromptInterceptor;\nuse LLM\\Agents\\AgentExecutor\\Interceptor\\InjectModelInterceptor;\nuse LLM\\Agents\\AgentExecutor\\Interceptor\\InjectOptionsInterceptor;\nuse LLM\\Agents\\AgentExecutor\\Interceptor\\InjectResponseIntoPromptInterceptor;\nuse LLM\\Agents\\AgentExecutor\\Interceptor\\InjectToolsInterceptor;\n\n$executor = new ExecutorPipeline(...);\n\n$executor = $executor-\u003ewithInterceptor(\n    new GeneratePromptInterceptor(...),\n    new InjectModelInterceptor(...),\n    new InjectToolsInterceptor(...),\n    new InjectOptionsInterceptor(...),\n    new InjectResponseIntoPromptInterceptor(...),\n);\n\n$executor-\u003eexecute(...);\n```\n\n### Available Interceptors\n\n1. **GeneratePromptInterceptor**\n    - **Purpose**: Generates the initial prompt for the agent.\n    - **Functionality**:\n        - Uses the `AgentPromptGeneratorInterface` to create a comprehensive prompt.\n        - Incorporates agent instructions, memory, and user input into the prompt.\n    - **When to use**: Always include this interceptor to ensure proper prompt generation.\n\n2. **InjectModelInterceptor**\n    - **Purpose**: Injects the appropriate language model for the agent.\n    - **Functionality**:\n        - Retrieves the model associated with the agent.\n        - Adds the model information to the execution options.\n    - **When to use**: Include this interceptor when you want to ensure the correct model is used for each agent,\n      especially in multi-agent systems.\n\n3. **InjectToolsInterceptor**\n    - **Purpose**: Adds the agent's tools to the execution options.\n    - **Functionality**:\n        - Retrieves all tools associated with the agent.\n        - Converts tool schemas into a format understood by the LLM.\n        - Adds tool information to the execution options.\n    - **When to use**: Include this interceptor when your agent uses tools and you want them available during execution.\n\n4. **InjectOptionsInterceptor**\n    - **Purpose**: Incorporates additional configuration options for the agent.\n    - **Functionality**:\n        - Retrieves any custom configuration options defined for the agent.\n        - Adds these options to the execution options.\n    - **When to use**: Include this interceptor when you have agent-specific configuration that should be applied during\n      execution.\n\n5. **InjectResponseIntoPromptInterceptor**\n    - **Purpose**: Adds the LLM's response back into the prompt for continuous conversation.\n    - **Functionality**:\n        - Takes the LLM's response from the previous execution.\n        - Appends this response to the existing prompt.\n    - **When to use**: Include this interceptor in conversational agents or when context from previous interactions is\n      important.\n\n### Creating Custom Interceptors\n\nYou can create custom interceptors to add specialized behavior to your agent execution pipeline.\n\nHere's an example of a custom interceptor that adds time-aware and user-specific context to the prompt:\n\n```php\nuse LLM\\Agents\\AgentExecutor\\ExecutorInterceptorInterface;\nuse LLM\\Agents\\AgentExecutor\\ExecutionInput;\nuse LLM\\Agents\\AgentExecutor\\InterceptorHandler;\nuse LLM\\Agents\\Agent\\Execution;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\Prompt;\nuse LLM\\Agents\\LLM\\Response\\ChatResponse;\nuse Psr\\Log\\LoggerInterface;\n\nclass TokenCounterInterceptor implements ExecutorInterceptorInterface\n{\n    public function __construct(\n        private TokenCounterInterface $tokenCounter,\n        private LoggerInterface $logger,\n    ) {}\n\n    public function execute(ExecutionInput $input, InterceptorHandler $next): Execution\n    {\n        // Count tokens in the input prompt\n        $promptTokens = $this-\u003etokenCounter-\u003ecount((string) $input-\u003eprompt);\n\n        // Execute the next interceptor in the chain\n        $execution = $next($input);\n\n        // Count tokens in the response\n        $responseTokens = 0;\n        if ($execution-\u003eresult instanceof ChatResponse) {\n            $responseTokens = $this-\u003etokenCounter-\u003ecount((string) $execution-\u003eresult-\u003econtent);\n        }\n\n        // Log the token counts\n        $this-\u003elogger-\u003einfo('Token usage', [\n            'prompt_tokens' =\u003e $promptTokens,\n            'response_tokens' =\u003e $responseTokens,\n            'total_tokens' =\u003e $promptTokens + $responseTokens,\n        ]);\n\n        return $execution;\n    }\n}\n```\n\nThen, you can add your custom interceptor to the executor:\n\n```php\nuse Psr\\Log\\LoggerInterface;\n\n// Assume you have implementations of TokenCounterInterface and LoggerInterface\n$tokenCounter = new MyTokenCounter();\n$logger = new MyLogger();\n\n$executor = $executor-\u003ewithInterceptor(\n    new TokenCounterInterceptor($tokenCounter, $logger),\n);\n```\n\nThis example demonstrates how to create a more complex and useful interceptor. The token counting interceptor can be\nvaluable for monitoring API usage, optimizing prompt length, or ensuring you stay within token limits of your LLM\nprovider.\n\n**You can create various other types of interceptors to suit your specific needs, such as:**\n\n- Caching interceptors to store and retrieve responses for identical prompts\n- Rate limiting interceptors to control the frequency of API calls\n- Error handling interceptors to gracefully manage and log exceptions\n- Analytics interceptors to gather data on agent performance and usage patterns\n\n## Implementing Required Interfaces\n\nTo use the LLM Agents package, you'll need to implement the required interfaces in your project.\n\n### → LLMInterface\n\nIt serves as a bridge between your application and LLM you're using, such as OpenAI, Claude, etc.\n\n```php\nuse LLM\\Agents\\LLM\\ContextInterface;\nuse LLM\\Agents\\LLM\\LLMInterface;\nuse LLM\\Agents\\LLM\\OptionsInterface;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\MessagePrompt;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\PromptInterface as ChatPromptInterface;\nuse LLM\\Agents\\LLM\\Prompt\\PromptInterface;\nuse LLM\\Agents\\LLM\\Prompt\\Tool;\nuse LLM\\Agents\\LLM\\Response\\Response;\nuse OpenAI\\Client;\n\nfinal readonly class OpenAILLM implements LLMInterface\n{\n    public function __construct(\n        private Client $client,\n        private MessageMapper $messageMapper,\n        private StreamResponseParser $streamParser,\n    ) {}\n\n    public function generate(\n        ContextInterface $context,\n        PromptInterface $prompt,\n        OptionsInterface $options,\n    ): Response {\n        $request = $this-\u003ebuildOptions($options);\n\n        $messages = $prompt instanceof ChatPromptInterface\n            ? $prompt-\u003eformat()\n            : [MessagePrompt::user($prompt)-\u003etoChatMessage()];\n\n        $request['messages'] = array_map(\n            fn($message) =\u003e $this-\u003emessageMapper-\u003emap($message),\n            $messages\n        );\n\n        if ($options-\u003ehas('tools')) {\n            $request['tools'] = array_values(array_map(\n                fn(Tool $tool): array =\u003e $this-\u003emessageMapper-\u003emap($tool),\n                $options-\u003eget('tools')\n            ));\n        }\n\n        $stream = $this-\u003eclient-\u003echat()-\u003ecreateStreamed($request);\n\n        return $this-\u003estreamParser-\u003eparse($stream);\n    }\n\n    private function buildOptions(OptionsInterface $options): array\n    {\n        $defaultOptions = [\n            'temperature' =\u003e 0.8,\n            'max_tokens' =\u003e 120,\n            'model' =\u003e null,\n            // Add other default options as needed\n        ];\n\n        $result = array_intersect_key($options-\u003egetIterator()-\u003egetArrayCopy(), $defaultOptions);\n        $result += array_diff_key($defaultOptions, $result);\n\n        if (!isset($result['model'])) {\n            throw new \\InvalidArgumentException('Model is required');\n        }\n\n        return array_filter($result, fn($value) =\u003e $value !== null);\n    }\n}\n```\n\nHere is an example of `MessageMapper` that converts messages to the format required by the LLM API:\n\n```php\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\ChatMessage;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\Role;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\ToolCalledPrompt;\nuse LLM\\Agents\\LLM\\Prompt\\Chat\\ToolCallResultMessage;\nuse LLM\\Agents\\LLM\\Prompt\\Tool;\nuse LLM\\Agents\\LLM\\Response\\ToolCall;\n\nfinal readonly class MessageMapper\n{\n    public function map(object $message): array\n    {\n        if ($message instanceof ChatMessage) {\n            return [\n                'content' =\u003e $message-\u003econtent,\n                'role' =\u003e $message-\u003erole-\u003evalue,\n            ];\n        }\n\n        if ($message instanceof ToolCallResultMessage) {\n            return [\n                'content' =\u003e \\is_array($message-\u003econtent) ? \\json_encode($message-\u003econtent) : $message-\u003econtent,\n                'tool_call_id' =\u003e $message-\u003eid,\n                'role' =\u003e $message-\u003erole-\u003evalue,\n            ];\n        }\n\n        if ($message instanceof ToolCalledPrompt) {\n            return [\n                'content' =\u003e null,\n                'role' =\u003e Role::Assistant-\u003evalue,\n                'tool_calls' =\u003e \\array_map(\n                    static fn(ToolCall $tool): array =\u003e [\n                        'id' =\u003e $tool-\u003eid,\n                        'type' =\u003e 'function',\n                        'function' =\u003e [\n                            'name' =\u003e $tool-\u003ename,\n                            'arguments' =\u003e $tool-\u003earguments,\n                        ],\n                    ],\n                    $message-\u003etools,\n                ),\n            ];\n        }\n\n        if ($message instanceof Tool) {\n            return [\n                'type' =\u003e 'function',\n                'function' =\u003e [\n                    'name' =\u003e $message-\u003ename,\n                    'description' =\u003e $message-\u003edescription,\n                    'parameters' =\u003e [\n                            'type' =\u003e 'object',\n                            'additionalProperties' =\u003e $message-\u003eadditionalProperties,\n                        ] + $message-\u003eparameters,\n                    'strict' =\u003e $message-\u003estrict,\n                ],\n            ];\n        }\n\n        if ($message instanceof \\JsonSerializable) {\n            return $message-\u003ejsonSerialize();\n        }\n\n        throw new \\InvalidArgumentException('Invalid message type');\n    }\n}\n```\n\n## Prompt Generation\n\nIt plays a vital role in preparing the context and instructions for an agent before it processes a user's request. It\nensures that the agent has all necessary information, including its own instructions, memory, associated agents, and any\nrelevant session context.\n\n- System message with the agent's instruction and important rules.\n- System message with the agent's memory (experiences).\n- System message about associated agents (if any).\n- System message with session context (if provided).\n- User message with the actual prompt.\n\nYou can customize the prompt generation logic to suit your specific requirements.\n\nInstead of implementing the `AgentPromptGeneratorInterface` yourself, you can use the `llm-agents/prompt-generator`\npackage as an implementation. This package provides a flexible and extensible system for generating chat prompts with\nall required system and user messages for LLM agents.\n\n\u003e **Note:** Read full documentation of the `llm-agents/prompt-generator`\n\u003e package [here](https://github.com/llm-agents-php/prompt-generator)\n\nTo use it, first install the package:\n\n```bash\ncomposer require llm-agents/prompt-generator\n```\n\nThen, set it up in your project. Here's an example using Spiral Framework:\n\n```php\nuse LLM\\Agents\\PromptGenerator\\Interceptors\\AgentMemoryInjector;\nuse LLM\\Agents\\PromptGenerator\\Interceptors\\InstructionGenerator;\nuse LLM\\Agents\\PromptGenerator\\Interceptors\\LinkedAgentsInjector;\nuse LLM\\Agents\\PromptGenerator\\Interceptors\\UserPromptInjector;\nuse LLM\\Agents\\PromptGenerator\\PromptGeneratorPipeline;\n\nclass PromptGeneratorBootloader extends Bootloader\n{\n    public function defineSingletons(): array\n    {\n        return [\n            PromptGeneratorPipeline::class =\u003e static function (\n                LinkedAgentsInjector $linkedAgentsInjector,\n            ): PromptGeneratorPipeline {\n                $pipeline = new PromptGeneratorPipeline();\n\n                return $pipeline-\u003ewithInterceptor(\n                    new InstructionGenerator(),\n                    new AgentMemoryInjector(),\n                    $linkedAgentsInjector,\n                    new UserPromptInjector(),\n                    // Add more interceptors as needed\n                );\n            },\n        ];\n    }\n}\n```\n\n### → SchemaMapperInterface\n\nThis class is responsible for handling conversions between JSON schemas and PHP objects.\n\nWe provide a schema mapper package that you can use to implement the `SchemaMapperInterface` in your project. This\npackage is a super handy JSON Schema Mapper for the LLM Agents project.\n\n**To install the package:**\n\n```bash\ncomposer require llm-agents/json-schema-mapper\n```\n\n\u003e **Note:** Read full documentation of the `llm-agents/json-schema-mapper`\n\u003e package [here](https://github.com/llm-agents-php/schema-mapper)\n\n### → ContextFactoryInterface\n\nIt provides a clean way to pass execution-specific data through the system without tightly coupling components or overly\ncomplicating method signatures.\n\n```php\nuse LLM\\Agents\\LLM\\ContextFactoryInterface;\nuse LLM\\Agents\\LLM\\ContextInterface;\n\nfinal class ContextFactory implements ContextFactoryInterface\n{\n    public function create(): ContextInterface\n    {\n        return new class implements ContextInterface {\n            // Implement any necessary methods or properties for your context\n        };\n    }\n}\n```\n\n### → OptionsFactoryInterface\n\nThe options is a simple key-value store that allows you to store and retrieve configuration options that can be passed\nto LLM clients and other components. For example, you can pass a model name, max tokens, and other configuration options\nto an LLM client.\n\n```php\nuse LLM\\Agents\\LLM\\OptionsFactoryInterface;\nuse LLM\\Agents\\LLM\\OptionsInterface;\n\nfinal class OptionsFactory implements OptionsFactoryInterface\n{\n    public function create(): OptionsInterface\n    {\n        return new class implements OptionsInterface {\n            private array $options = [];\n\n            public function has(string $option): bool\n            {\n                return isset($this-\u003eoptions[$option]);\n            }\n\n            public function get(string $option, mixed $default = null): mixed\n            {\n                return $this-\u003eoptions[$option] ?? $default;\n            }\n\n            public function with(string $option, mixed $value): static\n            {\n                $clone = clone $this;\n                $clone-\u003eoptions[$option] = $value;\n                return $clone;\n            }\n\n            public function getIterator(): \\Traversable\n            {\n                return new \\ArrayIterator($this-\u003eoptions);\n            }\n        };\n    }\n}\n```\n\n## 🏗️ Architecture\n\nThe LLM Agents package is built around several key components:\n\n- **AgentInterface**: Defines the contract for all agents.\n- **AgentAggregate**: Implements AgentInterface and aggregates an Agent instance with other Solution objects.\n- **Agent**: Represents a single agent with its key, name, description, and instruction.\n- **Solution**: Abstract base class for various components like Model and ToolLink.\n- **AgentExecutor**: Responsible for executing agents and managing their interactions.\n- **Tool**: Represents a capability that an agent can use to perform tasks.\n\nFor a visual representation of the architecture, refer to the class diagram in the documentation.\n\n## 🎨 Class Diagram\n\nHere's a class diagram illustrating the key components of the LLM Agents PHP SDK:\n\n```mermaid\nclassDiagram\n    class AgentInterface {\n        \u003c\u003cinterface\u003e\u003e\n        +getKey() string\n        +getName() string\n        +getDescription() string\n        +getInstruction() string\n        +getTools() array\n        +getAgents() array\n        +getModel() Model\n        +getMemory() array\n        +getPrompts() array\n        +getConfiguration() array\n    }\n\n    class AgentAggregate {\n        -agent: Agent\n        -associations: array\n        +addAssociation(Solution)\n        +addMetadata(SolutionMetadata)\n    }\n\n    class Agent {\n        +key: string\n        +name: string\n        +description: string\n        +instruction: string\n        +isActive: bool\n    }\n\n    class Solution {\n        \u003c\u003cabstract\u003e\u003e\n        +name: string\n        +type: SolutionType\n        +description: string\n        -metadata: array\n        +addMetadata(SolutionMetadata)\n        +getMetadata() array\n    }\n\n    class SolutionMetadata {\n        +type: MetadataType\n        +key: string\n        +content: string|Stringable|int\n    }\n\n    class Model {\n        +model: string\n    }\n\n    class ToolLink {\n        +getName() string\n    }\n\n    class AgentLink {\n        +getName() string\n        +outputSchema: string\n    }\n\n    class ExecutorInterface {\n        \u003c\u003cinterface\u003e\u003e\n        +execute(string, string|Stringable|Prompt, ContextInterface, OptionsInterface, PromptContextInterface) Execution\n        +withInterceptor(ExecutorInterceptorInterface) self\n    }\n\n    class ToolInterface {\n        \u003c\u003cinterface\u003e\u003e\n        +getName() string\n        +getDescription() string\n        +getInputSchema() string\n        +getLanguage() ToolLanguage\n        +execute(object) string|Stringable\n    }\n\n    AgentAggregate ..|\u003e AgentInterface\n    AgentAggregate o-- Agent\n    AgentAggregate o-- Solution\n    Agent --|\u003e Solution\n    Model --|\u003e Solution\n    ToolLink --|\u003e Solution\n    AgentLink --|\u003e Solution\n    ExecutorInterface --\u003e AgentInterface\n    ExecutorInterface --\u003e ToolInterface\n    Solution o-- SolutionMetadata\n```\n\n## 🙌 Want to Contribute?\n\nThank you for considering contributing to the llm-agents-php community! We are open to all kinds of contributions. If\nyou want to:\n\n- 🤔 [Suggest a feature](https://github.com/llm-agents-php/agents/issues/new?assignees=\u0026labels=type%3A+enhancement\u0026projects=\u0026template=2-feature-request.yml\u0026title=%5BFeature%5D%3A+)\n- 🐛 [Report an issue](https://github.com/llm-agents-php/agents/issues/new?assignees=\u0026labels=type%3A+documentation%2Ctype%3A+maintenance\u0026projects=\u0026template=1-bug-report.yml\u0026title=%5BBug%5D%3A+)\n- 📖 [Improve documentation](https://github.com/llm-agents-php/agents/issues/new?assignees=\u0026labels=type%3A+documentation%2Ctype%3A+maintenance\u0026projects=\u0026template=4-docs-bug-report.yml\u0026title=%5BDocs%5D%3A+)\n- 👨‍💻 Contribute to the code\n\nYou are more than welcome. Before contributing, kindly check our [contribution guidelines](.github/CONTRIBUTING.md).\n\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=for-the-badge)](https://conventionalcommits.org)\n\n## ⚖️ License\n\nLLM Agents is open-source software licensed under the [MIT license](https://opensource.org/licenses/MIT).\n\n[![Licence](https://img.shields.io/github/license/llm-agents-php/agents?style=for-the-badge\u0026color=blue)](./LICENSE.md)\n","funding_links":[],"categories":["LLMs \u0026 AI APIs","Natural Language Processing"],"sub_categories":["Recommended core stack","AI Agents \u0026 Orchestration"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllm-agents-php%2Fagents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fllm-agents-php%2Fagents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllm-agents-php%2Fagents/lists"}