{"id":47336803,"url":"https://github.com/aiaccess/ai-access","last_synced_at":"2026-04-01T02:00:33.931Z","repository":{"id":287418909,"uuid":"964663533","full_name":"aiaccess/ai-access","owner":"aiaccess","description":"A flexible PHP library providing access to various AI models (Gemini, OpenAI, Anthropic, DeepSeek, Grok) via a consistent interface.","archived":false,"fork":false,"pushed_at":"2025-12-22T03:38:23.000Z","size":50,"stargazers_count":45,"open_issues_count":0,"forks_count":1,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-12-23T14:55:13.985Z","etag":null,"topics":["ai","anthropic","api","artificial-intelligence","chatbot","client","deepseek","gemini","grok","large-language-model","llm","openai"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aiaccess.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/funding.yml","license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"dg"}},"created_at":"2025-04-11T15:23:01.000Z","updated_at":"2025-12-22T03:38:18.000Z","dependencies_parsed_at":"2025-04-11T17:04:54.566Z","dependency_job_id":null,"html_url":"https://github.com/aiaccess/ai-access","commit_stats":null,"previous_names":["aiaccess/ai-access"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/aiaccess/ai-access","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiaccess%2Fai-access","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiaccess%2Fai-access/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiaccess%2Fai-access/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiaccess%2Fai-access/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aiaccess","download_url":"https://codeload.github.com/aiaccess/ai-access/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiaccess%2Fai-access/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31262837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T01:56:54.585Z","status":"online","status_checked_at":"2026-04-01T02:00:07.777Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["ai","anthropic","api","artificial-intelligence","chatbot","client","deepseek","gemini","grok","large-language-model","llm","openai"],"created_at":"2026-03-17T22:00:50.933Z","updated_at":"2026-04-01T02:00:33.904Z","avatar_url":"https://github.com/aiaccess.png","language":"PHP","funding_links":["https://github.com/sponsors/dg"],"categories":["LLM Clients \u0026 Adapters"],"sub_categories":[],"readme":"![AI Access for PHP](https://github.com/user-attachments/assets/f9b6702d-6d6b-49fd-96ff-a33c53e26c68)\n\n[![Downloads this Month](https://img.shields.io/packagist/dm/ai-access/ai-access.svg)](https://packagist.org/packages/ai-access/ai-access)\n[![Tests](https://github.com/aiaccess/ai-access/workflows/Tests/badge.svg?branch=master)](https://github.com/aiaccess/ai-access/actions)\n[![Coverage Status](https://coveralls.io/repos/github/aiaccess/ai-access/badge.svg?branch=master)](https://coveralls.io/github/aiaccess/ai-access?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/aiaccess/ai-access/v/stable)](https://github.com/aiaccess/ai-access/releases)\n[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/aiaccess/ai-access/blob/master/license.md)\n\n\n \u003c!----\u003e\n\nUnified PHP library providing access to various AI models from different providers through a **single, unified PHP interface**.\n\n\u003ch3\u003e\n\n✅ **Consistent API:** Write once, use everywhere\u003cbr\u003e\n✅ **Easy Switching:** Change providers with one line of code\u003cbr\u003e\n✅ **Simplified Workflow:** Focus on your app, not vendor SDKs\u003cbr\u003e\n✅ **Modern PHP:** Built with strict types and PHP 8.1+\n\n\u003c/h3\u003e\n\n \u003c!----\u003e\n\nSupported Providers\n---\n\n\u003ch3\u003e\n\n\u003cimg src=\"https://github.com/user-attachments/assets/fad9ee33-e861-42c6-beb7-f0160deda45c\" width=\"35\" valign=\"middle\"\u003e \u0026nbsp; **OpenAI ChatGPT** \u003cbr\u003e\n\n\u003cimg src=\"https://github.com/user-attachments/assets/cc815e2d-0bb9-4c8e-984f-95d1b33570b9\" width=\"35\" valign=\"middle\"\u003e \u0026nbsp; **Anthropic Claude** \u003cbr\u003e\n\n\u003cimg src=\"https://github.com/user-attachments/assets/0ed4b173-1aec-4c1b-abb7-cf7abfc0ea21\" width=\"35\" valign=\"middle\"\u003e \u0026nbsp; **Google Gemini** \u003cbr\u003e\n\n\u003cimg src=\"https://github.com/user-attachments/assets/c2f76da8-3cfc-4645-8c82-dccd4fbfad98\" width=\"35\" valign=\"middle\"\u003e \u0026nbsp; **DeepSeek** \u003cbr\u003e\n\n\u003cimg src=\"https://github.com/user-attachments/assets/98d2d4d2-5df2-4ebb-ae50-b62f345d6bcf\" width=\"35\" valign=\"middle\"\u003e \u0026nbsp; **Grok (xAI)**\n\n\u003c/h3\u003e\n\n \u003c!----\u003e\n\nInstallation\n============\n\nDownload and install the library using Composer:\n\n```shell\ncomposer require ai-access/ai-access\n```\n\nAIAccess requires PHP 8.1 or later.\n\n \u003c!----\u003e\n\nInitializing the Client\n=======================\n\nTo start interacting with an AI provider, you first need to create a client instance. The specific class depends on the provider, but the core interface remains consistent.\n\nGet your API keys from the respective providers:\n\n*   **OpenAI:** [OpenAI Platform API Keys](https://platform.openai.com/api-keys)\n*   **Anthropic Claude:** [Anthropic Console API Keys](https://console.anthropic.com/settings/keys)\n*   **Google Gemini:** [Google AI Studio API Keys](https://aistudio.google.com/app/apikey)\n*   **DeepSeek:** [DeepSeek Platform API Keys](https://platform.deepseek.com/api_keys)\n*   **Grok (xAI):** [xAI API Console API Keys](https://console.x.ai/team/default/api-keys)\n\n```php\n$apiKey = trim(file_get_contents('path/to/your/key.txt'));\n\n// OpenAI Client\n$client = new AIAccess\\Provider\\OpenAI\\Client($apiKey);\n\n// Claude Client\n$client = new AIAccess\\Provider\\Claude\\Client($apiKey);\n\n// Gemini Client\n$client = new AIAccess\\Provider\\Gemini\\Client($apiKey);\n\n// DeepSeek Client\n$client = new AIAccess\\Provider\\DeepSeek\\Client($apiKey);\n\n// Grok (xAI) Client\n$client = new AIAccess\\Provider\\Grok\\Client($apiKey);\n```\n\nIn larger applications, you would typically configure and retrieve the client instance from a [Dependency Injection container](https://doc.nette.org/en/dependency-injection) instead of creating it directly in your application code.\n\nNow you can use the `$client` variable to interact with the chosen provider's API.\n\n**Key Points:**\n\n*   Choose the correct client class (`OpenAI\\Client`, `Claude\\Client`, `Gemini\\Client`, `DeepSeek\\Client`, `Grok\\Client`).\n*   Provide the corresponding API key during instantiation.\n*   The `$client` object now provides access to the provider's features through a unified interface where possible.\n\nAll subsequent examples will assume you have a `$client` variable initialized corresponding to your desired provider.\n\n \u003c!----\u003e\n\nBasic Chat Usage\n================\n\nOnce you have a `$client` instance, interacting with chat models is straightforward.\n\n```php\n// --- Choose a model appropriate for your chosen client ---\n// $model = 'gpt-4o-mini';              // OpenAI\n// $model = 'claude-3-5-haiku-latest';  // Claude\n// $model = 'gemini-2.5-flash';         // Gemini\n// $model = 'deepseek-chat';            // DeepSeek\n// $model = 'grok-3-fast-latest';       // Grok (xAI)\n\n// Assuming $client is initialized as shown in the previous section\n\n$chat = $client-\u003ecreateChat($model);\n\n// Send the message and get the response\n$response = $chat-\u003esendMessage('Write a short haiku about PHP.');\n\necho $response-\u003egetText() ?? 'No content generated';\n```\n\n**Switching Providers:** As shown in the \"Initializing the Client\" section, switching providers mainly involves changing the client instantiation line and selecting an appropriate model name for that provider. The chat interaction code itself (`createChat`, `sendMessage`, `getText`, etc.) remains largely consistent.\n\nIn addition to the generated text, it's often useful to check why the model stopped generating:\n\n```php\nuse AIAccess\\Chat\\FinishReason;\n\n$reason = $response-\u003egetFinishReason();\n\nif ($reason === FinishReason::Complete) {\n\techo \"✅ Model completed the response successfully.\\n\";\n} else {\n\techo \"⚠️ Model stopped early (reason: \" . ($reason?-\u003ename ?? 'Unknown') . \")\\n\";\n}\n```\n\nThe method `$response-\u003egetUsage()` returns an instance of `AIAccess\\Chat\\Usage`, containing provider-specific token statistics and raw metadata:\n\n```php\n$usage = $response-\u003egetUsage();\n\necho \"- Input tokens:  \" . ($usage-\u003einputTokens ?? 'N/A');\necho \"- Output tokens: \" . ($usage-\u003eoutputTokens ?? 'N/A');\necho \"- Reasoning:     \" . ($usage-\u003ereasoningTokens ?? 'N/A');\n```\n\n \u003c!----\u003e\n\nConversation History\n--------------------\n\nManage multi-turn conversations easily. You can add messages manually using `addMessage()` or let `sendMessage()` handle adding the user prompt and the model's response to the history automatically.\n\n```php\nuse AIAccess\\Chat\\Role;\n\n// Assuming $client is initialized and $model is set\n\n$chat = $client-\u003ecreateChat($model);\n\n// Manually add messages to history\n$chat-\u003eaddMessage('What is the capital of France?', Role::User);\n$chat-\u003eaddMessage('The capital of France is Paris.', Role::Model); // Simulate a previous response\n$chat-\u003eaddMessage('What is a famous landmark there?', Role::User); // Add the next question\n\n// Send request based on current history.\n// Since the last message was already added, call sendMessage() without arguments.\n$response = $chat-\u003esendMessage();\n\necho $response-\u003egetText();\n\n// The model's response is automatically added to the history by sendMessage().\n// Full Conversation History:\n$allMessages = $chat-\u003egetMessages();\nforeach ($allMessages as $message) {\n\techo \"[\" . $message-\u003egetRole()-\u003ename . \"]: \" . $message-\u003egetText();\n}\n```\n\n \u003c!----\u003e\n\nSystem Instructions\n-------------------\n\nGuide the model's overall behavior or persona using a system instruction. This instruction is typically considered by the model throughout the conversation.\n\n```php\n$chat-\u003esetSystemInstruction('You are a helpful assistant that speaks like a pirate.');\n```\n\n \u003c!----\u003e\n\nModel Options\n-------------\n\nFine-tune the model's response generation for specific requests using the `setOptions()` method on the `Chat` object. These options are provider-specific.\n\nHere's a generic example setting the `temperature` (which controls randomness):\n\n```php\n// Set a low temperature for less random output\n$chat-\u003esetOptions(temperature: 0.1);\n```\n\n**Provider-Specific Options (Examples):**\n\n*   **OpenAI** [OpenAI API Reference](https://platform.openai.com/docs/api-reference/chat)\n\t*   `temperature`: Controls randomness (0-2)\n\t*   `maxOutputTokens`: Max tokens in the response\n\t*   `topP`: Nucleus sampling threshold\n\t*   `tools`: Define functions the model can call\n\t*   `metadata`: Attach custom key-value data\n\n*   **Claude** [Anthropic API Reference](https://docs.anthropic.com/claude/reference/messages_post)\n\t*   `temperature`: Controls randomness (0-1)\n\t*   `maxTokens`: Max tokens to generate (*Note: Different name than others*)\n\t*   `topK`, `topP`: Alternative sampling methods\n\t*   `stopSequences`: Specify strings that stop generation\n\n*   **Gemini** [Google AI Gemini API Reference](https://ai.google.dev/api/rest/v1beta/models/generateContent).\n\t*   `temperature`: Controls randomness (0-1)\n\t*   `maxOutputTokens`: Max tokens in the response\n\t*   `topK`, `topP`: Alternative sampling methods\n\t*   `stopSequences`: Specify strings that stop generation\n\t*   `safetySettings`: Configure content safety filters\n\n*   **DeepSeek** [DeepSeek API Reference](https://api-docs.deepseek.com/api/create-chat-completion)\n\t*   `temperature`: Controls randomness (0-2, ignored by `deepseek-reasoner`)\n\t*   `maxOutputTokens`: Max tokens to generate (`max_tokens`)\n\t*   `topP`: Nucleus sampling (ignored by `deepseek-reasoner`)\n\t*   `frequencyPenalty`, `presencePenalty`: Control repetition (ignored by `deepseek-reasoner`)\n\t*   `stop`: Specify strings that stop generation\n\t*   `responseFormat`: Request JSON output (`['type' =\u003e 'json_object']`)\n\t*   `tools`: Define functions (not supported by `deepseek-reasoner`)\n\n*   **Grok (xAI)** [xAI API Reference](https://docs.x.ai/docs/api-reference#chat-completions)\n\t*   `temperature`: Controls randomness (0-2)\n\t*   `maxOutputTokens`: Max tokens in the response (`max_completion_tokens`)\n\t*   `topP`: Nucleus sampling threshold\n\t*   `frequencyPenalty`, `presencePenalty`: Control repetition\n\t*   `stop`: Specify strings that stop generation\n\t*   `responseFormat`: Request structured output (`['type' =\u003e 'json_object']` or `json_schema`)\n\t*   `tools`: Define functions\n\t*   `reasoningEffort`: Control thinking effort for reasoning models (`low`, `high`)\n\t*   `seed`: Attempt deterministic output\n\nAlways refer to the specific `Chat` class implementation (`src/\u003cVendor\u003e/Chat.php`) or the official vendor documentation for the most up-to-date and complete list of available options.\n\n \u003c!----\u003e\n\nBatch Processing\n================\n\nFor processing a large number of independent chat requests asynchronously, often at a lower cost, use the Batch API (supported by OpenAI and Claude). This is ideal when you don't need immediate responses, as processing can take significant time (minutes to potentially 24 hours, depending on the provider and queue load).\n\n**Note:** Grok (xAI), DeepSeek, and Gemini do not currently support a batch API via this library.\n\n**Concept:**\n1.  Create batch using `Client::createBatch()`\n2.  Create multiple `Chat` objects using `Batch::createChat()`, each configured with its own model, messages, system instructions, and options (using `addMessage`, `setSystemInstruction`, `setOptions` just like interactive chat). Assign a unique `customId` to each.\n3.  `submit()` the entire `Batch` container at once. This queues the jobs for background processing. **It does not send messages interactively.**\n4.  Store the returned `batchId`.\n5.  **Handle Asynchronously:** Use a separate mechanism (cron job, queue worker, webhook) to check the job status later using `retrieveBatch($batchId)`.\n6.  Once your checking mechanism confirms the job `status` is `Completed`, use `getMessages()` to get the results, mapped by `customId`.\n\n**Example: Preparing and Submitting the Batch**\n\n```php\nuse AIAccess\\Chat\\Role;\n\n$model = '...'; // Choose a model compatible with the $client\n\n// 1. Create a batch\n$batch = $client-\u003ecreateBatch();\n\n// 2. Add individual chat requests\n$chat1 = $batch-\u003eaddChat($model, 'request-greeting-1');\n$chat1-\u003esetSystemInstruction('Be brief and friendly.');\n$chat1-\u003eaddMessage('Hi!', Role::User);\n\n$chat2 = $batch-\u003eaddChat($model, 'request-translate-fr');\n$chat2-\u003esetSystemInstruction('Translate the user message to French.');\n$chat2-\u003eaddMessage('Hello world', Role::User);\n\n$chat3 = $batch-\u003eaddChat($model, 'request-code-explain');\n$chat3-\u003eaddMessage('Explain what this PHP code does: `echo \"Hello\";`', Role::User);\n\n// 3. Submit the batch job\n$batchResponse = $batch-\u003esubmit(); // Returns immediately\n\n$batchId = $batchResponse-\u003egetId();\n```\n\nNow, store the `$batchId` (e.g., in a database, queue message) associated with the task or user who initiated it.\n\nHandling Asynchronous Completion\n--------------------------------\n\nYou need a separate process (cron, queue worker, etc.) to check the status later using the stored `batchId`.\n\n```php\nuse AIAccess\\Batch\\Status;\n\n// --- In your separate checking script/job ---\n// $batchIdToCheck = ...; // Retrieve the ID from storage\n// $client = ...; // Re-initialize the appropriate client\n\n$currentBatch = $client-\u003eretrieveBatch($batchIdToCheck);\n$status = $currentBatch-\u003egetStatus();\n\nif ($status === Status::Completed) {\n\t// Mark job as complete, trigger result processing\n\techo \"Batch $batchIdToCheck completed.\\n\";\n\n} elseif ($status === Status::Failed) {\n\t// Mark job as failed, log error\n\t$errorDetails = $currentBatch-\u003egetError();\n\techo \"Batch $batchIdToCheck failed: \" . ($errorDetails ?? 'Unknown error');\n\n} else { // InProgress or Other\n\t// Job is still running, check again later based on your schedule\n\techo \"Batch $batchIdToCheck is still in status: \" . $status-\u003ename;\n}\n```\n\nRetrieve Results (After Confirmation)\n-------------------------------------\n\nOnce your asynchronous checking mechanism confirms that a batch job's status is `AIAccess\\Batch\\Status::Completed`, you can retrieve the results. This might happen within the checking job itself or in a separate process triggered upon completion.\n\n```php\n// Assuming $currentBatch is the completed BatchResponse object\n\n$messages = $currentBatch-\u003egetMessages(); // Returns ?array\u003cstring, AIAccess\\Chat\\Message\u003e\n\necho \"Retrieved \" . count($messages) . \" results:\\n\\n\";\nforeach ($messages as $customId =\u003e $message) {\n\techo \"Result for Request ID: '$customId' ---\\n\";\n\techo $message-\u003egetText();\n\t// Process the result\n}\n```\n\n**Batch API Differences \u0026 Abstraction:**\n\nWhile the underlying mechanisms for batch processing differ significantly between providers, **you don't need to worry about these details when using AIAccess.** The library completely abstracts these differences away. When you call the `$batch-\u003esubmit()` method:\n\n*   If using the `AIAccess\\OpenAI\\Client`, the library automatically formats your chat requests into the required JSONL structure, uploads the file to OpenAI, and initiates the batch job using the returned file ID.\n*   If using the `AIAccess\\Claude\\Client`, the library sends the prepared chat payloads directly in the batch creation request.\n\nThanks to this abstraction, you benefit from a **consistent and simplified workflow** for submitting batch jobs, regardless of the chosen backend provider (among those that support batch).\n\n \u003c!----\u003e\n\nEmbeddings\n==========\n\nEmbeddings transform text into numerical vectors (arrays of floating-point numbers), capturing semantic meaning. These vectors allow machines to understand relationships between texts. Embeddings are fundamental for tasks like:\n\n*   **Semantic Search:** Find documents relevant by meaning, not just keywords.\n*   **Clustering:** Group similar documents together.\n*   **Recommendations:** Suggest items based on content similarity.\n*   **Retrieval-Augmented Generation (RAG):** Provide relevant context to language models before generating answers.\n\nAIAccess provides a common interface (`calculateEmbeddings`) for generating these vectors using supported providers like OpenAI and Gemini.\n\n**Note:** Claude, DeepSeek, and Grok (xAI) do not currently offer embedding endpoints through this library.\n\n```php\n// Assuming $client is initialized (must be OpenAI\\Client or Gemini\\Client)\n\n// $embeddingModel = 'text-embedding-3-small'; // OpenAI Example\n$embeddingModel = 'embedding-001'; // Gemini Example\n\n$textsToEmbed = [\n\t'The quick brown fox jumps over the lazy dog.',\n\t'PHP is a popular general-purpose scripting language.',\n\t'Paris is the capital of France.',\n];\n\n// Calculating embeddings\n\n$results = $client-\u003ecalculateEmbeddings(\n\tmodel: $embeddingModel,\n\tinput: $textsToEmbed,\n\t// Provider-specific options go here as named arguments\n);\n```\n\nThe `calculateEmbeddings()` method returns an array of `AIAccess\\Embedding\\Vector` objects, one for each input text. Each `Vector` object contains the numerical vector representing the text's semantic meaning. You can then iterate through these results to use the vectors, for example, to calculate similarities or store them for later use.\n\n```php\n// Assuming $results is the array returned from calculateEmbeddings\n\nforeach ($results as $index =\u003e $vector) {\n\t// Example: Calculate similarity with the first embedding\n\tif ($index \u003e 0) {\n\t\t$similarity = $results[0]-\u003ecosineSimilarity($vector);\n\t\techo \"Cosine Similarity with first text: \" . number_format($similarity, 4);\n\t}\n}\n```\n\nYou can serialize embeddings for efficient storage:\n\n```php\nuse AIAccess\\Embedding\\Vector;\n\n$binaryData = $results[0]-\u003eserialize();\n// Store $binaryData in a database (e.g., BLOB column)\n\n// Later, retrieve and deserialize:\n$vector = Vector::deserialize($retrievedBinaryData);\n```\n\n**Embedding API Options:**\n\nPass these as additional named arguments to `calculateEmbeddings` when using the specific client:\n\n*   **OpenAI** [OpenAI Embeddings API Reference](https://platform.openai.com/docs/api-reference/embeddings/create)\n\t*   `dimensions` (int): Optional. Request specific vector size (e.g., 256) for `text-embedding-3-*` models.\n\n*   **Gemini** [Google AI Gemini API Reference (batchEmbedContents)](https://ai.google.dev/api/rest/v1beta/models/batchEmbedContents)\n\t*   `taskType` (string): Optional. Hint for use case (e.g., `RETRIEVAL_QUERY`, `RETRIEVAL_DOCUMENT`).\n\t*   `title` (string): Optional. Title when `taskType` is `RETRIEVAL_DOCUMENT`.\n\t*   `outputDimensionality` (int): Optional. Request specific dimensions.\n\n \u003c!----\u003e\n\nError Handling\n==============\n\nAIAccess uses a clear exception hierarchy designed around practical error handling needs:\n\n```\nServiceException                (Base for all service-related errors)\n├── ApiException                (API returned an explicit error)\n├── CommunicationException      (Cannot communicate with API or parse response)\n└── UnexpectedResponseException (Response has unexpected structure)\nLogicException                  (Programming errors - invalid parameters)\n```\n\n- **`ApiException`**: The provider API returned an explicit error response\n  - API communication succeeded, but the server returned an error code/message\n  - Examples: invalid API key, rate limits, content policy violations, invalid parameters\n  - Check `$e-\u003egetCode()` for the HTTP status code\n\n- **`CommunicationException`**: Failed to communicate with the API or parse the response\n  - Network issues (DNS failures, timeouts, connection resets)\n  - Invalid JSON responses that cannot be parsed\n  - *Retrying the request may resolve these issues*\n\n- **`UnexpectedResponseException`**: API returned data with an unexpected structure\n  - Response was received and parsed but doesn't match the expected schema\n  - *Retrying probably won't help - indicates API changes or library issues*\n\n- **`LogicException`**: Indicates programming errors in your usage of the library\n  - Invalid parameters, calling methods in wrong order, etc.\n  - Should be caught and fixed during development, not in production\n\nMost applications should handle exceptions based on recovery strategy:\n\n```php\ntry {\n\t$response = $chat-\u003esendMessage('Write a creative story.');\n\techo $response-\u003egetText();\n\n} catch (AIAccess\\ApiException $e) {\n\t// API explicitly returned an error\n\techo \"The AI service returned an error: \" . $e-\u003egetMessage();\n\n\t// You can check the status code for specific handling\n\tif ($e-\u003egetCode() === 429) {\n\t\techo \" Please try again later (rate limit reached).\";\n\t}\n\n} catch (AIAccess\\CommunicationException $e) {\n\t// Connection problems or invalid responses - can retry\n\techo \"Temporarily unable to reach the AI service. Please try again.\";\n\t// Consider automatic retry logic here\n\n} catch (AIAccess\\UnexpectedResponseException $e) {\n\t// Unexpected response structure - log for investigation\n\techo \"The service response was unexpected. Support has been notified.\";\n\t// Log the error for developers to investigate\n\n} catch (AIAccess\\ServiceException $e) {\n\t// Fallback for any other service-related errors\n\techo \"An error occurred with the AI service: \" . $e-\u003egetMessage();\n}\n```\n\nFor simpler applications, you can handle all service errors together:\n\n```php\ntry {\n\t$response = $chat-\u003esendMessage('Generate ideas for blog posts.');\n\techo $response-\u003egetText();\n\n} catch (AIAccess\\ServiceException $e) {\n\t// Handle all service errors in one place\n\techo \"Error communicating with AI: \" . $e-\u003egetMessage();\n\n\t// Log error details for troubleshooting\n\terror_log(get_class($e) . \": \" . $e-\u003egetMessage());\n}\n```\n\n`LogicException` indicates programming errors and typically shouldn't be caught in production code, as they should be fixed during development.\n\n \u003c!----\u003e\n\n[Support Me](https://github.com/sponsors/dg)\n============\n\nDo you like AI Access? Are you looking forward to new features?\n\n[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg)\n\nThank you!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiaccess%2Fai-access","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faiaccess%2Fai-access","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiaccess%2Fai-access/lists"}