├── .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 | 
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 | [](https://packagist.org/packages/grok-php/client)
9 | [](https://php.net)
10 | [](https://packagist.org/packages/grok-php/client)
11 | 
12 | [](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 | 
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 |
--------------------------------------------------------------------------------