├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug.yml └── workflows │ ├── fix-php-code-style-issues.yml │ └── run-tests.yml ├── assets └── images │ ├── demo.gif │ └── grok-client.png ├── src ├── Enums │ ├── Role.php │ ├── DefaultConfig.php │ └── Model.php ├── Contracts │ └── ClientInterface.php ├── Config │ ├── GrokConfig.php │ └── ChatOptions.php ├── Traits │ └── HandlesRequests.php ├── Clients │ ├── Vision.php │ └── GrokClient.php ├── Testing │ ├── ClientFake.php │ └── VisionFake.php ├── Utils │ ├── MessageBuilder.php │ └── ImageProcessor.php └── Exceptions │ └── GrokException.php ├── phpunit.xml.dist ├── LICENSE ├── CHANGELOG.md ├── composer.json └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: thefeqy 2 | -------------------------------------------------------------------------------- /assets/images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grok-php/client/HEAD/assets/images/demo.gif -------------------------------------------------------------------------------- /assets/images/grok-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grok-php/client/HEAD/assets/images/grok-client.png -------------------------------------------------------------------------------- /src/Enums/Role.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/fix-php-code-style-issues.yml: -------------------------------------------------------------------------------- 1 | name: Fix PHP code style issues 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | php-code-styling: 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 5 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | with: 20 | ref: ${{ github.head_ref }} 21 | 22 | - name: Fix PHP code style issues 23 | uses: aglipanci/laravel-pint-action@2.5 24 | 25 | - name: Commit changes 26 | uses: stefanzweifel/git-auto-commit-action@v5 27 | with: 28 | commit_message: Fix styling 29 | -------------------------------------------------------------------------------- /src/Contracts/ClientInterface.php: -------------------------------------------------------------------------------- 1 | value, 19 | public ?int $timeout = null 20 | ) { 21 | $this->apiKey = $apiKey ?? getenv('GROK_API_KEY'); 22 | 23 | if (! $this->apiKey) { 24 | throw GrokException::missingApiKey(); 25 | } 26 | $this->timeout = $timeout ?? (int) DefaultConfig::TIMEOUT->value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Config/ChatOptions.php: -------------------------------------------------------------------------------- 1 | model = $model ?: Model::tryFrom(DefaultConfig::MODEL->value) ?: Model::GROK_2; 29 | 30 | $this->temperature = $temperature ?? (float) DefaultConfig::TEMPERATURE->value; 31 | 32 | $this->stream = $stream ?? filter_var(DefaultConfig::STREAMING->value, FILTER_VALIDATE_BOOLEAN); 33 | 34 | $this->responseFormat = $responseFormat ? (array) $responseFormat : null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Grok PHP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/Traits/HandlesRequests.php: -------------------------------------------------------------------------------- 1 | httpClient->post($endpoint, [ 32 | 'headers' => [ 33 | 'Authorization' => "Bearer {$this->apiKey}", 34 | 'Content-Type' => 'application/json', 35 | ], 36 | 'json' => $payload, 37 | ]); 38 | 39 | return json_decode($response->getBody()->getContents(), true, flags: JSON_THROW_ON_ERROR); 40 | } catch (RequestException $e) { 41 | throw GrokException::fromResponse($e->getResponse()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Clients/Vision.php: -------------------------------------------------------------------------------- 1 | value); 31 | $base64Image = ImageProcessor::getBase64Image($imagePathOrUrl); 32 | $messages = MessageBuilder::build($prompt, $base64Image, $model); 33 | $options = new ChatOptions(model: $model, temperature: 0.7, stream: false); 34 | 35 | return $this->client->chat($messages, $options); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Enums/Model.php: -------------------------------------------------------------------------------- 1 | value => self::GROK_2_VISION_1212, 31 | self::GROK_2_VISION->value => self::GROK_2_VISION, 32 | self::GROK_2_VISION_LATEST->value => self::GROK_2_VISION_LATEST, 33 | self::GROK_2_1212->value => self::GROK_2_1212, 34 | self::GROK_2->value => self::GROK_2, 35 | self::GROK_2_LATEST->value => self::GROK_2_LATEST, 36 | self::GROK_BETA->value => self::GROK_BETA, 37 | self::GROK_VISION_BETA->value => self::GROK_VISION_BETA, 38 | default => throw new \InvalidArgumentException('Invalid model value'), 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | paths: 9 | - '**.php' 10 | - '.github/workflows/run-tests.yml' 11 | - 'phpunit.xml.dist' 12 | - 'composer.json' 13 | - 'composer.lock' 14 | 15 | jobs: 16 | test: 17 | runs-on: ${{ matrix.os }} 18 | timeout-minutes: 5 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [ubuntu-latest, windows-latest] 23 | php: [8.2, 8.3, 8.4] 24 | stability: [prefer-lowest, prefer-stable] 25 | 26 | name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} 27 | 28 | steps: 29 | - name: Checkout Code 30 | uses: actions/checkout@v4 31 | 32 | - name: Setup PHP ${{ matrix.php }} 33 | uses: shivammathur/setup-php@v2 34 | with: 35 | php-version: ${{ matrix.php }} 36 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 37 | coverage: none 38 | 39 | - name: Install Dependencies (${{ matrix.stability }}) 40 | run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction 41 | 42 | - name: Configure PHPUnit 43 | run: | 44 | cp phpunit.xml.dist phpunit.xml 45 | sed -i 's|||g' phpunit.xml 46 | 47 | - name: List Installed Dependencies 48 | run: composer show -D 49 | 50 | - name: Run PHPUnit Tests 51 | run: vendor/bin/phpunit 52 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `GrokPHP/Client` will be documented in this file. 4 | This project follows [Semantic Versioning](https://semver.org/). 5 | 6 | --- 7 | 8 | ## [v1.3.0] - 2025-02-24 9 | ### New Features 10 | - **Vision API Support:** Added a new **Vision** class to analyze images using the Grok-2-Vision models. 11 | - Supports `grok-2-vision`, `grok-2-vision-latest`, and `grok-2-vision-1212` models. 12 | - Allows image analysis through `vision()->analyze($image, $message)`. 13 | - Automatically validates supported models to prevent incorrect usage. 14 | 15 | --- 16 | 17 | ## [v1.2.0] - 2025-02-24 18 | ### Improvements 19 | - **Replaced Pest with PHPUnit** for testing, aligning with industry standards. 20 | - **Enhanced exception handling** to provide more structured and informative error responses. 21 | - **Updated GitHub Actions workflow** to support PHP 8.2, 8.3, and 8.4. 22 | 23 | --- 24 | 25 | ## [v1.1.1] - 2025-02-11 26 | ### Improvements 27 | - Refactored code formatting for better readability and maintainability. 28 | - Implemented **GitHub Actions CI** to run tests automatically on each push to the `main` branch. 29 | 30 | --- 31 | 32 | ## [v1.1.0] - 2025-02-07 33 | ### Enhancements 34 | - **Upgraded to version `1.1.0`** with internal improvements and stability fixes. 35 | 36 | --- 37 | 38 | ## [v1.0.0] - 2025-02-06 39 | ### Initial Release 40 | - **Launched `GrokPHP/Client` v1.0.0** – a robust, framework-agnostic PHP client for interacting with Grok AI APIs. 41 | 42 | --- 43 | 44 | ### Notes 45 | - For detailed usage, refer to the [README.md](README.md). 46 | - Found an issue? Report it on [GitHub Issues](https://github.com/grok-php/client/issues). 47 | -------------------------------------------------------------------------------- /src/Testing/ClientFake.php: -------------------------------------------------------------------------------- 1 | 'application/json'], json_encode([ 18 | 'id' => '7c51076a-e4cc-4855-8dbe-66c26818e35f', 19 | 'object' => 'chat.completion', 20 | 'created' => time(), 21 | 'model' => 'grok-2-1212', 22 | 'choices' => [ 23 | [ 24 | 'index' => 0, 25 | 'message' => [ 26 | 'role' => 'assistant', 27 | 'content' => json_encode($data ?? [ 28 | 'framework_name' => 'Laravel', 29 | 'release_date' => '2011', 30 | 'programming_language' => 'PHP', 31 | ], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), 32 | 'refusal' => null, 33 | ], 34 | 'finish_reason' => 'stop', 35 | ], 36 | ], 37 | 'usage' => [ 38 | 'prompt_tokens' => 72, 39 | 'completion_tokens' => 31, 40 | 'reasoning_tokens' => 0, 41 | 'total_tokens' => 103, 42 | 'prompt_tokens_details' => [ 43 | 'text_tokens' => 72, 44 | 'audio_tokens' => 0, 45 | 'image_tokens' => 0, 46 | 'cached_tokens' => 0, 47 | ], 48 | ], 49 | 'system_fingerprint' => 'fp_5c0c5bd9d9', 50 | ], JSON_THROW_ON_ERROR)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Utils/MessageBuilder.php: -------------------------------------------------------------------------------- 1 | 'text', 'text' => $prompt]]; 31 | 32 | if ($base64Image) { 33 | $content[] = [ 34 | 'type' => 'image_url', 35 | 'image_url' => [ 36 | 'url' => "data:image/jpeg;base64,{$base64Image}", 37 | 'detail' => 'high', 38 | ], 39 | ]; 40 | } 41 | 42 | return [['role' => 'user', 'content' => $content]]; 43 | } 44 | 45 | /** 46 | * Checks if the given model supports vision-based input. 47 | */ 48 | private static function supportsVision(Model $model): bool 49 | { 50 | return in_array($model->value, [ 51 | Model::GROK_2_VISION->value, 52 | Model::GROK_2_VISION_LATEST->value, 53 | Model::GROK_2_VISION_1212->value, 54 | ], true); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Utils/ImageProcessor.php: -------------------------------------------------------------------------------- 1 | get($imageUrl, ['http_errors' => false]); 42 | 43 | if ($response->getStatusCode() !== 200) { 44 | throw new GrokException("Failed to fetch image from URL: {$imageUrl}", 400, GrokException::ERROR_TYPES['invalid_request']); 45 | } 46 | 47 | return base64_encode($response->getBody()->getContents()); 48 | } catch (\Exception|GuzzleException $e) { 49 | throw new GrokException("Error fetching image from URL: {$imageUrl}", 400, GrokException::ERROR_TYPES['invalid_request']); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report an Issue or Bug with the Package 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | We're sorry to hear you have a problem. Can you help us solve it by providing the following details. 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: What happened? 14 | description: What did you expect to happen? 15 | placeholder: I cannot currently do X thing because when I do, it breaks X thing. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: how-to-reproduce 20 | attributes: 21 | label: How to reproduce the bug 22 | description: How did this occur, please add any config values used and provide a set of reliable steps if possible. 23 | placeholder: When I do X I see Y. 24 | validations: 25 | required: true 26 | - type: input 27 | id: package-version 28 | attributes: 29 | label: Package Version 30 | description: What version of our Package are you running? Please be as specific as possible 31 | placeholder: 1.2.0 32 | validations: 33 | required: true 34 | - type: input 35 | id: php-version 36 | attributes: 37 | label: PHP Version 38 | description: What version of PHP are you running? Please be as specific as possible 39 | placeholder: 8.2.0 40 | validations: 41 | required: true 42 | - type: input 43 | id: grok-model 44 | attributes: 45 | label: Grok Model 46 | description: What version of the Grok Model are you running? Please be as specific as possible 47 | placeholder: grok-2-latest 48 | validations: 49 | required: true 50 | - type: dropdown 51 | id: operating-systems 52 | attributes: 53 | label: Which operating systems does with happen with? 54 | description: You may select more than one. 55 | multiple: true 56 | options: 57 | - macOS 58 | - Windows 59 | - Linux 60 | - type: textarea 61 | id: notes 62 | attributes: 63 | label: Notes 64 | description: Use this field to provide any other notes that you feel might be relevant to the issue. 65 | validations: 66 | required: false 67 | -------------------------------------------------------------------------------- /src/Clients/GrokClient.php: -------------------------------------------------------------------------------- 1 | apiKey = $config->apiKey; 32 | $this->httpClient = $httpClient ?? new Client([ 33 | 'base_uri' => $config->baseUri, 34 | 'timeout' => $config->timeout ?? (int) DefaultConfig::TIMEOUT->value, 35 | ]); 36 | } 37 | 38 | /** 39 | * Returns the API key from the configuration. 40 | */ 41 | public function getApiKey(): string 42 | { 43 | return $this->config->apiKey; 44 | } 45 | 46 | /** 47 | * Overrides the HTTP client (useful for testing). 48 | * 49 | * @param Client $client Custom Guzzle client. 50 | */ 51 | public function setHttpClient(Client $client): void 52 | { 53 | $this->httpClient = $client; 54 | } 55 | 56 | /** 57 | * Sends a chat request to Grok API. 58 | * 59 | * @param array $messages Chat messages 60 | * @param ChatOptions $options Chat configuration 61 | * @return array API response 62 | * 63 | * @throws GrokException 64 | */ 65 | public function chat(array $messages, ChatOptions $options): array 66 | { 67 | return $this->sendRequest('chat/completions', [ 68 | 'model' => $options->model->value, 69 | 'messages' => $messages, 70 | 'temperature' => $options->temperature, 71 | 'stream' => $options->stream, 72 | 'response_format' => $options->responseFormat, 73 | ]); 74 | } 75 | 76 | /** 77 | * Returns a Vision instance for image analysis. 78 | */ 79 | public function vision(): Vision 80 | { 81 | return new Vision($this); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grok-php/client", 3 | "description": "Grok PHP Client is a robust and community-driven PHP client library for seamless integration with Grok AI API, offering efficient access to advanced AI and data processing capabilities.", 4 | "type": "library", 5 | "license": "MIT", 6 | "homepage": "https://github.com/grok-php/client", 7 | "support": { 8 | "issues": "https://github.com/grok-php/client/issues", 9 | "source": "https://github.com/grok-php/client" 10 | }, 11 | "autoload": { 12 | "psr-4": { 13 | "GrokPHP\\Client\\": "src/" 14 | } 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "GrokPHP\\Client\\Tests\\": "tests/" 19 | } 20 | }, 21 | "authors": [ 22 | { 23 | "name": "Grok PHP", 24 | "email": "thefeqy@gmail.com", 25 | "role": "organization" 26 | }, 27 | { 28 | "name": "Muhammed Elfeqy", 29 | "email": "thefeqy@gmail.com", 30 | "homepage": "https://github.com/thefeqy", 31 | "role": "creator" 32 | } 33 | ], 34 | "keywords": [ 35 | "Grok AI", 36 | "AI API", 37 | "PHP AI SDK", 38 | "Generative AI", 39 | "Machine Learning", 40 | "Natural Language Processing", 41 | "Large Language Model", 42 | "NLP", 43 | "AI Integration", 44 | "AI Client", 45 | "AI SDK", 46 | "REST API", 47 | "AI Text Generation", 48 | "Deep Learning", 49 | "Artificial Intelligence", 50 | "API Wrapper", 51 | "Text Processing", 52 | "AI-powered Applications", 53 | "AI Chatbot", 54 | "Language Model", 55 | "AI Research", 56 | "AI Developer Tools" 57 | ], 58 | "require": { 59 | "php": "^8.2 || ^8.3 || ^8.4", 60 | "guzzlehttp/guzzle": "^7.9" 61 | }, 62 | "require-dev": { 63 | "laravel/pint": "^1.20", 64 | "mockery/mockery": "^1.6", 65 | "orchestra/testbench": "^9.0", 66 | "phpstan/phpstan": "^1.12", 67 | "phpunit/phpunit": "^11" 68 | }, 69 | "minimum-stability": "dev", 70 | "prefer-stable": true, 71 | "scripts": { 72 | "test": "vendor/bin/phpunit", 73 | "test:types": "phpstan analyse src --ansi", 74 | "format": "vendor/bin/pint" 75 | }, 76 | "config": { 77 | "sort-packages": true, 78 | "preferred-install": "dist", 79 | "optimize-autoloader": true, 80 | "allow-plugins": { 81 | "pestphp/pest-plugin": true 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Testing/VisionFake.php: -------------------------------------------------------------------------------- 1 | 'application/json'], json_encode([ 18 | 'id' => '0b0fa149-6125-4bb8-a320-8b8c6cd24dec', 19 | 'object' => 'chat.completion', 20 | 'created' => time(), 21 | 'model' => 'grok-2-vision-1212', 22 | 'choices' => [ 23 | [ 24 | 'index' => 0, 25 | 'message' => [ 26 | 'role' => 'assistant', 27 | 'content' => 'The image shows a cat and a dog interacting affectionately. The cat, which is gray with some white patches, is nuzzling or rubbing its head against the dog. The dog, which appears to be a golden retriever, is lying down and looking at the cat. The scene is set indoors with a soft, natural light coming from a window in the background.', 28 | 'refusal' => null, 29 | ], 30 | 'finish_reason' => 'stop', 31 | ], 32 | ], 33 | 'usage' => [ 34 | 'prompt_tokens' => 269, 35 | 'completion_tokens' => 74, 36 | 'total_tokens' => 343, 37 | ], 38 | 'system_fingerprint' => 'fp_08923a0247', 39 | ], JSON_THROW_ON_ERROR)); 40 | } 41 | 42 | /** 43 | * Fake a failed API response for Vision analysis. 44 | * 45 | * @throws JsonException 46 | */ 47 | public static function fakeVisionInvalidModelResponse(): Response 48 | { 49 | return new Response(400, ['Content-Type' => 'application/json'], json_encode([ 50 | 'code' => 'Client specified an invalid argument', 51 | 'error' => 'The model does not support image input but some images are present in the request.', 52 | ], JSON_THROW_ON_ERROR)); 53 | } 54 | 55 | /** 56 | * Fake a failed API response when an image is not found. 57 | * 58 | * @throws JsonException 59 | */ 60 | public static function fakeVisionImageNotFoundResponse(): Response 61 | { 62 | return new Response(400, ['Content-Type' => 'application/json'], json_encode([ 63 | 'code' => 'Unrecoverable data loss or corruption', 64 | 'error' => 'Failed to fetch image from URL', 65 | ], JSON_THROW_ON_ERROR)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Exceptions/GrokException.php: -------------------------------------------------------------------------------- 1 | 'Invalid Request', 22 | 'authentication_error' => 'Authentication Error', 23 | 'invalid_api_key' => 'Invalid API Key', 24 | 'unsupported_model_for_images' => 'Unsupported Model for Images', 25 | ]; 26 | 27 | /** 28 | * Map error types to their corresponding HTTP status codes. 29 | */ 30 | const HTTP_STATUS_CODES = [ 31 | 'invalid_request' => 422, // JSON deserialization error 32 | 'authentication_error' => 400, // No API key provided 33 | 'invalid_api_key' => 400, // Incorrect API key 34 | ]; 35 | 36 | /** 37 | * Constructor. 38 | */ 39 | public function __construct( 40 | string $message, 41 | int $code, 42 | protected string $type = 'invalid_request', 43 | protected array $headers = [], 44 | protected ?stdClass $responseBody = null 45 | ) { 46 | parent::__construct($message, $code); 47 | } 48 | 49 | /** 50 | * Get the error type. 51 | */ 52 | public function getType(): string 53 | { 54 | return $this->type; 55 | } 56 | 57 | /** 58 | * Get the response headers. 59 | */ 60 | public function getHeaders(): array 61 | { 62 | return $this->headers; 63 | } 64 | 65 | /** 66 | * Get the API response body. 67 | */ 68 | public function getResponseBody(): ?stdClass 69 | { 70 | return $this->responseBody; 71 | } 72 | 73 | /** 74 | * Convert the exception details to an array. 75 | * 76 | * @throws JsonException 77 | */ 78 | public function toArray(): array 79 | { 80 | return [ 81 | 'message' => $this->getMessage(), 82 | 'type' => $this->getType(), 83 | 'code' => $this->getCode(), 84 | 'headers' => $this->getHeaders(), 85 | 'response_body' => $this->responseBody ? json_decode(json_encode($this->responseBody, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR) : null, 86 | ]; 87 | } 88 | 89 | /** 90 | * Convert the exception details to JSON. 91 | * 92 | * @throws JsonException 93 | */ 94 | public function toJson(): string 95 | { 96 | return json_encode($this->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); 97 | } 98 | 99 | /** 100 | * Create a `GrokException` from an API response. 101 | * 102 | * @throws JsonException 103 | */ 104 | public static function fromResponse(ResponseInterface $response): self 105 | { 106 | $body = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); 107 | $errorMessage = $body['error'] ?? 'Unknown error occurred'; 108 | $statusCode = $response->getStatusCode(); 109 | 110 | // Determine the error type based on the API response 111 | $errorType = match ($statusCode) { 112 | 400 => match (true) { 113 | str_contains(strtolower($errorMessage), 'incorrect api key') => 'invalid_api_key', 114 | str_contains(strtolower($errorMessage), 'no api key provided') => 'authentication_error', 115 | default => 'invalid_request', 116 | }, 117 | 422 => 'invalid_request', 118 | default => 'invalid_request', 119 | }; 120 | 121 | return new self( 122 | $errorMessage, 123 | $statusCode, 124 | $errorType, 125 | $response->getHeaders(), 126 | json_decode(json_encode($body, JSON_THROW_ON_ERROR), false, 512, JSON_THROW_ON_ERROR) 127 | ); 128 | } 129 | 130 | /** 131 | * Handle exceptions from Guzzle. 132 | * 133 | * @throws JsonException 134 | */ 135 | public static function fromGuzzleException(ClientException $exception): self 136 | { 137 | return self::fromResponse($exception->getResponse()); 138 | } 139 | 140 | /** 141 | * Create an exception for a missing API key. 142 | */ 143 | public static function missingApiKey(): self 144 | { 145 | return new self( 146 | 'No API key provided. Specify your API key in an Authorization header using Bearer auth.', 147 | 400, 148 | 'authentication_error' 149 | ); 150 | } 151 | 152 | /** 153 | * Create an exception for an invalid API key. 154 | */ 155 | public static function invalidApiKey(): self 156 | { 157 | return new self( 158 | 'Incorrect API key provided. Obtain an API key from https://console.x.ai.', 159 | 400, 160 | 'invalid_api_key' 161 | ); 162 | } 163 | 164 | /** 165 | * Create an exception for an invalid request (JSON deserialization error). 166 | */ 167 | public static function invalidRequest(): self 168 | { 169 | return new self( 170 | 'Failed to deserialize the JSON body into the target type. Ensure request structure is correct.', 171 | 422, 172 | 'invalid_request' 173 | ); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧠 Grok PHP Client 2 | 3 | ![Grok PHP Client](assets/images/grok-client.png) 4 | 5 | **A lightweight, framework-agnostic PHP client for interacting with Grok AI APIs.** 6 | Supports **PHP 8.2+**, built with **OOP best practices**, and **fully type-safe**. 7 | 8 | [![Latest Version](https://img.shields.io/packagist/v/grok-php/client)](https://packagist.org/packages/grok-php/client) 9 | [![PHP Version](https://img.shields.io/badge/PHP-8.2%2B-blue)](https://php.net) 10 | [![Total Downloads](https://img.shields.io/packagist/dt/grok-php/client)](https://packagist.org/packages/grok-php/client) 11 | ![GitHub Workflow Status](https://github.com/grok-php/client/actions/workflows/run-tests.yml/badge.svg) 12 | [![License](https://img.shields.io/badge/license-MIT-brightgreen)](LICENSE) 13 | 14 | --- 15 | 16 | ## 📖 Table of Contents 17 | - [Features](#features) 18 | - [Installation](#installation) 19 | - [Quick Start](#quick-start) 20 | - [Basic Usage](#basic-usage) 21 | - [Vision Analysis](#vision-analysis-image-recognition) 22 | - [Advanced Configuration](#advanced-configuration) 23 | - [Available Grok AI Models](#available-grok-ai-models) 24 | - [Streaming Responses](#streaming-responses) 25 | - [Response format](#response-format) 26 | - [Error Handling](#error-handling) 27 | - [Testing](#testing) 28 | - [Security](#security) 29 | - [Contributing](#contributing) 30 | - [License](#license) 31 | 32 | --- 33 | 34 | ## **Features** 35 | 36 | ![Grok PHP Client Demo](assets/images/demo.gif) 37 | 38 | - **Easy Integration** – Seamlessly connects with Grok AI APIs. 39 | - **Modern PHP Features** – Utilizes PHP 8.2+ features like enums and traits. 40 | - **Framework Agnostic** – Works with any PHP project, CLI scripts, or web applications. 41 | - **Streaming Support** – Built-in support for real-time responses. 42 | - **Lightweight & Efficient** – Optimized with PSR-4 autoloading and minimal dependencies. 43 | 44 | --- 45 | 46 | ## **Installation** 47 | 48 | Install via Composer: 49 | ```sh 50 | composer require grok-php/client 51 | ``` 52 | 53 | ### **Requirements:** 54 | - PHP 8.2+ 55 | - Composer 2.0+ 56 | 57 | --- 58 | 59 | ## **Quick Start** 60 | 61 | ### **Basic Usage** 62 | 63 | ```php 64 | use GrokPHP\Client\Clients\GrokClient; 65 | use GrokPHP\Client\Config\GrokConfig; 66 | use GrokPHP\Client\Config\ChatOptions; 67 | use GrokPHP\Client\Enums\Model; 68 | 69 | // Initialize the client 70 | $config = new GrokConfig('your-api-key'); 71 | $client = new GrokClient($config); 72 | 73 | // Define messages 74 | $messages = [ 75 | ['role' => 'system', 'content' => 'You are an AI assistant.'], 76 | ['role' => 'user', 'content' => 'Tell me a joke!'] 77 | ]; 78 | 79 | // Call API 80 | $options = new ChatOptions(model: Model::GROK_2, temperature: 0.7, stream: false); 81 | $response = $client->chat($messages, $options); 82 | 83 | echo "AI Response: " . $response['choices'][0]['message']['content']; 84 | ``` 85 | 86 | ### **Defaults Used:** 87 | - Model: `grok-2` 88 | - Temperature: `0.7` 89 | - Streaming: `false` 90 | 91 | --- 92 | 93 | ### Vision Analysis (Image Recognition) 94 | The **Vision API** allows you to send images for analysis using **Grok-2-Vision** models. 95 | 96 | ```php 97 | use GrokPHP\Client\Clients\GrokClient; 98 | use GrokPHP\Client\Config\GrokConfig; 99 | 100 | // Initialize the client 101 | $config = new GrokConfig('your-api-key'); 102 | $client = new GrokClient($config); 103 | 104 | // Use the Vision API to analyze an image 105 | $response = $client->vision()->analyze('https://example.com/image.jpg', 'Describe this image.'); 106 | 107 | echo "Vision Response: " . $response['choices'][0]['message']['content']; 108 | ``` 109 | 110 | #### Supported Models for Vision 111 | | Model Enum | API Model Name | Description | 112 | |-----------------------------|----------------------|----------------------------------| 113 | | `Model::GROK_2_VISION` | grok-2-vision | Base Vision Model | 114 | | `Model::GROK_2_VISION_LATEST` | grok-2-vision-latest | Latest Vision Model | 115 | | `Model::GROK_2_VISION_1212` | grok-2-vision-1212 | Default model for image analysis | 116 | 117 | **Note:** If you attempt to use an **unsupported model** for vision, an exception will be thrown. 118 | 119 | --- 120 | ### **Advanced Configuration** 121 | 122 | ```php 123 | use GrokPHP\Client\Clients\GrokClient; 124 | use GrokPHP\Client\Config\GrokConfig; 125 | use GrokPHP\Client\Config\ChatOptions; 126 | use GrokPHP\Client\Enums\Model; 127 | 128 | // Load API key from environment 129 | $apiKey = getenv('GROK_API_KEY'); 130 | 131 | $config = new GrokConfig($apiKey); 132 | $client = new GrokClient($config); 133 | 134 | // Define messages 135 | $messages = [ 136 | ['role' => 'system', 'content' => 'You are a helpful assistant.'], 137 | ['role' => 'user', 'content' => 'How do black holes form?'] 138 | ]; 139 | 140 | // Custom API settings 141 | $options = new ChatOptions( 142 | model: Model::GROK_2_LATEST, 143 | temperature: 1.2, 144 | stream: false 145 | ); 146 | 147 | $response = $client->chat($messages, $options); 148 | echo "AI Says: " . $response['choices'][0]['message']['content']; 149 | ``` 150 | 151 | --- 152 | 153 | ## **Available Grok AI Models** 154 | 155 | Grok AI offers multiple models optimized for different use cases. 156 | These models are available in the `Model` enum inside our package: 157 | 📄 `src/Enums/Model.php` 158 | 159 | | Model Enum | API Model Name | Description | 160 | |------------------------------|----------------------|-----------------------------------------------------| 161 | | `Model::GROK_VISION_BETA` | grok-vision-beta | Experimental vision-enabled model | 162 | | `Model::GROK_2_VISION` | grok-2-vision | Advanced multi-modal vision model | 163 | | `Model::GROK_2_VISION_LATEST` | grok-2-vision-latest | Latest iteration of Grok vision models | 164 | | `Model::GROK_2_VISION_1212` | grok-2-vision-1212 | Enhanced vision model with performance improvements | 165 | | `Model::GROK_2_1212` | grok-2-1212 | Optimized chat model | 166 | | `Model::GROK_2` | grok-2 | Default general-purpose Grok model | 167 | | `Model::GROK_2_LATEST` | grok-2-latest | Latest iteration of Grok-2 | 168 | | `Model::GROK_BETA` | grok-beta | Experimental beta model | 169 | 170 | #### **Default model used:** `Model::GROK_2` 171 | 172 | --- 173 | 174 | ## **Streaming Responses** 175 | 176 | The Grok API supports streaming responses for real-time interaction. 177 | Enable it by setting `stream: true`: 178 | 179 | ```php 180 | $options = new ChatOptions(model: Model::GROK_2, temperature: 0.7, stream: true); 181 | $response = $client->chat($messages, $options); 182 | ``` 183 | Streaming can be useful for chatbots, real-time applications, and CLI assistants. 184 | 185 | --- 186 | 187 | ## **Response format** 188 | 189 | The Grok API supports setting a response format, also refered to structured outputs, for the `grok-2-1212` model. 190 | 191 | ```php 192 | $options = new ChatOptions(model: Model::GROK_2_1212, temperature: 0.7, stream: false, responseFormat: ['type' => 'json_object']); 193 | $response = $client->chat($messages, $options); 194 | ``` 195 | 196 | --- 197 | 198 | ## **Error Handling** 199 | 200 | This package includes built-in error handling with a dedicated exception class. 201 | Common errors and their messages: 202 | 203 | | Error Type | HTTP Code | Message | 204 | |--------------------|----------|-------------------------------------------| 205 | | `Invalid API Key` | 400 | No API key provided. Specify your API key. | 206 | | `Invalid Request` | 400 | Client specified an invalid argument. | 207 | | `Invalid Role` | 422 | Unknown role variant provided in messages. | 208 | 209 | Example of handling exceptions: 210 | 211 | ```php 212 | use GrokPHP\Client\Exceptions\GrokException; 213 | 214 | try { 215 | $response = $client->chat($messages, $options); 216 | } catch (GrokException $e) { 217 | echo "Error: " . $e->getMessage(); 218 | } 219 | ``` 220 | 221 | --- 222 | ## **Testing** 223 | 224 | **Run the tests with PHPUnit:** 225 | ```sh 226 | composer test 227 | ``` 228 | Or run PHPUnit manually: 229 | ```sh 230 | vendor/bin/phpunit 231 | ``` 232 | --- 233 | 234 | ## **Security** 235 | 236 | If you discover a security vulnerability, please report it via email: 237 | 📩 [thefeqy@gmail.com](mailto:thefeqy@gmail.com) 238 | 239 | --- 240 | 241 | ## **Contributing** 242 | 243 | Want to improve this package? Check out [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. 244 | 245 | --- 246 | 247 | ## **License** 248 | 249 | This package is open-source software licensed under the [MIT License](LICENSE). 250 | --------------------------------------------------------------------------------