From e9f22bdd216b86e2fe4853d1c97593de3c69fc4b Mon Sep 17 00:00:00 2001 From: Mohamed Khaled Date: Tue, 19 Aug 2025 17:59:38 +0300 Subject: [PATCH 1/3] Add comprehensive documentation suite for PHP AI Client --- docs/1.intro.md | 121 ++++++ docs/2.getting-started.md | 280 ++++++++++++++ docs/3.using-providers.md | 350 ++++++++++++++++++ docs/4.registering-providers.md | 630 ++++++++++++++++++++++++++++++++ 4 files changed, 1381 insertions(+) create mode 100644 docs/1.intro.md create mode 100644 docs/2.getting-started.md create mode 100644 docs/3.using-providers.md create mode 100644 docs/4.registering-providers.md diff --git a/docs/1.intro.md b/docs/1.intro.md new file mode 100644 index 00000000..13efafe4 --- /dev/null +++ b/docs/1.intro.md @@ -0,0 +1,121 @@ +# Introduction to the PHP AI Client + +The PHP AI Client is a foundational SDK that provides a unified, provider-agnostic interface for interacting with AI services. It enables developers to integrate AI capabilities into their PHP applications without being locked into a specific AI provider or learning multiple APIs. + +## What is the PHP AI Client? + +The PHP AI Client is a comprehensive SDK designed to simplify AI integration in PHP applications. It provides the architectural foundation and interfaces for AI operations, with a focus on extensibility and type safety. + +### Core Concepts + +- **Providers**: AI service companies or platforms (implementations to be added) +- **Models**: Specific AI models offered by providers +- **Capabilities**: What an AI model can do (text generation, image generation, etc.) +- **Operations**: Individual AI requests that can be tracked and managed +- **Results**: The output from AI operations + +## Current State + +**⚠️ Important**: This SDK is currently in active development. The core architecture and interfaces are complete, but specific provider implementations are being added incrementally. + +### What's Available Now ✅ + +- **Provider Registry System**: Complete provider registration and discovery +- **Traditional API Methods**: Core generation methods like `generateTextResult()` +- **Type-Safe Interfaces**: Full interface definitions for providers and models +- **Message & Result DTOs**: Complete data structures for AI interactions +- **Operation Tracking**: Async operation support architecture + +### Future Features 🚧 + +- **Fluent API**: Chainable method syntax with PromptBuilder and MessageBuilder classes +- **Concrete Providers**: OpenAI, Anthropic, Google AI implementations with convenient factory methods +- **Enhanced Model Discovery**: Auto-selection based on capabilities and requirements +- **Convenience Methods**: Simplified message creation and model configuration + +## Why Use the PHP AI Client? + +### Provider Agnostic Architecture +The registry system allows you to write AI integrations once and add provider implementations as they become available. + +### Type Safety +Built with PHP 8+ features including strong typing, generics support, and comprehensive static analysis to catch errors early. + +### Extensible Design +Well-defined interfaces make it straightforward to implement new providers following established patterns. + +### Future-Proof +Designed to support emerging AI capabilities and modalities without breaking existing integrations. + +## Key Benefits + +### For AI Implementers +- **Traditional API**: Working generation methods available now +- **Consistent Interface**: Same API structure regardless of provider +- **Type Safety**: Full IDE support with autocompletion and type checking +- **Rich Documentation**: Comprehensive guides and examples + +### For AI Extenders +- **Extensible Architecture**: Clear interfaces for adding new providers +- **Well-Defined Contracts**: Complete interface definitions ready for implementation +- **Comprehensive Testing**: Full test coverage for reliable integrations +- **Standards Compliance**: Follows PSR standards and best practices + +## Architecture Overview + +The PHP AI Client follows a modular architecture: + +``` +AiClient (Entry Point) +├── ProviderRegistry (✅ Complete) +├── Provider Interfaces (✅ Complete) +├── Model Interfaces (✅ Complete) +├── Operations (✅ Complete) +├── Results (✅ Complete) +└── Concrete Providers (🚧 In Development) +``` + +### API Patterns + +**Traditional API** - Available now: +```php +// This works with current implementation +$result = AiClient::generateTextResult( + 'Write a poem about PHP', + $customModel // Your provider implementation +); +$text = $result->toText(); +``` + +**Fluent API** - Future feature (planned architecture): +```php +// 🚧 Not yet available - throws RuntimeException +$text = AiClient::prompt('Write a poem about PHP') + ->usingModel(OpenAI::model('gpt-4')) + ->generateText(); + +// Future convenience methods: +$image = AiClient::prompt('A sunset over mountains') + ->usingProvider('openai') + ->generateImage(); +``` + +## Use Cases + +### Content Generation +Generate blog posts, product descriptions, and marketing copy using the traditional API with custom provider implementations. + +### Image Creation +Create images and visual content once image generation providers are implemented. + +### Audio Processing +Convert text to speech and process audio data with future audio provider implementations. + +### Custom AI Services +Implement your own AI providers using the well-defined interfaces and registry system. + +## Getting Started + +The PHP AI Client provides a solid foundation for AI integrations. While some features are still in development, you can start using the traditional API and provider registry system immediately. + +Ready to start? Check out our [Getting Started guide](2.getting-started.md) to begin working with the current implementation and learn how to create custom providers. \ No newline at end of file diff --git a/docs/2.getting-started.md b/docs/2.getting-started.md new file mode 100644 index 00000000..9d4ccae2 --- /dev/null +++ b/docs/2.getting-started.md @@ -0,0 +1,280 @@ +# Getting Started with the PHP AI Client + +This guide will help you install and start using the PHP AI Client in your PHP application. + +## Requirements + +- PHP 8.0 or higher +- Composer for package management + +## Installation + +Install the PHP AI Client via Composer: + +```bash +composer require wordpress/php-ai-client +``` + +## Current Implementation Status + +**⚠️ Important**: The PHP AI Client is currently in active development. This guide covers what's available now and what's coming soon. + +### What Works Now ✅ + +- Provider registry system +- Traditional API methods (`generateTextResult`, `generateImageResult`, etc.) +- Core interfaces and DTOs +- Operation tracking + +### What's Coming Soon 🚧 + +- Fluent API (`AiClient::prompt()` - currently throws RuntimeException) +- Concrete provider implementations (OpenAI, Anthropic, Google) +- Builder classes for easier message construction + +## Working with the Current Implementation + +### Basic Provider Registration + +The provider registry system is fully functional: + +```php +registerProvider(CustomAIProvider::class); + +// Check if provider is registered +if (AiClient::defaultRegistry()->hasProvider('your-provider-id')) { + echo "Provider registered successfully\n"; +} +``` + +### Traditional API Usage (Available Now) + +Use the traditional API methods. The SDK supports flexible input formats: + +```php +toText(); +} catch (\RuntimeException $e) { + echo "Need a registered provider: " . $e->getMessage(); +} + +// Option 2: Explicit message construction +$message = new UserMessage([new MessagePart('Write a haiku about PHP development')]); +try { + $result = AiClient::generateTextResult([$message], $customModel); + echo $result->toText(); +} catch (\RuntimeException $e) { + echo "Need a registered provider: " . $e->getMessage(); +} + +// Option 3: Without model (auto-discovery - requires registered providers) +try { + $result = AiClient::generateTextResult('Write a haiku about PHP development'); + echo $result->toText(); +} catch (\RuntimeException $e) { + echo "No suitable models found: " . $e->getMessage(); +} +``` + +### Provider Registry Operations + +Discover and work with providers: + +```php +findModelsMetadataForSupport($requirements); + +foreach ($providersMetadata as $providerData) { + echo "Provider: " . $providerData->getProvider()->getName() . "\n"; + foreach ($providerData->getModels() as $model) { + echo " Model: " . $model->getName() . "\n"; + } +} +``` + +### Operation Tracking + +Create and track AI operations: + +```php +getId() . "\n"; +echo "Operation State: " . $operation->getState()->value . "\n"; +``` + +## Future Features (Coming Soon) + +The following features are planned but not yet implemented: + +### 🚧 Fluent API (Not Available) + +```php +generateText(); +``` + +### 🚧 Concrete Provider Implementations + +```php +withImage($file)->get(); +``` + +## Working with Current DTOs + +### Creating Messages + +Build messages using the available DTO classes: + +```php +toText(); + + // Access token usage information + $usage = $result->getTokenUsage(); + echo "Tokens used: " . $usage->getTotalTokens() . "\n"; + + // Get provider-specific metadata + $metadata = $result->getProviderMetadata(); + print_r($metadata); + +} catch (\Exception $e) { + echo "Generation failed: " . $e->getMessage() . "\n"; +} +``` + +### Provider Configuration Checking + +Check if providers are properly configured: + +```php +isProviderConfigured('your-custom-provider')) { + echo "Provider is ready to use\n"; +} else { + echo "Provider needs configuration\n"; +} +``` + +## Error Handling + +Handle potential errors in the current implementation: + +```php +toText(); + +} catch (\RuntimeException $e) { + // Handle not-yet-implemented features + echo "Feature not ready: " . $e->getMessage() . "\n"; + +} catch (\InvalidArgumentException $e) { + // Handle invalid arguments + echo "Invalid input: " . $e->getMessage() . "\n"; + +} catch (\Exception $e) { + // Handle other errors + echo "Error: " . $e->getMessage() . "\n"; +} +``` + +## Next Steps + +Now that you understand the current implementation: + +1. **Read [Registering Providers](4.registering-providers.md)** - Learn how to implement your own AI providers +2. **Study the interfaces** - Review the complete interface definitions in `src/Providers/Contracts/` +3. **Check the tests** - Look at `tests/` for concrete examples of how the APIs work +4. **Monitor development** - Watch for updates as concrete provider implementations are added + +## Need Help? + +- Check the [Architecture documentation](ARCHITECTURE.md) for deeper technical details +- Review the [Glossary](GLOSSARY.md) for key terms and concepts +- Look at the comprehensive test suite in `tests/` for usage examples +- Study the interface definitions in `src/Providers/Contracts/` for implementation guidance \ No newline at end of file diff --git a/docs/3.using-providers.md b/docs/3.using-providers.md new file mode 100644 index 00000000..0b039fad --- /dev/null +++ b/docs/3.using-providers.md @@ -0,0 +1,350 @@ +# Using AI Providers + +This guide explains how to work with the provider system in the PHP AI Client. + +## Current Status + +**⚠️ Important**: Concrete provider implementations (OpenAI, Anthropic, Google) are not yet available. This guide covers the provider registry system and how to work with custom provider implementations. + +## Understanding Providers + +A **provider** represents an AI service company or platform that offers access to AI models. The provider system includes: + +- **Provider Registry**: Central system for registering and discovering providers ✅ +- **Provider Interfaces**: Complete contracts for implementing providers ✅ +- **Provider Metadata**: Information about capabilities and configuration ✅ +- **Concrete Providers**: Specific implementations (🚧 in development) + +## Provider Registry Operations + +### Check Available Providers + +See which providers are registered and configured: + +```php +hasProvider('your-custom-provider')) { + echo "Custom provider is registered\n"; +} + +// Check if a provider is properly configured (has API key, etc.) +if ($registry->isProviderConfigured('your-custom-provider')) { + echo "Custom provider is configured and ready to use\n"; +} + +// Get provider class name by ID +try { + $className = $registry->getProviderClassName('your-custom-provider'); + echo "Provider class: " . $className . "\n"; +} catch (\InvalidArgumentException $e) { + echo "Provider not found: " . $e->getMessage() . "\n"; +} +``` + +### Find Models by Capability + +Discover models that support specific AI capabilities (requires custom provider implementations): + +```php +findModelsMetadataForSupport($requirements); + +foreach ($providersMetadata as $providerData) { + echo "Provider: " . $providerData->getProvider()->getName() . "\n"; + foreach ($providerData->getModels() as $model) { + echo " Model: " . $model->getName() . "\n"; + } +} +``` + +### Find Models within Specific Provider + +Look for models that support particular features within one provider: + +```php +findProviderModelsMetadataForSupport('your-custom-provider', $requirements); + +foreach ($models as $model) { + echo "Model: " . $model->getName() . " supports multimodal input\n"; +} +``` + +### Get Model Instances + +Create model instances from registered providers: + +```php +getProviderModel('your-custom-provider', 'text-model-id'); + +// Get a model instance with custom configuration +$config = new ModelConfig(); +$config->setTemperature(0.7); +$config->setMaxTokens(1000); + +$customModel = $registry->getProviderModel( + 'your-custom-provider', + 'text-model-id', + $config +); +``` + +## 🚧 Future: Concrete Provider Implementations + +The following provider implementations are planned but not yet available: + +### OpenAI (Coming Soon) + +```php +registerProvider(OpenAIProvider::class); +// $model = OpenAIProvider::model('gpt-4'); +``` + +### Anthropic (Coming Soon) + +```php +registerProvider(AnthropicProvider::class); +// $model = AnthropicProvider::model('claude-3-sonnet'); +``` + +### Google AI (Coming Soon) + +```php +registerProvider(GoogleProvider::class); +// $model = GoogleProvider::model('gemini-pro'); +``` + +## Working with Current Implementation + +### Model Configuration + +Configure models using the ModelConfig DTO: + +```php +setTemperature(0.7); // Creativity level +$config->setMaxTokens(1000); // Response length limit +$config->setTopK(40); // Top-K sampling +$config->setTopP(0.9); // Nucleus sampling +$config->setCandidateCount(3); // Multiple response options + +// Set output modalities +$config->setOutputModalities([ModalityEnum::TEXT]); + +// Set output format +$config->setOutputMimeType('application/json'); + +// Get a configured model instance +$model = AiClient::defaultRegistry()->getProviderModel( + 'your-custom-provider', + 'model-id', + $config +); +``` + +### Provider Availability Checking + +Use the static interface to check provider availability: + +```php +toText() . "\n"; + + // Access usage information + $usage = $result->getTokenUsage(); + echo "Tokens used: " . $usage->getTotalTokens() . "\n"; + +} catch (\RuntimeException $e) { + echo "Generation failed: " . $e->getMessage() . "\n"; +} + +// Explicit message construction (for complex messages) +$messages = [new UserMessage([new MessagePart('Explain PHP to a beginner')])]; +try { + $result = AiClient::generateTextResult($messages, $customModel); + echo "Generated text: " . $result->toText() . "\n"; +} catch (\RuntimeException $e) { + echo "Generation failed: " . $e->getMessage() . "\n"; +} + +// Image generation +try { + $result = AiClient::generateImageResult('A diagram of PHP architecture', $customImageModel); + $imageFile = $result->toImageFile(); + + if ($imageFile->getUrl()) { + echo "Generated image URL: " . $imageFile->getUrl() . "\n"; + } + +} catch (\RuntimeException $e) { + echo "Image generation failed: " . $e->getMessage() . "\n"; +} +``` + +### Streaming Support + +The streaming API is available (with compatible providers): + +```php +toText(); + flush(); + } +} catch (\RuntimeException $e) { + echo "Streaming failed: " . $e->getMessage() . "\n"; +} +``` + +### Operations Support + +Create and track operations: + +```php +getState()->value . "\n"; +echo "Image operation state: " . $imageOp->getState()->value . "\n"; + +// Operations can be tracked and managed asynchronously +if ($textOp->getState() === OperationStateEnum::SUCCEEDED) { + $result = $textOp->getResult(); + echo "Operation completed: " . $result->toText() . "\n"; +} +``` + +## Error Handling + +Handle different types of errors that may occur: + +```php +toText(); + +} catch (\RuntimeException $e) { + // Handle not-yet-implemented features or provider errors + echo "Runtime error: " . $e->getMessage() . "\n"; + +} catch (\InvalidArgumentException $e) { + // Handle invalid arguments (wrong model, bad configuration, etc.) + echo "Invalid argument: " . $e->getMessage() . "\n"; + +} catch (\Exception $e) { + // Handle unexpected errors + echo "Unexpected error: " . $e->getMessage() . "\n"; +} +``` + +## Next Steps + +1. **Learn about [Registering Providers](4.registering-providers.md)** - Implement your own AI providers +2. **Study the interfaces** - Review complete interface definitions in `src/Providers/Contracts/` +3. **Check the tests** - Look at `tests/` for concrete usage examples +4. **Review the Architecture** - Read [ARCHITECTURE.md](ARCHITECTURE.md) for technical details \ No newline at end of file diff --git a/docs/4.registering-providers.md b/docs/4.registering-providers.md new file mode 100644 index 00000000..5e4c2193 --- /dev/null +++ b/docs/4.registering-providers.md @@ -0,0 +1,630 @@ + # Registering AI Providers + +This guide explains how to implement and register custom AI providers with the PHP AI Client. + +## Understanding Provider Implementation + +Since the PHP AI Client doesn't include concrete provider implementations yet, you'll need to create your own providers to use the system. This guide shows you how to implement providers following the established interfaces. + +## Why Implement Custom Providers? + +Provider implementation is currently necessary for: + +- **Testing the SDK**: No concrete providers exist yet for testing +- **Custom AI Services**: Integrating proprietary or local AI services +- **Contributing Back**: Building providers that can be contributed to the project +- **Learning**: Understanding the architecture before concrete providers are available + +## Provider Interface Requirements + +The current implementation uses **static methods** for the ProviderInterface. Here's the correct interface to implement: + +### Basic Provider Registration + +Register a provider using its class name: + +```php +registerProvider(CustomAIProvider::class); + +// Verify registration +if (AiClient::defaultRegistry()->hasProvider('custom-ai')) { + echo "Custom provider registered successfully\n"; +} +``` + +### Static Provider Interface + +All providers must implement the `ProviderInterface` with **static methods**: + +```php + [ + 'name' => 'Custom Text Generator', + 'capabilities' => [CapabilityEnum::TEXT_GENERATION], + ], + 'custom-image-model' => [ + 'name' => 'Custom Image Generator', + 'capabilities' => [CapabilityEnum::IMAGE_GENERATION], + ], + ]; + + public function listModelMetadata(): array + { + $metadata = []; + + foreach ($this->models as $id => $config) { + $metadata[] = new ModelMetadata( + id: $id, + name: $config['name'], + supportedCapabilities: $config['capabilities'] + ); + } + + return $metadata; + } + + public function hasModelMetadata(string $modelId): bool + { + return isset($this->models[$modelId]); + } + + public function getModelMetadata(string $modelId): ModelMetadata + { + if (!$this->hasModelMetadata($modelId)) { + throw new InvalidArgumentException("Model not found: $modelId"); + } + + $config = $this->models[$modelId]; + + return new ModelMetadata( + id: $modelId, + name: $config['name'], + supportedCapabilities: $config['capabilities'] + ); + } +} +``` + +## Creating Custom Models + +### Text Generation Model + +Implement a model that can generate text: + +```php +formatPrompt($prompt); + + // Create HTTP request + $request = new Request( + method: 'POST', + uri: 'https://api.custom-ai.com/v1/generate', + headers: [ + 'Authorization' => ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']], + 'Content-Type' => ['application/json'], + ], + body: json_encode($requestData) + ); + + // Send request + $response = $this->httpTransporter->send($request); + + // Parse response into GenerativeAiResult + return $this->parseResponse($response); + } + + private function formatPrompt(array $messages): array + { + // Convert PHP AI Client messages to your provider's format + $formattedMessages = []; + + foreach ($messages as $message) { + if ($message instanceof Message) { + $formattedMessages[] = [ + 'role' => $message->getRole()->value, + 'content' => $this->extractTextFromMessage($message), + ]; + } + } + + return [ + 'model' => $this->modelId, + 'messages' => $formattedMessages, + 'temperature' => $this->config->getTemperature(), + 'max_tokens' => $this->config->getMaxTokens(), + ]; + } + + private function parseResponse(Response $response): GenerativeAiResult + { + $data = json_decode($response->getBody(), true); + + // Convert your provider's response to PHP AI Client format + $candidates = []; + foreach ($data['choices'] as $choice) { + $message = new ModelMessage($choice['message']['content']); + $candidates[] = new Candidate( + message: $message, + finishReason: FinishReasonEnum::STOP + ); + } + + return new GenerativeAiResult( + id: $data['id'], + candidates: $candidates, + tokenUsage: new TokenUsage( + promptTokens: $data['usage']['prompt_tokens'], + completionTokens: $data['usage']['completion_tokens'], + totalTokens: $data['usage']['total_tokens'] + ) + ); + } + + // Implement other required interface methods... + public function metadata(): ModelMetadata { /* ... */ } + public function setConfig(ModelConfig $config): void { /* ... */ } + public function getConfig(): ModelConfig { /* ... */ } + public function setHttpTransporter(HttpTransporterInterface $transporter): void { /* ... */ } + public function getHttpTransporter(): HttpTransporterInterface { /* ... */ } +} +``` + +### Image Generation Model + +Implement a model that can generate images: + +```php + ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']], + 'Content-Type' => ['application/json'], + ], + body: json_encode([ + 'prompt' => $this->extractTextFromMessages($prompt), + 'size' => '1024x1024', + 'quality' => 'standard', + ]) + ); + + $response = $this->httpTransporter->send($request); + $data = json_decode($response->getBody(), true); + + // Create File object for the generated image + $imageFile = new File( + fileType: FileTypeEnum::REMOTE, + mimeType: 'image/png', + url: $data['data'][0]['url'] + ); + + // Create message with the image + $message = new ModelMessage(); + $message->addPart(new MessagePart( + type: MessagePartTypeEnum::FILE, + file: $imageFile + )); + + $candidate = new Candidate( + message: $message, + finishReason: FinishReasonEnum::STOP + ); + + return new GenerativeAiResult( + id: $data['id'], + candidates: [$candidate], + tokenUsage: new TokenUsage(0, 0, 0) // Images don't use tokens + ); + } +} +``` + +## Advanced Provider Features + +### Supporting Operations + +For long-running requests, implement operation support: + +```php + ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']]] + ); + + $response = $this->httpTransporter->send($request); + $data = json_decode($response->getBody(), true); + + return new GenerativeAiOperation( + id: $data['id'], + state: OperationStateEnum::from($data['status']), + result: $data['result'] ? $this->parseResult($data['result']) : null + ); + } +} +``` + +### Custom Authentication + +Implement custom authentication methods: + +```php +generateSignature($request->getBody(), $timestamp); + + $request->addHeader('X-API-Key', $this->apiKey); + $request->addHeader('X-Timestamp', (string)$timestamp); + $request->addHeader('X-Signature', $signature); + } + + private function generateSignature(string $body, int $timestamp): string + { + $data = $body . $timestamp; + return hash_hmac('sha256', $data, $this->secretKey); + } + + public function getJsonSchema(): array + { + return [ + 'type' => 'object', + 'properties' => [ + 'apiKey' => ['type' => 'string'], + 'secretKey' => ['type' => 'string'] + ] + ]; + } +} +``` + +## Testing Custom Providers + +### Unit Testing + +Create comprehensive tests for your provider: + +```php +provider = new CustomAIProvider(); + } + + public function testProviderRegistration(): void + { + $registry = AiClient::defaultRegistry(); + $registry->registerProvider(CustomAIProvider::class); + + $this->assertTrue($registry->hasProvider('custom-ai')); + $this->assertEquals(CustomAIProvider::class, $registry->getProviderClassName('custom-ai')); + } + + public function testModelGeneration(): void + { + $model = $this->provider->model('custom-text-model'); + + $this->assertInstanceOf(ModelInterface::class, $model); + $this->assertEquals('custom-text-model', $model->metadata()->getId()); + } + + public function testTextGeneration(): void + { + // Set up mock HTTP responses + $this->mockHttpResponse([ + 'id' => 'test-id', + 'choices' => [ + ['message' => ['content' => 'Generated text']], + ], + 'usage' => [ + 'prompt_tokens' => 10, + 'completion_tokens' => 5, + 'total_tokens' => 15, + ], + ]); + + $model = $this->provider->model('custom-text-model'); + $result = $model->generateTextResult([ + new UserMessage('Test prompt') + ]); + + $this->assertEquals('Generated text', $result->toText()); + } +} +``` + +### Integration Testing + +Test your provider with the full AI Client: + +```php +registerProvider(CustomAIProvider::class); + +$text = AiClient::prompt('Test prompt') + ->usingProvider('custom-ai') + ->generateText(); + +echo "Generated: " . $text; +``` + +## Best Practices + +### Error Handling + +Implement comprehensive error handling: + +```php +httpTransporter->send($request); + + if ($response->getStatusCode() >= 400) { + throw new AiProviderException( + "API request failed: " . $response->getBody(), + $response->getStatusCode() + ); + } + + return $this->parseResponse($response); + } catch (HttpException $e) { + throw new AiProviderException( + "Network error: " . $e->getMessage(), + 0, + $e + ); + } + } +} +``` + +### Configuration Validation + +Validate provider configuration: + +```php +validateConfiguration(); + } + + private function validateConfiguration(): void + { + $apiKey = $_ENV['CUSTOM_AI_API_KEY'] ?? null; + + if (empty($apiKey)) { + throw new InvalidArgumentException( + 'CUSTOM_AI_API_KEY environment variable is required' + ); + } + + if (strlen($apiKey) < 32) { + throw new InvalidArgumentException( + 'Invalid API key format' + ); + } + } +} +``` + +### Documentation + +Document your provider thoroughly: + +```php + Date: Fri, 29 Aug 2025 20:49:25 +0300 Subject: [PATCH 2/3] Update documentation suite with new structure --- docs/1.intro.md | 170 +++--- docs/2.getting-started.md | 280 --------- docs/2.quick-start.md | 306 ++++++++++ docs/3.php-applications.md | 610 ++++++++++++++++++++ docs/3.using-providers.md | 350 ------------ docs/4.registering-providers.md | 630 --------------------- docs/4.wordpress-integration.md | 786 ++++++++++++++++++++++++++ docs/5.advanced-usage.md | 972 ++++++++++++++++++++++++++++++++ 8 files changed, 2756 insertions(+), 1348 deletions(-) delete mode 100644 docs/2.getting-started.md create mode 100644 docs/2.quick-start.md create mode 100644 docs/3.php-applications.md delete mode 100644 docs/3.using-providers.md delete mode 100644 docs/4.registering-providers.md create mode 100644 docs/4.wordpress-integration.md create mode 100644 docs/5.advanced-usage.md diff --git a/docs/1.intro.md b/docs/1.intro.md index 13efafe4..922cb0a6 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -1,121 +1,115 @@ -# Introduction to the PHP AI Client +# 1. Introduction & Overview -The PHP AI Client is a foundational SDK that provides a unified, provider-agnostic interface for interacting with AI services. It enables developers to integrate AI capabilities into their PHP applications without being locked into a specific AI provider or learning multiple APIs. +**What is the PHP AI Client?** -## What is the PHP AI Client? +The PHP AI Client is an intelligent, unified SDK that automatically selects the best AI model for your content generation needs. It seamlessly works with multiple providers and intelligently routes your requests to the most suitable model based on your prompt and desired output type. -The PHP AI Client is a comprehensive SDK designed to simplify AI integration in PHP applications. It provides the architectural foundation and interfaces for AI operations, with a focus on extensibility and type safety. +**Two Ways to Use It** -### Core Concepts +## Standalone PHP Applications -- **Providers**: AI service companies or platforms (implementations to be added) -- **Models**: Specific AI models offered by providers -- **Capabilities**: What an AI model can do (text generation, image generation, etc.) -- **Operations**: Individual AI requests that can be tracked and managed -- **Results**: The output from AI operations +Use it in any PHP project - Laravel, Symfony, custom applications: -## Current State +```php +toText(); -- **Provider Registry System**: Complete provider registration and discovery -- **Traditional API Methods**: Core generation methods like `generateTextResult()` -- **Type-Safe Interfaces**: Full interface definitions for providers and models -- **Message & Result DTOs**: Complete data structures for AI interactions -- **Operation Tracking**: Async operation support architecture +// Fluent API - modern chainable style +$result = AiClient::prompt('Write a product description for organic coffee') + ->usingTemperature(0.7) + ->usingMaxTokens(200) + ->generateTextResult(); -### Future Features 🚧 +echo $result->toText(); +``` -- **Fluent API**: Chainable method syntax with PromptBuilder and MessageBuilder classes -- **Concrete Providers**: OpenAI, Anthropic, Google AI implementations with convenient factory methods -- **Enhanced Model Discovery**: Auto-selection based on capabilities and requirements -- **Convenience Methods**: Simplified message creation and model configuration +## WordPress Integration -## Why Use the PHP AI Client? +Enhanced features for WordPress plugins and themes: -### Provider Agnostic Architecture -The registry system allows you to write AI integrations once and add provider implementations as they become available. +```php +usingTemperature(0.6) + ->generateTextResult(); + + // Create WordPress post + wp_insert_post([ + 'post_title' => 'AI Generated: ' . $topic, + 'post_content' => $result->toText(), + 'post_status' => 'draft' + ]); + } +} +``` -### Type Safety -Built with PHP 8+ features including strong typing, generics support, and comprehensive static analysis to catch errors early. +**What You Can Build** -### Extensible Design -Well-defined interfaces make it straightforward to implement new providers following established patterns. +### PHP Applications +- **Content Management Systems**: Auto-generate product descriptions, articles +- **Marketing Tools**: Create social media posts, email campaigns +- **Documentation**: Generate API docs, user guides +- **E-commerce**: Product descriptions, customer service responses +- **Data Analysis**: Generate reports and insights -### Future-Proof -Designed to support emerging AI capabilities and modalities without breaking existing integrations. +### WordPress Solutions +- **Content Plugins**: Auto-generate posts, pages, product descriptions +- **Theme Features**: Dynamic content based on user input +- **Admin Tools**: Bulk content generation for sites +- **User Features**: Let visitors generate custom content +- **SEO Tools**: Meta descriptions, alt text generation -## Key Benefits +**Supported AI Providers** -### For AI Implementers -- **Traditional API**: Working generation methods available now -- **Consistent Interface**: Same API structure regardless of provider -- **Type Safety**: Full IDE support with autocompletion and type checking -- **Rich Documentation**: Comprehensive guides and examples +The client works with multiple AI providers through a unified interface: -### For AI Extenders -- **Extensible Architecture**: Clear interfaces for adding new providers -- **Well-Defined Contracts**: Complete interface definitions ready for implementation -- **Comprehensive Testing**: Full test coverage for reliable integrations -- **Standards Compliance**: Follows PSR standards and best practices +- **OpenAI**: GPT-5, GPT-4o, o4-mini, GPT-image-1, DALL-E 3, Whisper, Sora +- **Anthropic**: Claude Opus 4.1, Claude Sonnet 4, Claude 3.5 Sonnet, Claude 3.5 Haiku +- **Google AI**: Gemini 2.5 Pro, Gemini 2.5 Flash, Imagen 4, Veo 3 -## Architecture Overview -The PHP AI Client follows a modular architecture: -``` -AiClient (Entry Point) -├── ProviderRegistry (✅ Complete) -├── Provider Interfaces (✅ Complete) -├── Model Interfaces (✅ Complete) -├── Operations (✅ Complete) -├── Results (✅ Complete) -└── Concrete Providers (🚧 In Development) -``` +**Two API Styles** -### API Patterns +Choose the style that fits your development preferences: -**Traditional API** - Available now: +**Traditional API** (WordPress-style): ```php -// This works with current implementation -$result = AiClient::generateTextResult( - 'Write a poem about PHP', - $customModel // Your provider implementation -); -$text = $result->toText(); +$result = AiClient::generateTextResult($prompt, $config); +$image = AiClient::generateImageResult($prompt); +$audio = AiClient::convertTextToSpeechResult($text); ``` -**Fluent API** - Future feature (planned architecture): +**Fluent API** (modern chainable): ```php -// 🚧 Not yet available - throws RuntimeException -$text = AiClient::prompt('Write a poem about PHP') - ->usingModel(OpenAI::model('gpt-4')) - ->generateText(); - -// Future convenience methods: -$image = AiClient::prompt('A sunset over mountains') - ->usingProvider('openai') - ->generateImage(); +$result = AiClient::prompt($prompt) + ->usingTemperature(0.7) + ->usingMaxTokens(500) + ->generateTextResult(); ``` -## Use Cases - -### Content Generation -Generate blog posts, product descriptions, and marketing copy using the traditional API with custom provider implementations. - -### Image Creation -Create images and visual content once image generation providers are implemented. - -### Audio Processing -Convert text to speech and process audio data with future audio provider implementations. - -### Custom AI Services -Implement your own AI providers using the well-defined interfaces and registry system. +**Key Benefits** -## Getting Started +- **Intelligent Model Selection**: Automatically chooses the best available model for your specific task +- **Provider Agnostic**: Switch between AI providers without changing code +- **Type Safe**: Full PHP type safety with IDE autocompletion +- **Multi-Modal**: Seamlessly handle text, images, and audio generation +- **Production Ready**: Comprehensive error handling and validation +- **Flexible**: Use in any PHP application or WordPress site +- **WordPress Optimized**: Special features for WordPress developers -The PHP AI Client provides a solid foundation for AI integrations. While some features are still in development, you can start using the traditional API and provider registry system immediately. +## Choose Your Path -Ready to start? Check out our [Getting Started guide](2.getting-started.md) to begin working with the current implementation and learn how to create custom providers. \ No newline at end of file +- **[Quick Start](2.quick-start.md)** - Get working examples for both PHP and WordPress +- **[PHP Applications](3.php-applications.md)** - Framework-agnostic PHP usage +- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific features +- **[Advanced Usage](5.advanced-usage.md)** - Providers, customization, production patterns diff --git a/docs/2.getting-started.md b/docs/2.getting-started.md deleted file mode 100644 index 9d4ccae2..00000000 --- a/docs/2.getting-started.md +++ /dev/null @@ -1,280 +0,0 @@ -# Getting Started with the PHP AI Client - -This guide will help you install and start using the PHP AI Client in your PHP application. - -## Requirements - -- PHP 8.0 or higher -- Composer for package management - -## Installation - -Install the PHP AI Client via Composer: - -```bash -composer require wordpress/php-ai-client -``` - -## Current Implementation Status - -**⚠️ Important**: The PHP AI Client is currently in active development. This guide covers what's available now and what's coming soon. - -### What Works Now ✅ - -- Provider registry system -- Traditional API methods (`generateTextResult`, `generateImageResult`, etc.) -- Core interfaces and DTOs -- Operation tracking - -### What's Coming Soon 🚧 - -- Fluent API (`AiClient::prompt()` - currently throws RuntimeException) -- Concrete provider implementations (OpenAI, Anthropic, Google) -- Builder classes for easier message construction - -## Working with the Current Implementation - -### Basic Provider Registration - -The provider registry system is fully functional: - -```php -registerProvider(CustomAIProvider::class); - -// Check if provider is registered -if (AiClient::defaultRegistry()->hasProvider('your-provider-id')) { - echo "Provider registered successfully\n"; -} -``` - -### Traditional API Usage (Available Now) - -Use the traditional API methods. The SDK supports flexible input formats: - -```php -toText(); -} catch (\RuntimeException $e) { - echo "Need a registered provider: " . $e->getMessage(); -} - -// Option 2: Explicit message construction -$message = new UserMessage([new MessagePart('Write a haiku about PHP development')]); -try { - $result = AiClient::generateTextResult([$message], $customModel); - echo $result->toText(); -} catch (\RuntimeException $e) { - echo "Need a registered provider: " . $e->getMessage(); -} - -// Option 3: Without model (auto-discovery - requires registered providers) -try { - $result = AiClient::generateTextResult('Write a haiku about PHP development'); - echo $result->toText(); -} catch (\RuntimeException $e) { - echo "No suitable models found: " . $e->getMessage(); -} -``` - -### Provider Registry Operations - -Discover and work with providers: - -```php -findModelsMetadataForSupport($requirements); - -foreach ($providersMetadata as $providerData) { - echo "Provider: " . $providerData->getProvider()->getName() . "\n"; - foreach ($providerData->getModels() as $model) { - echo " Model: " . $model->getName() . "\n"; - } -} -``` - -### Operation Tracking - -Create and track AI operations: - -```php -getId() . "\n"; -echo "Operation State: " . $operation->getState()->value . "\n"; -``` - -## Future Features (Coming Soon) - -The following features are planned but not yet implemented: - -### 🚧 Fluent API (Not Available) - -```php -generateText(); -``` - -### 🚧 Concrete Provider Implementations - -```php -withImage($file)->get(); -``` - -## Working with Current DTOs - -### Creating Messages - -Build messages using the available DTO classes: - -```php -toText(); - - // Access token usage information - $usage = $result->getTokenUsage(); - echo "Tokens used: " . $usage->getTotalTokens() . "\n"; - - // Get provider-specific metadata - $metadata = $result->getProviderMetadata(); - print_r($metadata); - -} catch (\Exception $e) { - echo "Generation failed: " . $e->getMessage() . "\n"; -} -``` - -### Provider Configuration Checking - -Check if providers are properly configured: - -```php -isProviderConfigured('your-custom-provider')) { - echo "Provider is ready to use\n"; -} else { - echo "Provider needs configuration\n"; -} -``` - -## Error Handling - -Handle potential errors in the current implementation: - -```php -toText(); - -} catch (\RuntimeException $e) { - // Handle not-yet-implemented features - echo "Feature not ready: " . $e->getMessage() . "\n"; - -} catch (\InvalidArgumentException $e) { - // Handle invalid arguments - echo "Invalid input: " . $e->getMessage() . "\n"; - -} catch (\Exception $e) { - // Handle other errors - echo "Error: " . $e->getMessage() . "\n"; -} -``` - -## Next Steps - -Now that you understand the current implementation: - -1. **Read [Registering Providers](4.registering-providers.md)** - Learn how to implement your own AI providers -2. **Study the interfaces** - Review the complete interface definitions in `src/Providers/Contracts/` -3. **Check the tests** - Look at `tests/` for concrete examples of how the APIs work -4. **Monitor development** - Watch for updates as concrete provider implementations are added - -## Need Help? - -- Check the [Architecture documentation](ARCHITECTURE.md) for deeper technical details -- Review the [Glossary](GLOSSARY.md) for key terms and concepts -- Look at the comprehensive test suite in `tests/` for usage examples -- Study the interface definitions in `src/Providers/Contracts/` for implementation guidance \ No newline at end of file diff --git a/docs/2.quick-start.md b/docs/2.quick-start.md new file mode 100644 index 00000000..040140ce --- /dev/null +++ b/docs/2.quick-start.md @@ -0,0 +1,306 @@ +# 2. Quick Start + +Get up and running with the PHP AI Client in minutes. Choose your path: + +## PHP Applications + +### Installation + +```bash +composer require wordpress/php-ai-client +``` + +### Smart Model Selection + +The SDK automatically chooses the best available model for your task: + +```php +toText(); + +// Auto-discovery with preferences +$result = AiClient::prompt('Explain machine learning to a 5-year-old') + ->usingTemperature(0.7) + ->usingMaxTokens(200) + ->generateTextResult(); // SDK selects optimal model based on prompt + +echo $result->toText(); + +// Different content types are automatically routed to appropriate models +$image = AiClient::prompt('A futuristic robot teaching children') + ->generateImageResult(); // Auto-selects best image model + +$audio = AiClient::convertTextToSpeechResult('Hello world'); +// Auto-selects best speech synthesis model +``` + +### Working with Providers + +```php +registerProvider(OpenAiProvider::class); +$registry->setProviderRequestAuthentication( + 'openai', + new ApiKeyRequestAuthentication($_ENV['OPENAI_API_KEY']) +); + +// Use specific model +$model = $registry->getProviderModel('openai', 'gpt-4o'); +$result = AiClient::prompt('Generate product descriptions for coffee beans') + ->usingModel($model) + ->usingTemperature(0.8) + ->generateTextResult(); + +echo $result->toText(); +``` + +### Laravel Integration + +```php + [ + 'key' => env('OPENAI_API_KEY'), + ], + 'anthropic' => [ + 'key' => env('ANTHROPIC_API_KEY'), + ], +]; +``` + +```php +registry = AiClient::defaultRegistry(); + + // Register providers + $this->registry->registerProvider(OpenAiProvider::class); + $this->registry->setProviderRequestAuthentication( + 'openai', + new ApiKeyRequestAuthentication(config('services.openai.key')) + ); + } + + public function generateContent(string $prompt, array $config = []): string + { + $builder = AiClient::prompt($prompt); + + if (isset($config['temperature'])) { + $builder->usingTemperature($config['temperature']); + } + + if (isset($config['max_tokens'])) { + $builder->usingMaxTokens($config['max_tokens']); + } + + return $builder->generateTextResult()->toText(); + } +} +``` + +## WordPress Integration + +### Plugin Setup + +```php +'; + echo '

This plugin requires the PHP AI Client library.

'; + echo ''; + }); + return; +} + +class YourAiPlugin { + public function __construct() { + add_action('init', [$this, 'setupProviders']); + add_action('wp_ajax_generate_content', [$this, 'handleAjaxGeneration']); + } + + public function setupProviders() { + $registry = WordPress\AiClient\AiClient::defaultRegistry(); + + // Setup OpenAI if key exists + $openaiKey = get_option('your_plugin_openai_key'); + if (!empty($openaiKey)) { + $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class); + $registry->setProviderRequestAuthentication( + 'openai', + new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($openaiKey) + ); + } + } + + public function handleAjaxGeneration() { + check_ajax_referer('generate_content'); + + $prompt = sanitize_text_field($_POST['prompt']); + + try { + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature(0.7) + ->generateTextResult(); + + wp_send_json_success(['content' => $result->toText()]); + } catch (Exception $e) { + wp_send_json_error(['message' => 'Generation failed']); + } + } +} + +new YourAiPlugin(); +``` + +### Theme Integration + +```php +post_title + ) + ->usingTemperature(0.6) + ->usingMaxTokens(100) + ->generateTextResult(); + + return $result->toText(); + } catch (Exception $e) { + return get_the_excerpt($post_id); + } +} + +// Use in templates +// {{ generate_post_excerpt(get_the_ID()) }} +``` + +### WordPress CLI Integration + +```php +usingTemperature(0.7) + ->generateTextResult(); + + WP_CLI::success($result->toText()); + } catch (Exception $e) { + WP_CLI::error('Generation failed: ' . $e->getMessage()); + } + }); +} + +// Usage: wp ai generate "Write about WordPress security" +``` + +## Multi-Modal Examples + +### Image Generation + +```php +generateImageResult(); + +echo "Generated image URL: " . $image->toImageFile()->getUrl(); +``` + +### Text-to-Speech + +```php +toAudioFile()->getPath(); +``` + +## Error Handling + +```php +usingTemperature(0.7) + ->generateTextResult(); + + echo $result->toText(); +} catch (AiClientException $e) { + // Handle AI-specific errors + error_log('AI generation failed: ' . $e->getMessage()); + echo 'Content generation is temporarily unavailable.'; +} catch (Exception $e) { + // Handle general errors + error_log('Unexpected error: ' . $e->getMessage()); + echo 'An error occurred.'; +} +``` + +## Next Steps + +- **[PHP Applications](3.php-applications.md)** - Deep dive into framework integration patterns +- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific features and patterns +- **[Advanced Usage](5.advanced-usage.md)** - Custom providers, production deployment, and optimization + +## Need Help? + +- Check [GitHub Issues](https://github.com/WordPress/php-ai-client/issues) for common problems +- Review the [Architecture documentation](ARCHITECTURE.md) for technical details +- Look at example implementations in the `examples/` directory "Coming Soon" diff --git a/docs/3.php-applications.md b/docs/3.php-applications.md new file mode 100644 index 00000000..6b09ccdd --- /dev/null +++ b/docs/3.php-applications.md @@ -0,0 +1,610 @@ +# 3. PHP Applications + +The PHP AI Client works in any PHP environment. This guide covers framework integrations, production patterns, and standalone usage. + +## Framework Integration + +### Laravel + +#### Service Provider + +```php +app->singleton('ai-client', function () { + $registry = AiClient::defaultRegistry(); + + // Register providers based on config + if (config('ai.openai.enabled')) { + $registry->registerProvider(OpenAiProvider::class); + $registry->setProviderRequestAuthentication( + 'openai', + new ApiKeyRequestAuthentication(config('ai.openai.key')) + ); + } + + if (config('ai.anthropic.enabled')) { + $registry->registerProvider(AnthropicProvider::class); + $registry->setProviderRequestAuthentication( + 'anthropic', + new AnthropicApiKeyRequestAuthentication(config('ai.anthropic.key')) + ); + } + + return $registry; + }); + } +} +``` + +#### Configuration + +```php + env('AI_DEFAULT_PROVIDER', 'openai'), + + 'openai' => [ + 'enabled' => env('AI_OPENAI_ENABLED', true), + 'key' => env('OPENAI_API_KEY'), + 'default_model' => env('AI_OPENAI_MODEL', 'gpt-4o'), + ], + + 'anthropic' => [ + 'enabled' => env('AI_ANTHROPIC_ENABLED', false), + 'key' => env('ANTHROPIC_API_KEY'), + 'default_model' => env('AI_ANTHROPIC_MODEL', 'claude-3-5-sonnet'), + ], + + 'generation' => [ + 'temperature' => env('AI_TEMPERATURE', 0.7), + 'max_tokens' => env('AI_MAX_TOKENS', 1000), + 'timeout' => env('AI_TIMEOUT', 30), + ], +]; +``` + +#### Service Class + +```php +registry = $registry; + } + + public function generateContent(string $prompt, array $options = []): string + { + try { + $builder = AiClient::prompt($prompt) + ->usingTemperature($options['temperature'] ?? config('ai.generation.temperature')) + ->usingMaxTokens($options['max_tokens'] ?? config('ai.generation.max_tokens')); + + if (isset($options['provider']) && isset($options['model'])) { + $model = $this->registry->getProviderModel($options['provider'], $options['model']); + $builder->usingModel($model); + } + + return $builder->generateTextResult()->toText(); + } catch (\Exception $e) { + throw new ContentGenerationException('Failed to generate content: ' . $e->getMessage()); + } + } + + public function generateImage(string $prompt, array $options = []): string + { + try { + $builder = AiClient::prompt($prompt); + + if (isset($options['provider']) && isset($options['model'])) { + $model = $this->registry->getProviderModel($options['provider'], $options['model']); + $builder->usingModel($model); + } + + $image = $builder->generateImageResult(); + return $image->toImageFile()->getUrl(); + } catch (\Exception $e) { + throw new ContentGenerationException('Failed to generate image: ' . $e->getMessage()); + } + } +} +``` + +### Symfony + +#### Service Configuration + +```yaml +# config/services.yaml + +services: + WordPress\AiClient\Providers\Registry\ProviderRegistry: + factory: ['WordPress\AiClient\AiClient', 'defaultRegistry'] + calls: + - [registerProvider, ['WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider']] + - [setProviderRequestAuthentication, ['openai', '@ai.openai_auth']] + + ai.openai_auth: + class: WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication + arguments: ['%env(OPENAI_API_KEY)%'] + + App\Service\ContentGenerator: + arguments: + - '@WordPress\AiClient\Providers\Registry\ProviderRegistry' +``` + +#### Service Implementation + +```php +registry = $registry; + } + + public function generateArticle(string $topic, string $tone = 'professional'): array + { + $prompts = [ + 'title' => "Generate an engaging title for an article about: {$topic}", + 'intro' => "Write a compelling introduction for an article titled about {$topic}. Tone: {$tone}", + 'conclusion' => "Write a strong conclusion for an article about {$topic}. Tone: {$tone}", + ]; + + $results = []; + foreach ($prompts as $section => $prompt) { + $results[$section] = AiClient::prompt($prompt) + ->usingTemperature(0.8) + ->usingMaxTokens(200) + ->generateTextResult() + ->toText(); + } + + return $results; + } +} +``` + +## Production Patterns + +### Connection Pool Management + +```php +registry = AiClient::defaultRegistry(); + + // Configure HTTP client with connection pooling + $httpClient = HttpTransporterFactory::createTransporter([ + 'pool_size' => 10, + 'timeout' => 30, + 'retry_attempts' => 3, + ]); + + $this->registry->setHttpTransporter($httpClient); + $this->setupProviders(); + } + + public static function getInstance(): self + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + + private function setupProviders(): void + { + $providers = [ + 'openai' => [ + 'class' => \WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class, + 'auth' => new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication( + $_ENV['OPENAI_API_KEY'] ?? '' + ), + ], + 'anthropic' => [ + 'class' => \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class, + 'auth' => new \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicApiKeyRequestAuthentication( + $_ENV['ANTHROPIC_API_KEY'] ?? '' + ), + ], + ]; + + foreach ($providers as $id => $config) { + if (!empty($config['auth'])) { + $this->registry->registerProvider($config['class']); + $this->registry->setProviderRequestAuthentication($id, $config['auth']); + } + } + } + + public function getRegistry() + { + return $this->registry; + } +} +``` + +### Caching Layer + +```php +cache = $cache; + $this->registry = $registry; + } + + public function generateWithCache(string $prompt, array $options = [], int $ttl = 3600): string + { + $cacheKey = $this->generateCacheKey($prompt, $options); + + // Check cache first + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached; + } + + // Generate content + try { + $builder = AiClient::prompt($prompt) + ->usingTemperature($options['temperature'] ?? 0.7) + ->usingMaxTokens($options['max_tokens'] ?? 1000); + + if (isset($options['model'])) { + $model = $this->registry->getProviderModel($options['provider'] ?? 'openai', $options['model']); + $builder->usingModel($model); + } + + $result = $builder->generateTextResult()->toText(); + + // Cache the result + $this->cache->set($cacheKey, $result, $ttl); + + return $result; + } catch (\Exception $e) { + throw new \RuntimeException('Content generation failed: ' . $e->getMessage()); + } + } + + private function generateCacheKey(string $prompt, array $options): string + { + return 'ai_content_' . md5($prompt . serialize($options)); + } +} +``` + +### Rate Limiting + +```php +rateLimiter = $rateLimiter; + $this->registry = $registry; + } + + public function generateWithRateLimit(string $prompt, string $userId, array $options = []): string + { + $key = "ai_generation_{$userId}"; + + if (!$this->rateLimiter->attempt($key, 10, 60)) { // 10 requests per minute + throw new \Exception('Rate limit exceeded. Please wait before making another request.'); + } + + return AiClient::prompt($prompt) + ->usingTemperature($options['temperature'] ?? 0.7) + ->generateTextResult() + ->toText(); + } +} +``` + +## Error Handling & Monitoring + +### Comprehensive Error Handler + +```php +logger = $logger; + $this->metrics = $metrics; + } + + public function handleGeneration(callable $generationCallback, array $fallbackOptions = []): string + { + $attempts = 0; + $maxAttempts = $fallbackOptions['max_attempts'] ?? 3; + + while ($attempts < $maxAttempts) { + try { + $result = $generationCallback(); + $this->metrics->increment('ai.generation.success'); + return $result; + } catch (RateLimitException $e) { + $this->logger->warning('AI rate limit hit', ['attempt' => $attempts + 1]); + $this->metrics->increment('ai.generation.rate_limited'); + + if ($attempts < $maxAttempts - 1) { + sleep(pow(2, $attempts)); // Exponential backoff + } + } catch (AuthenticationException $e) { + $this->logger->error('AI authentication failed', ['error' => $e->getMessage()]); + $this->metrics->increment('ai.generation.auth_failed'); + throw $e; // Don't retry auth errors + } catch (AiClientException $e) { + $this->logger->error('AI client error', [ + 'error' => $e->getMessage(), + 'attempt' => $attempts + 1 + ]); + $this->metrics->increment('ai.generation.client_error'); + } catch (\Exception $e) { + $this->logger->error('Unexpected AI error', [ + 'error' => $e->getMessage(), + 'attempt' => $attempts + 1 + ]); + $this->metrics->increment('ai.generation.unexpected_error'); + } + + $attempts++; + } + + // All attempts failed, return fallback or throw + if (isset($fallbackOptions['fallback_text'])) { + $this->metrics->increment('ai.generation.fallback_used'); + return $fallbackOptions['fallback_text']; + } + + throw new \RuntimeException('AI generation failed after ' . $maxAttempts . ' attempts'); + } +} +``` + +## Background Processing + +### Queue Integration + +```php +registry = $registry; + } + + public function handle(array $jobData): void + { + try { + $result = AiClient::prompt($jobData['prompt']) + ->usingTemperature($jobData['temperature'] ?? 0.7) + ->usingMaxTokens($jobData['max_tokens'] ?? 1000) + ->generateTextResult(); + + // Store result or trigger callback + $this->storeResult($jobData['job_id'], $result->toText()); + + if (isset($jobData['callback_url'])) { + $this->triggerCallback($jobData['callback_url'], $result->toText()); + } + } catch (\Exception $e) { + $this->handleJobFailure($jobData['job_id'], $e->getMessage()); + } + } + + private function storeResult(string $jobId, string $content): void + { + // Store in database or cache + } + + private function triggerCallback(string $url, string $content): void + { + // Make HTTP callback + } + + private function handleJobFailure(string $jobId, string $error): void + { + // Log error and update job status + } +} +``` + +## Testing + +### Mock Provider for Testing + +```php +responses[$prompt] = $response; + } + + public static function metadata(): ProviderMetadata + { + return new ProviderMetadata( + id: 'mock', + name: 'Mock Provider', + type: ProviderTypeEnum::TESTING + ); + } + + public static function model(string $modelId, ?ModelConfig $modelConfig = null): ModelInterface + { + return new MockModel($this->responses); + } + + // ... other required methods +} + +// Usage in tests +class ContentGeneratorTest extends TestCase +{ + public function testContentGeneration(): void + { + $mockProvider = new MockAiProvider(); + $mockProvider->addMockResponse( + 'Generate a product description', + 'This is a mock product description' + ); + + $registry = AiClient::defaultRegistry(); + $registry->registerProvider(MockAiProvider::class); + + $result = AiClient::prompt('Generate a product description') + ->usingModel($registry->getProviderModel('mock', 'test')) + ->generateTextResult(); + + $this->assertEquals('This is a mock product description', $result->toText()); + } +} +``` + +## Performance Optimization + +### Batch Processing + +```php +registry = $registry; + } + + public function generateBatch(array $prompts, array $options = []): array + { + $results = []; + $chunks = array_chunk($prompts, $options['batch_size'] ?? 5); + + foreach ($chunks as $chunk) { + $promises = []; + + // Create async requests + foreach ($chunk as $index => $prompt) { + $promises[$index] = $this->generateAsync($prompt, $options); + } + + // Wait for all requests in chunk to complete + $chunkResults = $this->resolvePromises($promises); + $results = array_merge($results, $chunkResults); + + // Rate limiting between chunks + if (isset($options['delay_between_chunks'])) { + sleep($options['delay_between_chunks']); + } + } + + return $results; + } + + private function generateAsync(string $prompt, array $options): Promise + { + // Implementation depends on your HTTP client's async capabilities + } + + private function resolvePromises(array $promises): array + { + // Wait for and resolve all promises + } +} +``` + +## Next Steps + +- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific patterns and hooks +- **[Advanced Usage](5.advanced-usage.md)** - Custom providers and advanced configuration + +## Useful Resources + +- [PSR-18 HTTP Client](https://www.php-fig.org/psr/psr-18/) for HTTP abstraction +- [Guzzle HTTP](https://docs.guzzlephp.org/) for advanced HTTP features +- [PHP-FIG Standards](https://www.php-fig.org/psr/) for coding standards +- [Composer Documentation](https://getcomposer.org/doc/) for dependency management \ No newline at end of file diff --git a/docs/3.using-providers.md b/docs/3.using-providers.md deleted file mode 100644 index 0b039fad..00000000 --- a/docs/3.using-providers.md +++ /dev/null @@ -1,350 +0,0 @@ -# Using AI Providers - -This guide explains how to work with the provider system in the PHP AI Client. - -## Current Status - -**⚠️ Important**: Concrete provider implementations (OpenAI, Anthropic, Google) are not yet available. This guide covers the provider registry system and how to work with custom provider implementations. - -## Understanding Providers - -A **provider** represents an AI service company or platform that offers access to AI models. The provider system includes: - -- **Provider Registry**: Central system for registering and discovering providers ✅ -- **Provider Interfaces**: Complete contracts for implementing providers ✅ -- **Provider Metadata**: Information about capabilities and configuration ✅ -- **Concrete Providers**: Specific implementations (🚧 in development) - -## Provider Registry Operations - -### Check Available Providers - -See which providers are registered and configured: - -```php -hasProvider('your-custom-provider')) { - echo "Custom provider is registered\n"; -} - -// Check if a provider is properly configured (has API key, etc.) -if ($registry->isProviderConfigured('your-custom-provider')) { - echo "Custom provider is configured and ready to use\n"; -} - -// Get provider class name by ID -try { - $className = $registry->getProviderClassName('your-custom-provider'); - echo "Provider class: " . $className . "\n"; -} catch (\InvalidArgumentException $e) { - echo "Provider not found: " . $e->getMessage() . "\n"; -} -``` - -### Find Models by Capability - -Discover models that support specific AI capabilities (requires custom provider implementations): - -```php -findModelsMetadataForSupport($requirements); - -foreach ($providersMetadata as $providerData) { - echo "Provider: " . $providerData->getProvider()->getName() . "\n"; - foreach ($providerData->getModels() as $model) { - echo " Model: " . $model->getName() . "\n"; - } -} -``` - -### Find Models within Specific Provider - -Look for models that support particular features within one provider: - -```php -findProviderModelsMetadataForSupport('your-custom-provider', $requirements); - -foreach ($models as $model) { - echo "Model: " . $model->getName() . " supports multimodal input\n"; -} -``` - -### Get Model Instances - -Create model instances from registered providers: - -```php -getProviderModel('your-custom-provider', 'text-model-id'); - -// Get a model instance with custom configuration -$config = new ModelConfig(); -$config->setTemperature(0.7); -$config->setMaxTokens(1000); - -$customModel = $registry->getProviderModel( - 'your-custom-provider', - 'text-model-id', - $config -); -``` - -## 🚧 Future: Concrete Provider Implementations - -The following provider implementations are planned but not yet available: - -### OpenAI (Coming Soon) - -```php -registerProvider(OpenAIProvider::class); -// $model = OpenAIProvider::model('gpt-4'); -``` - -### Anthropic (Coming Soon) - -```php -registerProvider(AnthropicProvider::class); -// $model = AnthropicProvider::model('claude-3-sonnet'); -``` - -### Google AI (Coming Soon) - -```php -registerProvider(GoogleProvider::class); -// $model = GoogleProvider::model('gemini-pro'); -``` - -## Working with Current Implementation - -### Model Configuration - -Configure models using the ModelConfig DTO: - -```php -setTemperature(0.7); // Creativity level -$config->setMaxTokens(1000); // Response length limit -$config->setTopK(40); // Top-K sampling -$config->setTopP(0.9); // Nucleus sampling -$config->setCandidateCount(3); // Multiple response options - -// Set output modalities -$config->setOutputModalities([ModalityEnum::TEXT]); - -// Set output format -$config->setOutputMimeType('application/json'); - -// Get a configured model instance -$model = AiClient::defaultRegistry()->getProviderModel( - 'your-custom-provider', - 'model-id', - $config -); -``` - -### Provider Availability Checking - -Use the static interface to check provider availability: - -```php -toText() . "\n"; - - // Access usage information - $usage = $result->getTokenUsage(); - echo "Tokens used: " . $usage->getTotalTokens() . "\n"; - -} catch (\RuntimeException $e) { - echo "Generation failed: " . $e->getMessage() . "\n"; -} - -// Explicit message construction (for complex messages) -$messages = [new UserMessage([new MessagePart('Explain PHP to a beginner')])]; -try { - $result = AiClient::generateTextResult($messages, $customModel); - echo "Generated text: " . $result->toText() . "\n"; -} catch (\RuntimeException $e) { - echo "Generation failed: " . $e->getMessage() . "\n"; -} - -// Image generation -try { - $result = AiClient::generateImageResult('A diagram of PHP architecture', $customImageModel); - $imageFile = $result->toImageFile(); - - if ($imageFile->getUrl()) { - echo "Generated image URL: " . $imageFile->getUrl() . "\n"; - } - -} catch (\RuntimeException $e) { - echo "Image generation failed: " . $e->getMessage() . "\n"; -} -``` - -### Streaming Support - -The streaming API is available (with compatible providers): - -```php -toText(); - flush(); - } -} catch (\RuntimeException $e) { - echo "Streaming failed: " . $e->getMessage() . "\n"; -} -``` - -### Operations Support - -Create and track operations: - -```php -getState()->value . "\n"; -echo "Image operation state: " . $imageOp->getState()->value . "\n"; - -// Operations can be tracked and managed asynchronously -if ($textOp->getState() === OperationStateEnum::SUCCEEDED) { - $result = $textOp->getResult(); - echo "Operation completed: " . $result->toText() . "\n"; -} -``` - -## Error Handling - -Handle different types of errors that may occur: - -```php -toText(); - -} catch (\RuntimeException $e) { - // Handle not-yet-implemented features or provider errors - echo "Runtime error: " . $e->getMessage() . "\n"; - -} catch (\InvalidArgumentException $e) { - // Handle invalid arguments (wrong model, bad configuration, etc.) - echo "Invalid argument: " . $e->getMessage() . "\n"; - -} catch (\Exception $e) { - // Handle unexpected errors - echo "Unexpected error: " . $e->getMessage() . "\n"; -} -``` - -## Next Steps - -1. **Learn about [Registering Providers](4.registering-providers.md)** - Implement your own AI providers -2. **Study the interfaces** - Review complete interface definitions in `src/Providers/Contracts/` -3. **Check the tests** - Look at `tests/` for concrete usage examples -4. **Review the Architecture** - Read [ARCHITECTURE.md](ARCHITECTURE.md) for technical details \ No newline at end of file diff --git a/docs/4.registering-providers.md b/docs/4.registering-providers.md deleted file mode 100644 index 5e4c2193..00000000 --- a/docs/4.registering-providers.md +++ /dev/null @@ -1,630 +0,0 @@ - # Registering AI Providers - -This guide explains how to implement and register custom AI providers with the PHP AI Client. - -## Understanding Provider Implementation - -Since the PHP AI Client doesn't include concrete provider implementations yet, you'll need to create your own providers to use the system. This guide shows you how to implement providers following the established interfaces. - -## Why Implement Custom Providers? - -Provider implementation is currently necessary for: - -- **Testing the SDK**: No concrete providers exist yet for testing -- **Custom AI Services**: Integrating proprietary or local AI services -- **Contributing Back**: Building providers that can be contributed to the project -- **Learning**: Understanding the architecture before concrete providers are available - -## Provider Interface Requirements - -The current implementation uses **static methods** for the ProviderInterface. Here's the correct interface to implement: - -### Basic Provider Registration - -Register a provider using its class name: - -```php -registerProvider(CustomAIProvider::class); - -// Verify registration -if (AiClient::defaultRegistry()->hasProvider('custom-ai')) { - echo "Custom provider registered successfully\n"; -} -``` - -### Static Provider Interface - -All providers must implement the `ProviderInterface` with **static methods**: - -```php - [ - 'name' => 'Custom Text Generator', - 'capabilities' => [CapabilityEnum::TEXT_GENERATION], - ], - 'custom-image-model' => [ - 'name' => 'Custom Image Generator', - 'capabilities' => [CapabilityEnum::IMAGE_GENERATION], - ], - ]; - - public function listModelMetadata(): array - { - $metadata = []; - - foreach ($this->models as $id => $config) { - $metadata[] = new ModelMetadata( - id: $id, - name: $config['name'], - supportedCapabilities: $config['capabilities'] - ); - } - - return $metadata; - } - - public function hasModelMetadata(string $modelId): bool - { - return isset($this->models[$modelId]); - } - - public function getModelMetadata(string $modelId): ModelMetadata - { - if (!$this->hasModelMetadata($modelId)) { - throw new InvalidArgumentException("Model not found: $modelId"); - } - - $config = $this->models[$modelId]; - - return new ModelMetadata( - id: $modelId, - name: $config['name'], - supportedCapabilities: $config['capabilities'] - ); - } -} -``` - -## Creating Custom Models - -### Text Generation Model - -Implement a model that can generate text: - -```php -formatPrompt($prompt); - - // Create HTTP request - $request = new Request( - method: 'POST', - uri: 'https://api.custom-ai.com/v1/generate', - headers: [ - 'Authorization' => ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']], - 'Content-Type' => ['application/json'], - ], - body: json_encode($requestData) - ); - - // Send request - $response = $this->httpTransporter->send($request); - - // Parse response into GenerativeAiResult - return $this->parseResponse($response); - } - - private function formatPrompt(array $messages): array - { - // Convert PHP AI Client messages to your provider's format - $formattedMessages = []; - - foreach ($messages as $message) { - if ($message instanceof Message) { - $formattedMessages[] = [ - 'role' => $message->getRole()->value, - 'content' => $this->extractTextFromMessage($message), - ]; - } - } - - return [ - 'model' => $this->modelId, - 'messages' => $formattedMessages, - 'temperature' => $this->config->getTemperature(), - 'max_tokens' => $this->config->getMaxTokens(), - ]; - } - - private function parseResponse(Response $response): GenerativeAiResult - { - $data = json_decode($response->getBody(), true); - - // Convert your provider's response to PHP AI Client format - $candidates = []; - foreach ($data['choices'] as $choice) { - $message = new ModelMessage($choice['message']['content']); - $candidates[] = new Candidate( - message: $message, - finishReason: FinishReasonEnum::STOP - ); - } - - return new GenerativeAiResult( - id: $data['id'], - candidates: $candidates, - tokenUsage: new TokenUsage( - promptTokens: $data['usage']['prompt_tokens'], - completionTokens: $data['usage']['completion_tokens'], - totalTokens: $data['usage']['total_tokens'] - ) - ); - } - - // Implement other required interface methods... - public function metadata(): ModelMetadata { /* ... */ } - public function setConfig(ModelConfig $config): void { /* ... */ } - public function getConfig(): ModelConfig { /* ... */ } - public function setHttpTransporter(HttpTransporterInterface $transporter): void { /* ... */ } - public function getHttpTransporter(): HttpTransporterInterface { /* ... */ } -} -``` - -### Image Generation Model - -Implement a model that can generate images: - -```php - ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']], - 'Content-Type' => ['application/json'], - ], - body: json_encode([ - 'prompt' => $this->extractTextFromMessages($prompt), - 'size' => '1024x1024', - 'quality' => 'standard', - ]) - ); - - $response = $this->httpTransporter->send($request); - $data = json_decode($response->getBody(), true); - - // Create File object for the generated image - $imageFile = new File( - fileType: FileTypeEnum::REMOTE, - mimeType: 'image/png', - url: $data['data'][0]['url'] - ); - - // Create message with the image - $message = new ModelMessage(); - $message->addPart(new MessagePart( - type: MessagePartTypeEnum::FILE, - file: $imageFile - )); - - $candidate = new Candidate( - message: $message, - finishReason: FinishReasonEnum::STOP - ); - - return new GenerativeAiResult( - id: $data['id'], - candidates: [$candidate], - tokenUsage: new TokenUsage(0, 0, 0) // Images don't use tokens - ); - } -} -``` - -## Advanced Provider Features - -### Supporting Operations - -For long-running requests, implement operation support: - -```php - ['Bearer ' . $_ENV['CUSTOM_AI_API_KEY']]] - ); - - $response = $this->httpTransporter->send($request); - $data = json_decode($response->getBody(), true); - - return new GenerativeAiOperation( - id: $data['id'], - state: OperationStateEnum::from($data['status']), - result: $data['result'] ? $this->parseResult($data['result']) : null - ); - } -} -``` - -### Custom Authentication - -Implement custom authentication methods: - -```php -generateSignature($request->getBody(), $timestamp); - - $request->addHeader('X-API-Key', $this->apiKey); - $request->addHeader('X-Timestamp', (string)$timestamp); - $request->addHeader('X-Signature', $signature); - } - - private function generateSignature(string $body, int $timestamp): string - { - $data = $body . $timestamp; - return hash_hmac('sha256', $data, $this->secretKey); - } - - public function getJsonSchema(): array - { - return [ - 'type' => 'object', - 'properties' => [ - 'apiKey' => ['type' => 'string'], - 'secretKey' => ['type' => 'string'] - ] - ]; - } -} -``` - -## Testing Custom Providers - -### Unit Testing - -Create comprehensive tests for your provider: - -```php -provider = new CustomAIProvider(); - } - - public function testProviderRegistration(): void - { - $registry = AiClient::defaultRegistry(); - $registry->registerProvider(CustomAIProvider::class); - - $this->assertTrue($registry->hasProvider('custom-ai')); - $this->assertEquals(CustomAIProvider::class, $registry->getProviderClassName('custom-ai')); - } - - public function testModelGeneration(): void - { - $model = $this->provider->model('custom-text-model'); - - $this->assertInstanceOf(ModelInterface::class, $model); - $this->assertEquals('custom-text-model', $model->metadata()->getId()); - } - - public function testTextGeneration(): void - { - // Set up mock HTTP responses - $this->mockHttpResponse([ - 'id' => 'test-id', - 'choices' => [ - ['message' => ['content' => 'Generated text']], - ], - 'usage' => [ - 'prompt_tokens' => 10, - 'completion_tokens' => 5, - 'total_tokens' => 15, - ], - ]); - - $model = $this->provider->model('custom-text-model'); - $result = $model->generateTextResult([ - new UserMessage('Test prompt') - ]); - - $this->assertEquals('Generated text', $result->toText()); - } -} -``` - -### Integration Testing - -Test your provider with the full AI Client: - -```php -registerProvider(CustomAIProvider::class); - -$text = AiClient::prompt('Test prompt') - ->usingProvider('custom-ai') - ->generateText(); - -echo "Generated: " . $text; -``` - -## Best Practices - -### Error Handling - -Implement comprehensive error handling: - -```php -httpTransporter->send($request); - - if ($response->getStatusCode() >= 400) { - throw new AiProviderException( - "API request failed: " . $response->getBody(), - $response->getStatusCode() - ); - } - - return $this->parseResponse($response); - } catch (HttpException $e) { - throw new AiProviderException( - "Network error: " . $e->getMessage(), - 0, - $e - ); - } - } -} -``` - -### Configuration Validation - -Validate provider configuration: - -```php -validateConfiguration(); - } - - private function validateConfiguration(): void - { - $apiKey = $_ENV['CUSTOM_AI_API_KEY'] ?? null; - - if (empty($apiKey)) { - throw new InvalidArgumentException( - 'CUSTOM_AI_API_KEY environment variable is required' - ); - } - - if (strlen($apiKey) < 32) { - throw new InvalidArgumentException( - 'Invalid API key format' - ); - } - } -} -``` - -### Documentation - -Document your provider thoroughly: - -```php -toText(), 'ai_client', HOUR_IN_SECONDS); + + // Update usage statistics + $count = get_option('ai_generations_count', 0); + update_option('ai_generations_count', $count + 1); + } + + public function customProviderSelection($defaultProvider, $prompt) { + // Use different providers based on content type + if (strpos($prompt, 'image') !== false) { + return 'openai'; // Prefer OpenAI for image generation + } + + if (strlen($prompt) > 1000) { + return 'anthropic'; // Use Claude for long content + } + + return $defaultProvider; + } +} + +new WpAiContentPlugin(); +``` + +## Plugin Development Patterns + +### Complete Plugin Example + +```php +

'; + echo esc_html__('AI Content Generator requires the PHP AI Client library.', 'ai-content'); + echo '

'; + }); + return; +} + +class AiContentGenerator { + const OPTION_PREFIX = 'ai_content_'; + + public function __construct() { + add_action('init', [$this, 'init']); + add_action('admin_menu', [$this, 'addAdminMenu']); + add_action('wp_ajax_ai_generate_content', [$this, 'ajaxGenerateContent']); + add_action('add_meta_boxes', [$this, 'addMetaBoxes']); + + // WordPress AI Client hooks + add_filter('wp_ai_client_default_temperature', [$this, 'getDefaultTemperature']); + add_filter('wp_ai_client_max_tokens', [$this, 'getMaxTokens']); + } + + public function init() { + // Load text domain + load_plugin_textdomain('ai-content', false, dirname(plugin_basename(__FILE__)) . '/languages'); + + // Setup AI providers with WordPress options + $this->setupAiProviders(); + } + + private function setupAiProviders() { + $registry = WordPress\AiClient\AiClient::defaultRegistry(); + + // OpenAI setup + $openaiKey = get_option(self::OPTION_PREFIX . 'openai_key'); + if (!empty($openaiKey)) { + $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class); + $registry->setProviderRequestAuthentication( + 'openai', + new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($openaiKey) + ); + } + + // Anthropic setup + $anthropicKey = get_option(self::OPTION_PREFIX . 'anthropic_key'); + if (!empty($anthropicKey)) { + $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class); + $registry->setProviderRequestAuthentication( + 'anthropic', + new \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicApiKeyRequestAuthentication($anthropicKey) + ); + } + } + + public function addAdminMenu() { + add_options_page( + __('AI Content Settings', 'ai-content'), + __('AI Content', 'ai-content'), + 'manage_options', + 'ai-content-settings', + [$this, 'renderSettingsPage'] + ); + + add_menu_page( + __('AI Generator', 'ai-content'), + __('AI Generator', 'ai-content'), + 'edit_posts', + 'ai-generator', + [$this, 'renderGeneratorPage'], + 'dashicons-robot', + 30 + ); + } + + public function renderSettingsPage() { + if (isset($_POST['submit'])) { + $this->saveSettings(); + echo '

' . esc_html__('Settings saved!', 'ai-content') . '

'; + } + + $openaiKey = get_option(self::OPTION_PREFIX . 'openai_key', ''); + $anthropicKey = get_option(self::OPTION_PREFIX . 'anthropic_key', ''); + $temperature = get_option(self::OPTION_PREFIX . 'temperature', 0.7); + + ?> +
+

+
+ + + + + + + + + + + + + + +
+ +

+
+ +

+
+ +

+
+ +
+
+ +
+

+ +
+ +

+ + +
+ + +
+ + + __('Permission denied', 'ai-content')]); + } + + $prompt = sanitize_text_field($_POST['prompt'] ?? ''); + $type = sanitize_text_field($_POST['type'] ?? 'text'); + + if (empty($prompt)) { + wp_send_json_error(['message' => __('Prompt is required', 'ai-content')]); + } + + try { + if ($type === 'image') { + $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); + wp_send_json_success(['url' => $result->toImageFile()->getUrl()]); + } else { + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature(floatval(get_option(self::OPTION_PREFIX . 'temperature', 0.7))) + ->generateTextResult(); + wp_send_json_success(['content' => $result->toText()]); + } + } catch (Exception $e) { + wp_send_json_error(['message' => $e->getMessage()]); + } + } + + public function addMetaBoxes() { + add_meta_box( + 'ai-content-generator', + __('AI Content Generator', 'ai-content'), + [$this, 'renderMetaBox'], + ['post', 'page'], + 'side' + ); + } + + public function renderMetaBox($post) { + ?> +
+

+ + +

+

+ + +

+
+ + + post_title; + if (!empty($post->post_content)) { + $prompt .= "\n\nContent preview: " . wp_trim_words($post->post_content, 50); + } + + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature(0.6) + ->usingMaxTokens($length * 2) // Rough token estimation + ->generateTextResult(); + + $excerpt = $result->toText(); + + // Cache for 1 hour + set_transient('ai_excerpt_' . $post_id, $excerpt, HOUR_IN_SECONDS); + + return $excerpt; + } catch (Exception $e) { + // Fallback to regular excerpt + return get_the_excerpt($post_id); + } +} + +// Generate SEO meta descriptions +function generate_ai_meta_description($post_id) { + $post = get_post($post_id); + + try { + $prompt = "Write a SEO-optimized meta description (150 characters max) for: " . $post->post_title; + + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature(0.5) + ->usingMaxTokens(50) + ->generateTextResult(); + + return substr($result->toText(), 0, 150); + } catch (Exception $e) { + return wp_trim_words($post->post_content, 20); + } +} + +// Hook into WordPress SEO plugins +add_filter('wpseo_metadesc', function($metadesc, $post_id) { + if (empty($metadesc)) { + return generate_ai_meta_description($post_id); + } + return $metadesc; +}, 10, 2); +``` + +### Block Editor Integration + +```php + 'ai-content-block', + 'render_callback' => 'render_ai_content_block' + ]); +} + +function render_ai_content_block($attributes) { + $prompt = $attributes['prompt'] ?? ''; + + if (empty($prompt)) { + return '

Please enter a prompt in the block editor.

'; + } + + try { + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature(0.7) + ->generateTextResult(); + + return '
' . wp_kses_post($result->toText()) . '
'; + } catch (Exception $e) { + return '

Content generation failed. Please try again later.

'; + } +} +``` + +## REST API Extensions + +### Custom Endpoints + +```php + 'POST', + 'callback' => 'ai_rest_generate_content', + 'permission_callback' => function() { + return current_user_can('edit_posts'); + }, + 'args' => [ + 'prompt' => [ + 'required' => true, + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field' + ], + 'type' => [ + 'default' => 'text', + 'enum' => ['text', 'image', 'audio'] + ], + 'temperature' => [ + 'default' => 0.7, + 'type' => 'number', + 'minimum' => 0, + 'maximum' => 1 + ] + ] + ]); +} + +function ai_rest_generate_content($request) { + $prompt = $request['prompt']; + $type = $request['type']; + $temperature = $request['temperature']; + + try { + switch ($type) { + case 'image': + $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); + return rest_ensure_response([ + 'success' => true, + 'data' => [ + 'type' => 'image', + 'url' => $result->toImageFile()->getUrl(), + 'prompt' => $prompt + ] + ]); + + case 'audio': + $result = WordPress\AiClient\AiClient::convertTextToSpeechResult($prompt); + return rest_ensure_response([ + 'success' => true, + 'data' => [ + 'type' => 'audio', + 'url' => $result->toAudioFile()->getPath(), + 'prompt' => $prompt + ] + ]); + + default: // text + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature($temperature) + ->generateTextResult(); + + return rest_ensure_response([ + 'success' => true, + 'data' => [ + 'type' => 'text', + 'content' => $result->toText(), + 'prompt' => $prompt + ] + ]); + } + } catch (Exception $e) { + return new WP_Error('generation_failed', $e->getMessage(), ['status' => 500]); + } +} +``` + +## WP-CLI Integration + +```php + + * : The content prompt + * + * [--type=] + * : Content type (text, image, audio) + * --- + * default: text + * --- + * + * [--temperature=] + * : Temperature (0-1) + * --- + * default: 0.7 + * --- + * + * ## EXAMPLES + * + * wp ai generate "Write about WordPress" + * wp ai generate "A sunset landscape" --type=image + * + */ + public function generate($args, $assoc_args) { + $prompt = $args[0]; + $type = $assoc_args['type'] ?? 'text'; + $temperature = floatval($assoc_args['temperature'] ?? 0.7); + + WP_CLI::log("Generating {$type} content..."); + + try { + switch ($type) { + case 'image': + $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); + WP_CLI::success("Image generated: " . $result->toImageFile()->getUrl()); + break; + + case 'audio': + $result = WordPress\AiClient\AiClient::convertTextToSpeechResult($prompt); + WP_CLI::success("Audio generated: " . $result->toAudioFile()->getPath()); + break; + + default: + $result = WordPress\AiClient\AiClient::prompt($prompt) + ->usingTemperature($temperature) + ->generateTextResult(); + WP_CLI::success("Generated content:\n" . $result->toText()); + break; + } + } catch (Exception $e) { + WP_CLI::error("Generation failed: " . $e->getMessage()); + } + } + + /** + * Bulk generate posts with AI + * + * ## OPTIONS + * + * + * : File containing post topics (one per line) + * + * [--status=] + * : Post status + * --- + * default: draft + * --- + * + */ + public function bulk_posts($args, $assoc_args) { + $file = $args[0]; + $status = $assoc_args['status'] ?? 'draft'; + + if (!file_exists($file)) { + WP_CLI::error("File not found: {$file}"); + } + + $topics = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $progress = WP_CLI\Utils\make_progress_bar('Generating posts', count($topics)); + + foreach ($topics as $topic) { + try { + $content = WordPress\AiClient\AiClient::prompt("Write a detailed blog post about: {$topic}") + ->usingTemperature(0.7) + ->usingMaxTokens(1500) + ->generateTextResult(); + + wp_insert_post([ + 'post_title' => trim($topic), + 'post_content' => $content->toText(), + 'post_status' => $status, + 'post_type' => 'post' + ]); + + $progress->tick(); + } catch (Exception $e) { + WP_CLI::warning("Failed to generate post for: {$topic}"); + $progress->tick(); + } + } + + $progress->finish(); + WP_CLI::success("Bulk post generation complete!"); + } +} +``` + +## Performance & Caching + +### WordPress Object Cache Integration + +```php +usingTemperature($options['temperature']); + } + + if (isset($options['max_tokens'])) { + $builder->usingMaxTokens($options['max_tokens']); + } + + $result = $builder->generateTextResult()->toText(); + + self::set($cache_key, $result, $ttl); + + return $result; + } catch (Exception $e) { + // Cache errors for short time to avoid spam + self::set($cache_key . '_error', $e->getMessage(), 300); + throw $e; + } + } + + private static function getCacheKey($prompt, $options) { + return 'prompt_' . md5($prompt . serialize($options)); + } +} +``` + +## Next Steps + +- **[Advanced Usage](5.advanced-usage.md)** - Custom providers, production deployment, and optimization + +## Resources + +- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/) +- [WordPress REST API](https://developer.wordpress.org/rest-api/) +- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/) +- [WP-CLI Commands](https://wp-cli.org/commands/) \ No newline at end of file diff --git a/docs/5.advanced-usage.md b/docs/5.advanced-usage.md new file mode 100644 index 00000000..e4b63537 --- /dev/null +++ b/docs/5.advanced-usage.md @@ -0,0 +1,972 @@ +# 5. Advanced Usage + +Advanced patterns for custom providers, production deployment, monitoring, and optimization. + +## Custom AI Providers + +### Creating a Custom Provider + +Build your own AI provider to integrate proprietary or specialized AI services: + +```php + new CustomTextModel($modelId, $modelConfig ?? new ModelConfig()), + 'custom-image-v1' => new CustomImageModel($modelId, $modelConfig ?? new ModelConfig()), + default => throw new \InvalidArgumentException("Unsupported model: {$modelId}") + }; + } + + public static function availability(): ProviderAvailabilityInterface + { + return new CustomProviderAvailability(); + } + + public static function modelMetadataDirectory(): ModelMetadataDirectoryInterface + { + return new CustomModelMetadataDirectory(); + } +} +``` + +### Custom Model Implementation + +```php +modelId = $modelId; + $this->config = $config; + $this->httpClient = HttpTransporterFactory::createTransporter(); + } + + public function generateTextResult(array $messages): GenerativeAiResult + { + $payload = $this->buildApiPayload($messages); + + try { + $response = $this->httpClient->post('/api/v1/generate', [ + 'json' => $payload, + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->getApiKey(), + 'Content-Type' => 'application/json' + ] + ]); + + return $this->parseApiResponse($response); + } catch (\Exception $e) { + throw new \RuntimeException("Custom AI generation failed: " . $e->getMessage()); + } + } + + private function buildApiPayload(array $messages): array + { + return [ + 'model' => $this->modelId, + 'messages' => array_map(fn($msg) => $this->convertMessage($msg), $messages), + 'temperature' => $this->config->getTemperature(), + 'max_tokens' => $this->config->getMaxTokens(), + 'system_instruction' => $this->config->getSystemInstruction() + ]; + } + + private function parseApiResponse($response): GenerativeAiResult + { + $data = json_decode($response->getBody()->getContents(), true); + + if (!isset($data['choices'][0]['message']['content'])) { + throw new \RuntimeException('Invalid API response format'); + } + + return new GenerativeAiResult( + text: $data['choices'][0]['message']['content'], + usage: new Usage( + promptTokens: $data['usage']['prompt_tokens'] ?? 0, + completionTokens: $data['usage']['completion_tokens'] ?? 0, + totalTokens: $data['usage']['total_tokens'] ?? 0 + ) + ); + } + + // Implement other required methods... + public function metadata(): ModelMetadata { /* ... */ } + public function setConfig(ModelConfig $config): void { $this->config = $config; } + public function getConfig(): ModelConfig { return $this->config; } +} +``` + +### Provider Registration & Authentication + +```php +registerProvider(CustomAIProvider::class); + +// Set up authentication +$registry->setProviderRequestAuthentication( + 'custom-ai', + new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication( + $_ENV['CUSTOM_AI_API_KEY'] + ) +); + +// Use custom provider +$result = AiClient::prompt('Generate specialized content') + ->usingModel($registry->getProviderModel('custom-ai', 'custom-text-v1')) + ->generateTextResult(); + +echo $result->toText(); +``` + +## Production Configuration + +### Environment-Based Setup + +```php +config = [ + 'providers' => [ + 'openai' => [ + 'enabled' => (bool) $_ENV['OPENAI_ENABLED'] ?? true, + 'api_key' => $_ENV['OPENAI_API_KEY'] ?? '', + 'models' => [ + 'primary' => $_ENV['OPENAI_PRIMARY_MODEL'] ?? 'gpt-4o', + 'fallback' => $_ENV['OPENAI_FALLBACK_MODEL'] ?? 'gpt-4o-mini', + ], + 'rate_limits' => [ + 'requests_per_minute' => (int) $_ENV['OPENAI_RPM'] ?? 60, + 'tokens_per_minute' => (int) $_ENV['OPENAI_TPM'] ?? 100000, + ] + ], + 'anthropic' => [ + 'enabled' => (bool) $_ENV['ANTHROPIC_ENABLED'] ?? false, + 'api_key' => $_ENV['ANTHROPIC_API_KEY'] ?? '', + 'models' => [ + 'primary' => $_ENV['ANTHROPIC_PRIMARY_MODEL'] ?? 'claude-3-5-sonnet', + 'fallback' => $_ENV['ANTHROPIC_FALLBACK_MODEL'] ?? 'claude-3-haiku', + ] + ] + ], + 'cache' => [ + 'enabled' => (bool) $_ENV['AI_CACHE_ENABLED'] ?? true, + 'ttl' => (int) $_ENV['AI_CACHE_TTL'] ?? 3600, + 'driver' => $_ENV['AI_CACHE_DRIVER'] ?? 'redis', + ], + 'monitoring' => [ + 'enabled' => (bool) $_ENV['AI_MONITORING_ENABLED'] ?? true, + 'metrics_endpoint' => $_ENV['METRICS_ENDPOINT'] ?? '', + 'log_level' => $_ENV['AI_LOG_LEVEL'] ?? 'info', + ] + ]; + } + + public function setupProviders(): void + { + $registry = AiClient::defaultRegistry(); + + foreach ($this->config['providers'] as $providerId => $config) { + if (!$config['enabled'] || empty($config['api_key'])) { + continue; + } + + $this->registerProvider($registry, $providerId, $config); + } + } + + private function registerProvider(ProviderRegistry $registry, string $providerId, array $config): void + { + $providerClass = match($providerId) { + 'openai' => \WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class, + 'anthropic' => \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class, + 'google' => \WordPress\AiClient\ProviderImplementations\Google\GoogleProvider::class, + default => throw new \InvalidArgumentException("Unknown provider: {$providerId}") + }; + + $registry->registerProvider($providerClass); + $registry->setProviderRequestAuthentication( + $providerId, + new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($config['api_key']) + ); + } +} + +// Initialize production configuration +$config = new ProductionAIConfig(); +$config->setupProviders(); +``` + +### High-Availability Setup + +```php +providerPriority = $providerPriority; + $this->registry = $registry; + $this->logger = $logger; + } + + public function generateWithFallback(string $prompt, array $options = []): string + { + $lastException = null; + + foreach ($this->providerPriority as $providerId) { + if (!$this->registry->isProviderConfigured($providerId)) { + continue; + } + + try { + $model = $this->registry->getProviderModel( + $providerId, + $options['model'] ?? $this->getPreferredModel($providerId) + ); + + $result = AiClient::prompt($prompt) + ->usingModel($model) + ->usingTemperature($options['temperature'] ?? 0.7) + ->generateTextResult(); + + $this->logger->info("AI generation successful", [ + 'provider' => $providerId, + 'model' => $model->metadata()->getId(), + 'prompt_length' => strlen($prompt) + ]); + + return $result->toText(); + } catch (\Exception $e) { + $lastException = $e; + $this->logger->warning("AI generation failed, trying next provider", [ + 'provider' => $providerId, + 'error' => $e->getMessage() + ]); + } + } + + throw new \RuntimeException( + 'All AI providers failed. Last error: ' . ($lastException?->getMessage() ?? 'Unknown error') + ); + } + + private function getPreferredModel(string $providerId): string + { + return match($providerId) { + 'openai' => 'gpt-4o', + 'anthropic' => 'claude-3-5-sonnet', + 'google' => 'gemini-2.5-pro', + default => '' + }; + } +} + +// Usage +$haClient = new HighAvailabilityAIClient( + ['openai', 'anthropic', 'google'], // Provider priority order + AiClient::defaultRegistry(), + $logger +); + +$content = $haClient->generateWithFallback('Write about AI resilience'); +``` + +## Monitoring & Observability + +### Metrics Collection + +```php +metrics = $metrics; + $this->logger = $logger; + } + + public function trackGeneration(string $provider, string $model, float $duration, bool $success, array $metadata = []): void + { + // Increment counters + $this->metrics->increment('ai.generations.total', [ + 'provider' => $provider, + 'model' => $model, + 'status' => $success ? 'success' : 'failure' + ]); + + // Record timing + $this->metrics->histogram('ai.generation.duration', $duration, [ + 'provider' => $provider, + 'model' => $model + ]); + + // Track costs (if available) + if (isset($metadata['tokens'])) { + $this->metrics->histogram('ai.generation.tokens', $metadata['tokens'], [ + 'provider' => $provider, + 'model' => $model, + 'type' => $metadata['token_type'] ?? 'total' + ]); + } + + // Log detailed information + $this->logger->info('AI generation tracked', [ + 'provider' => $provider, + 'model' => $model, + 'duration_ms' => $duration * 1000, + 'success' => $success, + 'metadata' => $metadata + ]); + } + + public function trackError(string $provider, string $model, \Exception $error): void + { + $this->metrics->increment('ai.errors.total', [ + 'provider' => $provider, + 'model' => $model, + 'error_type' => get_class($error) + ]); + + $this->logger->error('AI generation error', [ + 'provider' => $provider, + 'model' => $model, + 'error' => $error->getMessage(), + 'trace' => $error->getTraceAsString() + ]); + } +} + +// Instrumented AI client +class InstrumentedAIClient +{ + private ProviderRegistry $registry; + private AIMetricsCollector $metrics; + + public function generateWithMetrics(string $prompt, array $options = []): string + { + $provider = $options['provider'] ?? 'auto'; + $model = $options['model'] ?? 'auto'; + $startTime = microtime(true); + + try { + $builder = AiClient::prompt($prompt); + + if (isset($options['model_instance'])) { + $builder->usingModel($options['model_instance']); + $provider = $options['model_instance']->metadata()->getProvider()->getId(); + $model = $options['model_instance']->metadata()->getId(); + } + + $result = $builder->generateTextResult(); + $duration = microtime(true) - $startTime; + + $this->metrics->trackGeneration($provider, $model, $duration, true, [ + 'tokens' => $result->getUsage()?->getTotalTokens() ?? 0, + 'prompt_length' => strlen($prompt) + ]); + + return $result->toText(); + } catch (\Exception $e) { + $duration = microtime(true) - $startTime; + $this->metrics->trackGeneration($provider, $model, $duration, false); + $this->metrics->trackError($provider, $model, $e); + throw $e; + } + } +} +``` + +### Health Checks + +```php + 'Generate a simple greeting', + 'image' => 'A small blue circle', + ]; + + public function checkProviderHealth(string $providerId): array + { + $results = [ + 'provider' => $providerId, + 'available' => false, + 'configured' => false, + 'models' => [], + 'latency' => null, + 'error' => null + ]; + + try { + // Check if provider is available and configured + $results['available'] = $this->registry->hasProvider($providerId); + $results['configured'] = $this->registry->isProviderConfigured($providerId); + + if (!$results['available'] || !$results['configured']) { + return $results; + } + + // Test text generation + $startTime = microtime(true); + $model = $this->registry->getProviderModel($providerId, $this->getTestModel($providerId)); + + $result = AiClient::prompt($this->testPrompts['text']) + ->usingModel($model) + ->usingMaxTokens(50) + ->generateTextResult(); + + $results['latency'] = microtime(true) - $startTime; + $results['models'][] = [ + 'id' => $model->metadata()->getId(), + 'type' => 'text', + 'status' => 'healthy', + 'response_length' => strlen($result->toText()) + ]; + + } catch (\Exception $e) { + $results['error'] = $e->getMessage(); + $results['latency'] = microtime(true) - ($startTime ?? microtime(true)); + } + + return $results; + } + + public function generateHealthReport(): array + { + $report = [ + 'timestamp' => date('c'), + 'overall_status' => 'healthy', + 'providers' => [] + ]; + + $availableProviders = ['openai', 'anthropic', 'google']; + $healthyCount = 0; + + foreach ($availableProviders as $providerId) { + $providerHealth = $this->checkProviderHealth($providerId); + $report['providers'][$providerId] = $providerHealth; + + if ($providerHealth['configured'] && !$providerHealth['error']) { + $healthyCount++; + } + } + + // Determine overall status + if ($healthyCount === 0) { + $report['overall_status'] = 'critical'; + } elseif ($healthyCount < count($availableProviders)) { + $report['overall_status'] = 'degraded'; + } + + return $report; + } + + private function getTestModel(string $providerId): string + { + return match($providerId) { + 'openai' => 'gpt-4o-mini', + 'anthropic' => 'claude-3-haiku', + 'google' => 'gemini-2.5-flash', + default => '' + }; + } +} + +// Health check endpoint +$healthChecker = new AIHealthChecker(); +$report = $healthChecker->generateHealthReport(); + +header('Content-Type: application/json'); +http_response_code($report['overall_status'] === 'healthy' ? 200 : 503); +echo json_encode($report, JSON_PRETTY_PRINT); +``` + +## Performance Optimization + +### Request Batching + +```php +registry = $registry; + $this->batchSize = $batchSize; + $this->rateLimitDelay = $rateLimitDelay; // milliseconds + } + + public function processBatch(array $prompts, array $options = []): array + { + $results = []; + $batches = array_chunk($prompts, $this->batchSize, true); + + foreach ($batches as $batchIndex => $batch) { + $batchResults = $this->processBatchChunk($batch, $options); + $results = array_merge($results, $batchResults); + + // Rate limiting between batches + if ($batchIndex < count($batches) - 1) { + usleep($this->rateLimitDelay * 1000); + } + } + + return $results; + } + + private function processBatchChunk(array $batch, array $options): array + { + $promises = []; + $results = []; + + // Create concurrent requests if HTTP client supports it + foreach ($batch as $key => $prompt) { + try { + $result = AiClient::prompt($prompt) + ->usingTemperature($options['temperature'] ?? 0.7) + ->usingMaxTokens($options['max_tokens'] ?? 500) + ->generateTextResult(); + + $results[$key] = [ + 'success' => true, + 'content' => $result->toText(), + 'usage' => $result->getUsage() + ]; + } catch (\Exception $e) { + $results[$key] = [ + 'success' => false, + 'error' => $e->getMessage(), + 'content' => null + ]; + } + } + + return $results; + } +} + +// Usage example +$batchProcessor = new BatchAIProcessor(AiClient::defaultRegistry(), 3, 500); + +$prompts = [ + 'product_1' => 'Write a description for wireless headphones', + 'product_2' => 'Write a description for laptop stand', + 'product_3' => 'Write a description for coffee mug', + 'product_4' => 'Write a description for desk lamp', +]; + +$results = $batchProcessor->processBatch($prompts, [ + 'temperature' => 0.8, + 'max_tokens' => 300 +]); + +foreach ($results as $key => $result) { + if ($result['success']) { + echo "✅ {$key}: {$result['content']}\n\n"; + } else { + echo "❌ {$key}: {$result['error']}\n\n"; + } +} +``` + +### Caching Strategies + +```php +cache = $cache; + $this->config = array_merge([ + 'default_ttl' => 3600, + 'max_prompt_length' => 1000, + 'cache_images' => true, + 'cache_audio' => false, + 'invalidation_patterns' => [] + ], $config); + } + + public function generateWithCache(string $prompt, array $options = []): string + { + // Skip caching for very long prompts or dynamic content + if (strlen($prompt) > $this->config['max_prompt_length'] || + $this->containsDynamicContent($prompt)) { + return $this->generateDirect($prompt, $options); + } + + $cacheKey = $this->generateCacheKey($prompt, $options); + + // Try to get from cache + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached; + } + + // Generate and cache + $result = $this->generateDirect($prompt, $options); + $ttl = $this->calculateTtl($prompt, $options); + $this->cache->set($cacheKey, $result, $ttl); + + return $result; + } + + private function generateDirect(string $prompt, array $options): string + { + $builder = AiClient::prompt($prompt); + + foreach ($options as $key => $value) { + match($key) { + 'temperature' => $builder->usingTemperature($value), + 'max_tokens' => $builder->usingMaxTokens($value), + 'model' => $builder->usingModel($value), + default => null + }; + } + + return $builder->generateTextResult()->toText(); + } + + private function generateCacheKey(string $prompt, array $options): string + { + $keyData = [ + 'prompt' => md5($prompt), + 'options' => $options, + 'version' => '1.0' + ]; + + return 'ai_content_' . md5(serialize($keyData)); + } + + private function containsDynamicContent(string $prompt): bool + { + $dynamicPatterns = [ + '/\btoday\b/i', + '/\bnow\b/i', + '/\bcurrent\b/i', + '/\d{4}-\d{2}-\d{2}/', // Date patterns + '/\{[^}]+\}/', // Template variables + ]; + + foreach ($dynamicPatterns as $pattern) { + if (preg_match($pattern, $prompt)) { + return true; + } + } + + return false; + } + + private function calculateTtl(string $prompt, array $options): int + { + // Longer TTL for stable content types + if (str_contains(strtolower($prompt), 'definition') || + str_contains(strtolower($prompt), 'explain')) { + return $this->config['default_ttl'] * 24; // 24 hours + } + + // Shorter TTL for creative content + if (isset($options['temperature']) && $options['temperature'] > 0.8) { + return $this->config['default_ttl'] / 2; // 30 minutes + } + + return $this->config['default_ttl']; + } + + public function invalidatePattern(string $pattern): int + { + // Implementation depends on cache backend + // This is a simplified example + $deletedCount = 0; + + // Redis example: + // $keys = $this->cache->keys("ai_content_*{$pattern}*"); + // $deletedCount = $this->cache->del($keys); + + return $deletedCount; + } +} +``` + +## Security Considerations + +### Input Sanitization + +```php +)<[^<]*)*<\/script>/mi', + '/javascript:/i' + ]; + + private int $maxPromptLength = 10000; + + public function secureGenerate(string $prompt, array $options = []): string + { + // Validate and sanitize prompt + $sanitizedPrompt = $this->sanitizePrompt($prompt); + + // Additional security checks + $this->validatePrompt($sanitizedPrompt); + + // Rate limiting by user/IP + $this->checkRateLimit($options['user_id'] ?? null); + + // Content filtering + $result = AiClient::prompt($sanitizedPrompt) + ->usingTemperature(min($options['temperature'] ?? 0.7, 0.9)) // Cap temperature + ->usingMaxTokens(min($options['max_tokens'] ?? 1000, 2000)) // Cap tokens + ->generateTextResult(); + + // Output filtering + return $this->filterOutput($result->toText()); + } + + private function sanitizePrompt(string $prompt): string + { + // Remove potential injection patterns + foreach ($this->blockedPatterns as $pattern) { + $prompt = preg_replace($pattern, '', $prompt); + } + + // Normalize whitespace + $prompt = preg_replace('/\s+/', ' ', trim($prompt)); + + // Truncate if too long + if (strlen($prompt) > $this->maxPromptLength) { + $prompt = substr($prompt, 0, $this->maxPromptLength) . '...'; + } + + return $prompt; + } + + private function validatePrompt(string $prompt): void + { + if (empty(trim($prompt))) { + throw new \InvalidArgumentException('Prompt cannot be empty'); + } + + // Check for suspicious patterns + foreach ($this->blockedPatterns as $pattern) { + if (preg_match($pattern, $prompt)) { + throw new \SecurityException('Prompt contains blocked content'); + } + } + } + + private function filterOutput(string $output): string + { + // Remove potentially sensitive information + $output = preg_replace('/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/', '[REDACTED]', $output); + $output = preg_replace('/\b[\w\.-]+@[\w\.-]+\.\w+\b/', '[EMAIL]', $output); + + return $output; + } + + private function checkRateLimit(?string $userId): void + { + // Implementation depends on rate limiting system + // This is a placeholder + if ($userId && $this->isRateLimited($userId)) { + throw new \Exception('Rate limit exceeded'); + } + } + + private function isRateLimited(string $userId): bool + { + // Check rate limiting logic + return false; + } +} +``` + +## Deployment Patterns + +### Docker Configuration + +```dockerfile +# Dockerfile for AI-powered PHP application +FROM php:8.2-fpm + +# Install required extensions +RUN apt-get update && apt-get install -y \ + git \ + zip \ + unzip \ + redis-tools \ + && docker-php-ext-install \ + bcmath \ + sockets + +# Install Composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Copy composer files +COPY composer.json composer.lock ./ + +# Install PHP dependencies +RUN composer install --no-dev --optimize-autoloader --no-scripts + +# Copy application code +COPY . . + +# Set permissions +RUN chown -R www-data:www-data /app/storage /app/cache + +# Environment configuration +ENV PHP_MEMORY_LIMIT=512M +ENV PHP_MAX_EXECUTION_TIME=300 +ENV AI_CACHE_DRIVER=redis +ENV AI_CACHE_TTL=3600 + +EXPOSE 9000 +CMD ["php-fpm"] +``` + +```yaml +# docker-compose.yml +version: '3.8' + +services: + app: + build: . + volumes: + - .:/app + environment: + - OPENAI_API_KEY=${OPENAI_API_KEY} + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - REDIS_HOST=redis + - DB_HOST=mysql + depends_on: + - redis + - mysql + networks: + - ai-app + + nginx: + image: nginx:alpine + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: + - app + networks: + - ai-app + + redis: + image: redis:alpine + volumes: + - redis_data:/data + networks: + - ai-app + + mysql: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + MYSQL_DATABASE: ${DB_NAME} + volumes: + - mysql_data:/var/lib/mysql + networks: + - ai-app + +volumes: + redis_data: + mysql_data: + +networks: + ai-app: + driver: bridge +``` + +## Resources & Further Reading + +### Official Documentation +- [PHP AI Client Architecture](ARCHITECTURE.md) +- [Provider Implementation Guide](https://github.com/WordPress/php-ai-client/wiki) +- [Model Capabilities Reference](https://github.com/WordPress/php-ai-client/wiki/capabilities) + + +### Community +- [GitHub Discussions](https://github.com/WordPress/php-ai-client/issues) +- [WordPress AI Team](https://make.wordpress.org/ai/) +- [Contributing Guidelines](https://github.com/WordPress/php-ai-client/blob/trunk/CONTRIBUTING.md) From 4d2cdcbb1d89e4a8d1149463a78c918e96882ab4 Mon Sep 17 00:00:00 2001 From: Mohamed Khaled Date: Fri, 29 Aug 2025 21:47:59 +0300 Subject: [PATCH 3/3] documentation: draft of documentation suite --- docs/1.intro.md | 163 +++--- docs/2.quick-start.md | 298 +--------- docs/3.php-applications.md | 624 ++------------------ docs/4.wordpress-integration.md | 787 ++----------------------- docs/5.advanced-usage.md | 983 ++------------------------------ 5 files changed, 224 insertions(+), 2631 deletions(-) diff --git a/docs/1.intro.md b/docs/1.intro.md index 922cb0a6..7f4a14ee 100644 --- a/docs/1.intro.md +++ b/docs/1.intro.md @@ -1,115 +1,112 @@ # 1. Introduction & Overview -**What is the PHP AI Client?** +**Welcome to the PHP AI Client SDK** - A comprehensive, production-ready library that brings the power of multiple AI providers to any PHP application. -The PHP AI Client is an intelligent, unified SDK that automatically selects the best AI model for your content generation needs. It seamlessly works with multiple providers and intelligently routes your requests to the most suitable model based on your prompt and desired output type. +## What is the PHP AI Client? -**Two Ways to Use It** +The PHP AI Client SDK is an intelligent, unified interface that automatically handles model selection, provider management, and content generation across multiple AI providers. Built with enterprise-grade reliability and WordPress integration in mind, it provides both modern fluent APIs and traditional function-based approaches. -## Standalone PHP Applications +**Key Features:** -Use it in any PHP project - Laravel, Symfony, custom applications: +- ** Multi-Provider Support**: OpenAI, Anthropic, Google AI with automatic failover +- ** Intelligent Model Selection**: Automatically chooses the best model for your content type +- ** Two API Styles**: Modern fluent API and WordPress-style traditional functions +- ** Multi-Modal Generation**: Text, images, speech, and audio generation +- **️ Enterprise Ready**: Full error handling, type safety, and production patterns +- ** WordPress Optimized**: Deep WordPress integration with hooks and filters +- ** Usage Analytics**: Built-in tracking and optimization suggestions -```php -toText(); -// Fluent API - modern chainable style -$result = AiClient::prompt('Write a product description for organic coffee') +// With configuration +$result = AiClient::prompt('Write a product description') ->usingTemperature(0.7) - ->usingMaxTokens(200) ->generateTextResult(); - -echo $result->toText(); ``` -## WordPress Integration - -Enhanced features for WordPress plugins and themes: +### WordPress Integration ```php -usingTemperature(0.6) - ->generateTextResult(); - - // Create WordPress post - wp_insert_post([ - 'post_title' => 'AI Generated: ' . $topic, - 'post_content' => $result->toText(), - 'post_status' => 'draft' - ]); - } -} +// WordPress plugin example +add_action('init', function() { + $content = AiClient::prompt('Generate blog post about sustainable energy') + ->generateTextResult() + ->toText(); +}); ``` -**What You Can Build** +## Content Types -### PHP Applications -- **Content Management Systems**: Auto-generate product descriptions, articles -- **Marketing Tools**: Create social media posts, email campaigns -- **Documentation**: Generate API docs, user guides -- **E-commerce**: Product descriptions, customer service responses -- **Data Analysis**: Generate reports and insights - -### WordPress Solutions -- **Content Plugins**: Auto-generate posts, pages, product descriptions -- **Theme Features**: Dynamic content based on user input -- **Admin Tools**: Bulk content generation for sites -- **User Features**: Let visitors generate custom content -- **SEO Tools**: Meta descriptions, alt text generation - -**Supported AI Providers** - -The client works with multiple AI providers through a unified interface: +```php +// Text generation +$text = AiClient::prompt('Write about sustainable energy')->generateTextResult(); -- **OpenAI**: GPT-5, GPT-4o, o4-mini, GPT-image-1, DALL-E 3, Whisper, Sora -- **Anthropic**: Claude Opus 4.1, Claude Sonnet 4, Claude 3.5 Sonnet, Claude 3.5 Haiku -- **Google AI**: Gemini 2.5 Pro, Gemini 2.5 Flash, Imagen 4, Veo 3 +// Image generation +$image = AiClient::prompt('Mountain landscape')->generateImageResult(); +// Text-to-speech +$audio = AiClient::convertTextToSpeechResult('Hello world'); +``` +## What You Can Build -**Two API Styles** +### PHP Applications +- **E-commerce Platforms**: Product descriptions, customer service, recommendations +- **SaaS Applications**: Content generation, user onboarding, feature explanations +- **Analytics Tools**: Report generation, insight summaries, trend analysis +- **Educational Platforms**: Course content, quizzes, personalized learning +- **Content Management**: Automated publishing, social media, email campaigns -Choose the style that fits your development preferences: +### WordPress Solutions +- **Content Plugins**: Auto-generate posts, pages, product descriptions +- **Theme Features**: Dynamic content based on user preferences +- **Admin Tools**: Bulk content generation, SEO optimization +- **User Features**: Custom content creation for site visitors +- **Marketing Tools**: Meta descriptions, social media posts, ad copy -**Traditional API** (WordPress-style): -```php -$result = AiClient::generateTextResult($prompt, $config); -$image = AiClient::generateImageResult($prompt); -$audio = AiClient::convertTextToSpeechResult($text); -``` +## Key Features -**Fluent API** (modern chainable): -```php -$result = AiClient::prompt($prompt) - ->usingTemperature(0.7) - ->usingMaxTokens(500) - ->generateTextResult(); -``` +- **Automatic Routing**: Content types are automatically routed to appropriate AI services +- **Provider Failover**: Built-in reliability with automatic provider switching +- **Error Handling**: Comprehensive exception handling for production use +- **Performance**: Built-in caching and batch processing capabilities -**Key Benefits** +## Getting Started -- **Intelligent Model Selection**: Automatically chooses the best available model for your specific task -- **Provider Agnostic**: Switch between AI providers without changing code -- **Type Safe**: Full PHP type safety with IDE autocompletion -- **Multi-Modal**: Seamlessly handle text, images, and audio generation -- **Production Ready**: Comprehensive error handling and validation -- **Flexible**: Use in any PHP application or WordPress site -- **WordPress Optimized**: Special features for WordPress developers +Choose your path based on your needs: -## Choose Your Path +- ** [Quick Start](2.quick-start.md)** - Get up and running in 5 minutes +- **️ [PHP Applications](3.php-applications.md)** - Framework integration patterns +- ** [WordPress Integration](4.wordpress-integration.md)** - WordPress-specific features +- ** [Advanced Usage](5.advanced-usage.md)** - Production deployment, custom providers, optimization -- **[Quick Start](2.quick-start.md)** - Get working examples for both PHP and WordPress -- **[PHP Applications](3.php-applications.md)** - Framework-agnostic PHP usage -- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific features -- **[Advanced Usage](5.advanced-usage.md)** - Providers, customization, production patterns diff --git a/docs/2.quick-start.md b/docs/2.quick-start.md index 040140ce..f0a5567b 100644 --- a/docs/2.quick-start.md +++ b/docs/2.quick-start.md @@ -1,306 +1,60 @@ -# 2. Quick Start +# 2. Quick Start Guide -Get up and running with the PHP AI Client in minutes. Choose your path: - -## PHP Applications - -### Installation +## Installation ```bash composer require wordpress/php-ai-client ``` -### Smart Model Selection - -The SDK automatically chooses the best available model for your task: +## Basic Usage ```php -toText(); -// Auto-discovery with preferences -$result = AiClient::prompt('Explain machine learning to a 5-year-old') +// With configuration +$result = AiClient::prompt('Write a product description') ->usingTemperature(0.7) - ->usingMaxTokens(200) - ->generateTextResult(); // SDK selects optimal model based on prompt - -echo $result->toText(); - -// Different content types are automatically routed to appropriate models -$image = AiClient::prompt('A futuristic robot teaching children') - ->generateImageResult(); // Auto-selects best image model - -$audio = AiClient::convertTextToSpeechResult('Hello world'); -// Auto-selects best speech synthesis model -``` - -### Working with Providers - -```php -registerProvider(OpenAiProvider::class); -$registry->setProviderRequestAuthentication( - 'openai', - new ApiKeyRequestAuthentication($_ENV['OPENAI_API_KEY']) -); - -// Use specific model -$model = $registry->getProviderModel('openai', 'gpt-4o'); -$result = AiClient::prompt('Generate product descriptions for coffee beans') - ->usingModel($model) - ->usingTemperature(0.8) ->generateTextResult(); -echo $result->toText(); +// Image generation +$image = AiClient::generateImageResult('Mountain landscape'); +echo $image->toImageFile()->getUrl(); ``` - -### Laravel Integration +## Provider Authentication ```php - [ - 'key' => env('OPENAI_API_KEY'), - ], - 'anthropic' => [ - 'key' => env('ANTHROPIC_API_KEY'), - ], -]; -``` - -```php -registry = AiClient::defaultRegistry(); - - // Register providers - $this->registry->registerProvider(OpenAiProvider::class); - $this->registry->setProviderRequestAuthentication( - 'openai', - new ApiKeyRequestAuthentication(config('services.openai.key')) - ); - } +// Set up API keys +$registry = AiClient::defaultRegistry(); +$registry->setProviderRequestAuthentication('openai', $_ENV['OPENAI_API_KEY']); +$registry->setProviderRequestAuthentication('anthropic', $_ENV['ANTHROPIC_API_KEY']); - public function generateContent(string $prompt, array $config = []): string - { - $builder = AiClient::prompt($prompt); - - if (isset($config['temperature'])) { - $builder->usingTemperature($config['temperature']); - } - - if (isset($config['max_tokens'])) { - $builder->usingMaxTokens($config['max_tokens']); - } - - return $builder->generateTextResult()->toText(); - } -} +// Use the SDK +$result = AiClient::prompt('Write a product description')->generateTextResult(); ``` ## WordPress Integration -### Plugin Setup - ```php -'; - echo '

This plugin requires the PHP AI Client library.

'; - echo ''; - }); - return; -} - -class YourAiPlugin { - public function __construct() { - add_action('init', [$this, 'setupProviders']); - add_action('wp_ajax_generate_content', [$this, 'handleAjaxGeneration']); - } - - public function setupProviders() { - $registry = WordPress\AiClient\AiClient::defaultRegistry(); - - // Setup OpenAI if key exists - $openaiKey = get_option('your_plugin_openai_key'); - if (!empty($openaiKey)) { - $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class); - $registry->setProviderRequestAuthentication( - 'openai', - new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($openaiKey) - ); - } - } - - public function handleAjaxGeneration() { - check_ajax_referer('generate_content'); - - $prompt = sanitize_text_field($_POST['prompt']); - - try { - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature(0.7) - ->generateTextResult(); - - wp_send_json_success(['content' => $result->toText()]); - } catch (Exception $e) { - wp_send_json_error(['message' => 'Generation failed']); - } - } -} - -new YourAiPlugin(); -``` - -### Theme Integration - -```php -post_title - ) - ->usingTemperature(0.6) - ->usingMaxTokens(100) - ->generateTextResult(); - - return $result->toText(); - } catch (Exception $e) { - return get_the_excerpt($post_id); - } -} - -// Use in templates -// {{ generate_post_excerpt(get_the_ID()) }} -``` - -### WordPress CLI Integration - -```php -usingTemperature(0.7) - ->generateTextResult(); - - WP_CLI::success($result->toText()); - } catch (Exception $e) { - WP_CLI::error('Generation failed: ' . $e->getMessage()); - } - }); -} - -// Usage: wp ai generate "Write about WordPress security" -``` - -## Multi-Modal Examples - -### Image Generation - -```php -generateImageResult(); - -echo "Generated image URL: " . $image->toImageFile()->getUrl(); -``` - -### Text-to-Speech - -```php -toAudioFile()->getPath(); +// WordPress plugin +add_action('init', function() { + $content = AiClient::prompt('Generate blog content') + ->generateTextResult() + ->toText(); +}); ``` ## Error Handling ```php -usingTemperature(0.7) - ->generateTextResult(); - + $result = AiClient::prompt('Generate content')->generateTextResult(); echo $result->toText(); -} catch (AiClientException $e) { - // Handle AI-specific errors - error_log('AI generation failed: ' . $e->getMessage()); - echo 'Content generation is temporarily unavailable.'; } catch (Exception $e) { - // Handle general errors - error_log('Unexpected error: ' . $e->getMessage()); - echo 'An error occurred.'; -} -``` - -## Next Steps - -- **[PHP Applications](3.php-applications.md)** - Deep dive into framework integration patterns -- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific features and patterns -- **[Advanced Usage](5.advanced-usage.md)** - Custom providers, production deployment, and optimization - -## Need Help? - -- Check [GitHub Issues](https://github.com/WordPress/php-ai-client/issues) for common problems -- Review the [Architecture documentation](ARCHITECTURE.md) for technical details -- Look at example implementations in the `examples/` directory "Coming Soon" + error_log('AI generation failed: ' . $e->getMessage()); +} \ No newline at end of file diff --git a/docs/3.php-applications.md b/docs/3.php-applications.md index 6b09ccdd..81c5f868 100644 --- a/docs/3.php-applications.md +++ b/docs/3.php-applications.md @@ -1,610 +1,66 @@ # 3. PHP Applications -The PHP AI Client works in any PHP environment. This guide covers framework integrations, production patterns, and standalone usage. - -## Framework Integration - -### Laravel - -#### Service Provider - -```php -app->singleton('ai-client', function () { - $registry = AiClient::defaultRegistry(); - - // Register providers based on config - if (config('ai.openai.enabled')) { - $registry->registerProvider(OpenAiProvider::class); - $registry->setProviderRequestAuthentication( - 'openai', - new ApiKeyRequestAuthentication(config('ai.openai.key')) - ); - } - - if (config('ai.anthropic.enabled')) { - $registry->registerProvider(AnthropicProvider::class); - $registry->setProviderRequestAuthentication( - 'anthropic', - new AnthropicApiKeyRequestAuthentication(config('ai.anthropic.key')) - ); - } - - return $registry; - }); - } -} -``` - -#### Configuration +## Laravel Integration ```php - env('AI_DEFAULT_PROVIDER', 'openai'), - - 'openai' => [ - 'enabled' => env('AI_OPENAI_ENABLED', true), - 'key' => env('OPENAI_API_KEY'), - 'default_model' => env('AI_OPENAI_MODEL', 'gpt-4o'), - ], - - 'anthropic' => [ - 'enabled' => env('AI_ANTHROPIC_ENABLED', false), - 'key' => env('ANTHROPIC_API_KEY'), - 'default_model' => env('AI_ANTHROPIC_MODEL', 'claude-3-5-sonnet'), - ], - - 'generation' => [ - 'temperature' => env('AI_TEMPERATURE', 0.7), - 'max_tokens' => env('AI_MAX_TOKENS', 1000), - 'timeout' => env('AI_TIMEOUT', 30), + 'ai' => [ + 'openai_key' => env('OPENAI_API_KEY'), + 'anthropic_key' => env('ANTHROPIC_API_KEY'), ], ]; -``` - -#### Service Class -```php -registry = $registry; - } - - public function generateContent(string $prompt, array $options = []): string - { - try { - $builder = AiClient::prompt($prompt) - ->usingTemperature($options['temperature'] ?? config('ai.generation.temperature')) - ->usingMaxTokens($options['max_tokens'] ?? config('ai.generation.max_tokens')); - - if (isset($options['provider']) && isset($options['model'])) { - $model = $this->registry->getProviderModel($options['provider'], $options['model']); - $builder->usingModel($model); - } - - return $builder->generateTextResult()->toText(); - } catch (\Exception $e) { - throw new ContentGenerationException('Failed to generate content: ' . $e->getMessage()); - } - } - - public function generateImage(string $prompt, array $options = []): string - { - try { - $builder = AiClient::prompt($prompt); - - if (isset($options['provider']) && isset($options['model'])) { - $model = $this->registry->getProviderModel($options['provider'], $options['model']); - $builder->usingModel($model); - } - - $image = $builder->generateImageResult(); - return $image->toImageFile()->getUrl(); - } catch (\Exception $e) { - throw new ContentGenerationException('Failed to generate image: ' . $e->getMessage()); - } +// app/Services/AiService.php +class AiService { + public function generateText(string $prompt): string { + return AiClient::prompt($prompt)->generateTextResult()->toText(); } } ``` -### Symfony - -#### Service Configuration - -```yaml -# config/services.yaml +## Symfony Integration +```php +// services.yaml services: - WordPress\AiClient\Providers\Registry\ProviderRegistry: - factory: ['WordPress\AiClient\AiClient', 'defaultRegistry'] - calls: - - [registerProvider, ['WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider']] - - [setProviderRequestAuthentication, ['openai', '@ai.openai_auth']] - - ai.openai_auth: - class: WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication - arguments: ['%env(OPENAI_API_KEY)%'] - - App\Service\ContentGenerator: + app.ai_service: + class: App\Service\AiService arguments: - - '@WordPress\AiClient\Providers\Registry\ProviderRegistry' -``` - -#### Service Implementation - -```php -registry = $registry; - } - - public function generateArticle(string $topic, string $tone = 'professional'): array - { - $prompts = [ - 'title' => "Generate an engaging title for an article about: {$topic}", - 'intro' => "Write a compelling introduction for an article titled about {$topic}. Tone: {$tone}", - 'conclusion' => "Write a strong conclusion for an article about {$topic}. Tone: {$tone}", - ]; - - $results = []; - foreach ($prompts as $section => $prompt) { - $results[$section] = AiClient::prompt($prompt) - ->usingTemperature(0.8) - ->usingMaxTokens(200) - ->generateTextResult() - ->toText(); - } - - return $results; - } -} -``` - -## Production Patterns - -### Connection Pool Management - -```php -registry = AiClient::defaultRegistry(); - - // Configure HTTP client with connection pooling - $httpClient = HttpTransporterFactory::createTransporter([ - 'pool_size' => 10, - 'timeout' => 30, - 'retry_attempts' => 3, - ]); - - $this->registry->setHttpTransporter($httpClient); - $this->setupProviders(); - } - - public static function getInstance(): self - { - if (self::$instance === null) { - self::$instance = new self(); - } - return self::$instance; - } - - private function setupProviders(): void - { - $providers = [ - 'openai' => [ - 'class' => \WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class, - 'auth' => new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication( - $_ENV['OPENAI_API_KEY'] ?? '' - ), - ], - 'anthropic' => [ - 'class' => \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class, - 'auth' => new \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicApiKeyRequestAuthentication( - $_ENV['ANTHROPIC_API_KEY'] ?? '' - ), - ], - ]; - - foreach ($providers as $id => $config) { - if (!empty($config['auth'])) { - $this->registry->registerProvider($config['class']); - $this->registry->setProviderRequestAuthentication($id, $config['auth']); - } - } - } - - public function getRegistry() - { - return $this->registry; - } -} -``` - -### Caching Layer - -```php -cache = $cache; - $this->registry = $registry; - } - - public function generateWithCache(string $prompt, array $options = [], int $ttl = 3600): string - { - $cacheKey = $this->generateCacheKey($prompt, $options); - - // Check cache first - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return $cached; - } - - // Generate content - try { - $builder = AiClient::prompt($prompt) - ->usingTemperature($options['temperature'] ?? 0.7) - ->usingMaxTokens($options['max_tokens'] ?? 1000); - - if (isset($options['model'])) { - $model = $this->registry->getProviderModel($options['provider'] ?? 'openai', $options['model']); - $builder->usingModel($model); - } - - $result = $builder->generateTextResult()->toText(); - - // Cache the result - $this->cache->set($cacheKey, $result, $ttl); - - return $result; - } catch (\Exception $e) { - throw new \RuntimeException('Content generation failed: ' . $e->getMessage()); - } - } - - private function generateCacheKey(string $prompt, array $options): string - { - return 'ai_content_' . md5($prompt . serialize($options)); - } -} -``` - -### Rate Limiting - -```php -rateLimiter = $rateLimiter; - $this->registry = $registry; - } - - public function generateWithRateLimit(string $prompt, string $userId, array $options = []): string - { - $key = "ai_generation_{$userId}"; - - if (!$this->rateLimiter->attempt($key, 10, 60)) { // 10 requests per minute - throw new \Exception('Rate limit exceeded. Please wait before making another request.'); - } - - return AiClient::prompt($prompt) - ->usingTemperature($options['temperature'] ?? 0.7) - ->generateTextResult() - ->toText(); - } -} -``` - -## Error Handling & Monitoring - -### Comprehensive Error Handler - -```php -logger = $logger; - $this->metrics = $metrics; +// src/Service/AiService.php +class AiService { + public function __construct(private string $openaiKey) { + // Setup AI client } - - public function handleGeneration(callable $generationCallback, array $fallbackOptions = []): string - { - $attempts = 0; - $maxAttempts = $fallbackOptions['max_attempts'] ?? 3; - - while ($attempts < $maxAttempts) { - try { - $result = $generationCallback(); - $this->metrics->increment('ai.generation.success'); - return $result; - } catch (RateLimitException $e) { - $this->logger->warning('AI rate limit hit', ['attempt' => $attempts + 1]); - $this->metrics->increment('ai.generation.rate_limited'); - - if ($attempts < $maxAttempts - 1) { - sleep(pow(2, $attempts)); // Exponential backoff - } - } catch (AuthenticationException $e) { - $this->logger->error('AI authentication failed', ['error' => $e->getMessage()]); - $this->metrics->increment('ai.generation.auth_failed'); - throw $e; // Don't retry auth errors - } catch (AiClientException $e) { - $this->logger->error('AI client error', [ - 'error' => $e->getMessage(), - 'attempt' => $attempts + 1 - ]); - $this->metrics->increment('ai.generation.client_error'); - } catch (\Exception $e) { - $this->logger->error('Unexpected AI error', [ - 'error' => $e->getMessage(), - 'attempt' => $attempts + 1 - ]); - $this->metrics->increment('ai.generation.unexpected_error'); - } - - $attempts++; - } - - // All attempts failed, return fallback or throw - if (isset($fallbackOptions['fallback_text'])) { - $this->metrics->increment('ai.generation.fallback_used'); - return $fallbackOptions['fallback_text']; - } - - throw new \RuntimeException('AI generation failed after ' . $maxAttempts . ' attempts'); - } -} -``` - -## Background Processing - -### Queue Integration - -```php -registry = $registry; - } - - public function handle(array $jobData): void - { - try { - $result = AiClient::prompt($jobData['prompt']) - ->usingTemperature($jobData['temperature'] ?? 0.7) - ->usingMaxTokens($jobData['max_tokens'] ?? 1000) - ->generateTextResult(); - - // Store result or trigger callback - $this->storeResult($jobData['job_id'], $result->toText()); - - if (isset($jobData['callback_url'])) { - $this->triggerCallback($jobData['callback_url'], $result->toText()); - } - } catch (\Exception $e) { - $this->handleJobFailure($jobData['job_id'], $e->getMessage()); - } - } - - private function storeResult(string $jobId, string $content): void - { - // Store in database or cache - } - - private function triggerCallback(string $url, string $content): void - { - // Make HTTP callback - } - - private function handleJobFailure(string $jobId, string $error): void - { - // Log error and update job status + + public function generate(string $prompt): string { + return AiClient::prompt($prompt)->generateTextResult()->toText(); } } ``` -## Testing - -### Mock Provider for Testing +## Production Considerations ```php -responses[$prompt] = $response; - } - - public static function metadata(): ProviderMetadata - { - return new ProviderMetadata( - id: 'mock', - name: 'Mock Provider', - type: ProviderTypeEnum::TESTING - ); - } - - public static function model(string $modelId, ?ModelConfig $modelConfig = null): ModelInterface - { - return new MockModel($this->responses); - } - - // ... other required methods +// Rate limiting +$result = AiClient::prompt($prompt) + ->usingMaxTokens(500) + ->generateTextResult(); + +// Error handling +try { + $result = AiClient::prompt($prompt)->generateTextResult(); +} catch (AiClientException $e) { + // Handle AI-specific errors + return 'Content generation unavailable'; } -// Usage in tests -class ContentGeneratorTest extends TestCase -{ - public function testContentGeneration(): void - { - $mockProvider = new MockAiProvider(); - $mockProvider->addMockResponse( - 'Generate a product description', - 'This is a mock product description' - ); - - $registry = AiClient::defaultRegistry(); - $registry->registerProvider(MockAiProvider::class); - - $result = AiClient::prompt('Generate a product description') - ->usingModel($registry->getProviderModel('mock', 'test')) - ->generateTextResult(); - - $this->assertEquals('This is a mock product description', $result->toText()); - } +// Caching +$key = 'ai_' . md5($prompt); +if (!$cached = cache()->get($key)) { + $cached = AiClient::prompt($prompt)->generateTextResult()->toText(); + cache()->put($key, $cached, 3600); } -``` - -## Performance Optimization - -### Batch Processing - -```php -registry = $registry; - } - - public function generateBatch(array $prompts, array $options = []): array - { - $results = []; - $chunks = array_chunk($prompts, $options['batch_size'] ?? 5); - - foreach ($chunks as $chunk) { - $promises = []; - - // Create async requests - foreach ($chunk as $index => $prompt) { - $promises[$index] = $this->generateAsync($prompt, $options); - } - - // Wait for all requests in chunk to complete - $chunkResults = $this->resolvePromises($promises); - $results = array_merge($results, $chunkResults); - - // Rate limiting between chunks - if (isset($options['delay_between_chunks'])) { - sleep($options['delay_between_chunks']); - } - } - - return $results; - } - - private function generateAsync(string $prompt, array $options): Promise - { - // Implementation depends on your HTTP client's async capabilities - } - - private function resolvePromises(array $promises): array - { - // Wait for and resolve all promises - } -} -``` - -## Next Steps - -- **[WordPress Integration](4.wordpress-integration.md)** - WordPress-specific patterns and hooks -- **[Advanced Usage](5.advanced-usage.md)** - Custom providers and advanced configuration - -## Useful Resources - -- [PSR-18 HTTP Client](https://www.php-fig.org/psr/psr-18/) for HTTP abstraction -- [Guzzle HTTP](https://docs.guzzlephp.org/) for advanced HTTP features -- [PHP-FIG Standards](https://www.php-fig.org/psr/) for coding standards -- [Composer Documentation](https://getcomposer.org/doc/) for dependency management \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/4.wordpress-integration.md b/docs/4.wordpress-integration.md index 073ae991..133a39d4 100644 --- a/docs/4.wordpress-integration.md +++ b/docs/4.wordpress-integration.md @@ -1,786 +1,77 @@ # 4. WordPress Integration -The PHP AI Client provides enhanced WordPress integration patterns, hooks, and features specifically designed for WordPress plugins and themes. - -## WordPress-Specific Features - -### Automatic Provider Discovery - -WordPress plugins can automatically discover and configure AI providers: - -```php -toText(), 'ai_client', HOUR_IN_SECONDS); - - // Update usage statistics - $count = get_option('ai_generations_count', 0); - update_option('ai_generations_count', $count + 1); - } - - public function customProviderSelection($defaultProvider, $prompt) { - // Use different providers based on content type - if (strpos($prompt, 'image') !== false) { - return 'openai'; // Prefer OpenAI for image generation - } - - if (strlen($prompt) > 1000) { - return 'anthropic'; // Use Claude for long content - } - - return $defaultProvider; - } -} - -new WpAiContentPlugin(); -``` - -## Plugin Development Patterns - -### Complete Plugin Example +## Basic WordPress Plugin ```php

'; - echo esc_html__('AI Content Generator requires the PHP AI Client library.', 'ai-content'); - echo '

'; - }); - return; -} - -class AiContentGenerator { - const OPTION_PREFIX = 'ai_content_'; - +class AiContentPlugin { public function __construct() { add_action('init', [$this, 'init']); - add_action('admin_menu', [$this, 'addAdminMenu']); - add_action('wp_ajax_ai_generate_content', [$this, 'ajaxGenerateContent']); - add_action('add_meta_boxes', [$this, 'addMetaBoxes']); - - // WordPress AI Client hooks - add_filter('wp_ai_client_default_temperature', [$this, 'getDefaultTemperature']); - add_filter('wp_ai_client_max_tokens', [$this, 'getMaxTokens']); + add_shortcode('ai_generate', [$this, 'shortcode']); } public function init() { - // Load text domain - load_plugin_textdomain('ai-content', false, dirname(plugin_basename(__FILE__)) . '/languages'); - - // Setup AI providers with WordPress options - $this->setupAiProviders(); - } - - private function setupAiProviders() { - $registry = WordPress\AiClient\AiClient::defaultRegistry(); - - // OpenAI setup - $openaiKey = get_option(self::OPTION_PREFIX . 'openai_key'); - if (!empty($openaiKey)) { - $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class); - $registry->setProviderRequestAuthentication( - 'openai', - new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($openaiKey) - ); - } - - // Anthropic setup - $anthropicKey = get_option(self::OPTION_PREFIX . 'anthropic_key'); - if (!empty($anthropicKey)) { - $registry->registerProvider(\WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class); - $registry->setProviderRequestAuthentication( - 'anthropic', - new \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicApiKeyRequestAuthentication($anthropicKey) - ); - } - } - - public function addAdminMenu() { - add_options_page( - __('AI Content Settings', 'ai-content'), - __('AI Content', 'ai-content'), - 'manage_options', - 'ai-content-settings', - [$this, 'renderSettingsPage'] - ); - - add_menu_page( - __('AI Generator', 'ai-content'), - __('AI Generator', 'ai-content'), - 'edit_posts', - 'ai-generator', - [$this, 'renderGeneratorPage'], - 'dashicons-robot', - 30 - ); - } - - public function renderSettingsPage() { - if (isset($_POST['submit'])) { - $this->saveSettings(); - echo '

' . esc_html__('Settings saved!', 'ai-content') . '

'; - } - - $openaiKey = get_option(self::OPTION_PREFIX . 'openai_key', ''); - $anthropicKey = get_option(self::OPTION_PREFIX . 'anthropic_key', ''); - $temperature = get_option(self::OPTION_PREFIX . 'temperature', 0.7); - - ?> -
-

-
- - - - - - - - - - - - - - -
- -

-
- -

-
- -

-
- -
-
- -
-

- -
- -

- - -
- - -
- - - 'Write helpful content'], $atts); - if (!current_user_can('edit_posts')) { - wp_send_json_error(['message' => __('Permission denied', 'ai-content')]); - } - - $prompt = sanitize_text_field($_POST['prompt'] ?? ''); - $type = sanitize_text_field($_POST['type'] ?? 'text'); - - if (empty($prompt)) { - wp_send_json_error(['message' => __('Prompt is required', 'ai-content')]); - } - - try { - if ($type === 'image') { - $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); - wp_send_json_success(['url' => $result->toImageFile()->getUrl()]); - } else { - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature(floatval(get_option(self::OPTION_PREFIX . 'temperature', 0.7))) - ->generateTextResult(); - wp_send_json_success(['content' => $result->toText()]); - } - } catch (Exception $e) { - wp_send_json_error(['message' => $e->getMessage()]); - } - } - - public function addMetaBoxes() { - add_meta_box( - 'ai-content-generator', - __('AI Content Generator', 'ai-content'), - [$this, 'renderMetaBox'], - ['post', 'page'], - 'side' - ); - } - - public function renderMetaBox($post) { - ?> -
-

- - -

-

- - -

-
- - - generateTextResult(); + return wp_kses_post($result->toText()); } } -// Initialize plugin -new AiContentGenerator(); +new AiContentPlugin(); ``` -## Theme Integration - -### Theme Functions +## WordPress Hooks ```php -post_title; - if (!empty($post->post_content)) { - $prompt .= "\n\nContent preview: " . wp_trim_words($post->post_content, 50); - } - - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature(0.6) - ->usingMaxTokens($length * 2) // Rough token estimation - ->generateTextResult(); - - $excerpt = $result->toText(); - - // Cache for 1 hour - set_transient('ai_excerpt_' . $post_id, $excerpt, HOUR_IN_SECONDS); - - return $excerpt; - } catch (Exception $e) { - // Fallback to regular excerpt - return get_the_excerpt($post_id); - } -} - -// Generate SEO meta descriptions -function generate_ai_meta_description($post_id) { - $post = get_post($post_id); - - try { - $prompt = "Write a SEO-optimized meta description (150 characters max) for: " . $post->post_title; - - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature(0.5) - ->usingMaxTokens(50) - ->generateTextResult(); - - return substr($result->toText(), 0, 150); - } catch (Exception $e) { - return wp_trim_words($post->post_content, 20); - } -} - -// Hook into WordPress SEO plugins -add_filter('wpseo_metadesc', function($metadesc, $post_id) { - if (empty($metadesc)) { - return generate_ai_meta_description($post_id); - } - return $metadesc; -}, 10, 2); +// Modify content before generation +add_filter('wp_ai_content_prompt', function($prompt) { + return "WordPress site context: " . $prompt; +}); + +// Process generated content +add_filter('wp_ai_generated_content', function($content) { + return wpautop($content); +}); ``` -### Block Editor Integration +## Admin Integration ```php - 'ai-content-block', - 'render_callback' => 'render_ai_content_block' - ]); -} - -function render_ai_content_block($attributes) { - $prompt = $attributes['prompt'] ?? ''; - - if (empty($prompt)) { - return '

Please enter a prompt in the block editor.

'; - } - - try { - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature(0.7) - ->generateTextResult(); - - return '
' . wp_kses_post($result->toText()) . '
'; - } catch (Exception $e) { - return '

Content generation failed. Please try again later.

'; - } -} -``` - -## REST API Extensions +}); -### Custom Endpoints - -```php - 'POST', - 'callback' => 'ai_rest_generate_content', - 'permission_callback' => function() { - return current_user_can('edit_posts'); - }, - 'args' => [ - 'prompt' => [ - 'required' => true, - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field' - ], - 'type' => [ - 'default' => 'text', - 'enum' => ['text', 'image', 'audio'] - ], - 'temperature' => [ - 'default' => 0.7, - 'type' => 'number', - 'minimum' => 0, - 'maximum' => 1 - ] - ] - ]); -} - -function ai_rest_generate_content($request) { - $prompt = $request['prompt']; - $type = $request['type']; - $temperature = $request['temperature']; - - try { - switch ($type) { - case 'image': - $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); - return rest_ensure_response([ - 'success' => true, - 'data' => [ - 'type' => 'image', - 'url' => $result->toImageFile()->getUrl(), - 'prompt' => $prompt - ] - ]); - - case 'audio': - $result = WordPress\AiClient\AiClient::convertTextToSpeechResult($prompt); - return rest_ensure_response([ - 'success' => true, - 'data' => [ - 'type' => 'audio', - 'url' => $result->toAudioFile()->getPath(), - 'prompt' => $prompt - ] - ]); - - default: // text - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature($temperature) - ->generateTextResult(); - - return rest_ensure_response([ - 'success' => true, - 'data' => [ - 'type' => 'text', - 'content' => $result->toText(), - 'prompt' => $prompt - ] - ]); - } - } catch (Exception $e) { - return new WP_Error('generation_failed', $e->getMessage(), ['status' => 500]); - } +function ai_settings_page() { + // API key settings form } ``` -## WP-CLI Integration +## WP-CLI Commands ```php - - * : The content prompt - * - * [--type=] - * : Content type (text, image, audio) - * --- - * default: text - * --- - * - * [--temperature=] - * : Temperature (0-1) - * --- - * default: 0.7 - * --- - * - * ## EXAMPLES - * - * wp ai generate "Write about WordPress" - * wp ai generate "A sunset landscape" --type=image - * - */ - public function generate($args, $assoc_args) { - $prompt = $args[0]; - $type = $assoc_args['type'] ?? 'text'; - $temperature = floatval($assoc_args['temperature'] ?? 0.7); - - WP_CLI::log("Generating {$type} content..."); - - try { - switch ($type) { - case 'image': - $result = WordPress\AiClient\AiClient::prompt($prompt)->generateImageResult(); - WP_CLI::success("Image generated: " . $result->toImageFile()->getUrl()); - break; - - case 'audio': - $result = WordPress\AiClient\AiClient::convertTextToSpeechResult($prompt); - WP_CLI::success("Audio generated: " . $result->toAudioFile()->getPath()); - break; - - default: - $result = WordPress\AiClient\AiClient::prompt($prompt) - ->usingTemperature($temperature) - ->generateTextResult(); - WP_CLI::success("Generated content:\n" . $result->toText()); - break; - } - } catch (Exception $e) { - WP_CLI::error("Generation failed: " . $e->getMessage()); - } - } - - /** - * Bulk generate posts with AI - * - * ## OPTIONS - * - * - * : File containing post topics (one per line) - * - * [--status=] - * : Post status - * --- - * default: draft - * --- - * - */ - public function bulk_posts($args, $assoc_args) { - $file = $args[0]; - $status = $assoc_args['status'] ?? 'draft'; - - if (!file_exists($file)) { - WP_CLI::error("File not found: {$file}"); - } - - $topics = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $progress = WP_CLI\Utils\make_progress_bar('Generating posts', count($topics)); - - foreach ($topics as $topic) { - try { - $content = WordPress\AiClient\AiClient::prompt("Write a detailed blog post about: {$topic}") - ->usingTemperature(0.7) - ->usingMaxTokens(1500) - ->generateTextResult(); - - wp_insert_post([ - 'post_title' => trim($topic), - 'post_content' => $content->toText(), - 'post_status' => $status, - 'post_type' => 'post' - ]); - - $progress->tick(); - } catch (Exception $e) { - WP_CLI::warning("Failed to generate post for: {$topic}"); - $progress->tick(); - } - } - - $progress->finish(); - WP_CLI::success("Bulk post generation complete!"); - } -} -``` - -## Performance & Caching - -### WordPress Object Cache Integration - -```php -usingTemperature($options['temperature']); - } - - if (isset($options['max_tokens'])) { - $builder->usingMaxTokens($options['max_tokens']); - } - - $result = $builder->generateTextResult()->toText(); - - self::set($cache_key, $result, $ttl); - - return $result; - } catch (Exception $e) { - // Cache errors for short time to avoid spam - self::set($cache_key . '_error', $e->getMessage(), 300); - throw $e; - } - } - - private static function getCacheKey($prompt, $options) { - return 'prompt_' . md5($prompt . serialize($options)); - } + WP_CLI::add_command('ai generate', function($args) { + $content = AiClient::prompt($args[0])->generateTextResult()->toText(); + WP_CLI::success($content); + }); } -``` - -## Next Steps - -- **[Advanced Usage](5.advanced-usage.md)** - Custom providers, production deployment, and optimization - -## Resources - -- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/) -- [WordPress REST API](https://developer.wordpress.org/rest-api/) -- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/) -- [WP-CLI Commands](https://wp-cli.org/commands/) \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/5.advanced-usage.md b/docs/5.advanced-usage.md index e4b63537..1cca0d3b 100644 --- a/docs/5.advanced-usage.md +++ b/docs/5.advanced-usage.md @@ -1,972 +1,67 @@ # 5. Advanced Usage -Advanced patterns for custom providers, production deployment, monitoring, and optimization. - -## Custom AI Providers - -### Creating a Custom Provider - -Build your own AI provider to integrate proprietary or specialized AI services: +## Custom Providers ```php - new CustomTextModel($modelId, $modelConfig ?? new ModelConfig()), - 'custom-image-v1' => new CustomImageModel($modelId, $modelConfig ?? new ModelConfig()), - default => throw new \InvalidArgumentException("Unsupported model: {$modelId}") - }; - } - - public static function availability(): ProviderAvailabilityInterface - { - return new CustomProviderAvailability(); - } - - public static function modelMetadataDirectory(): ModelMetadataDirectoryInterface - { - return new CustomModelMetadataDirectory(); - } -} -``` - -### Custom Model Implementation - -```php -modelId = $modelId; - $this->config = $config; - $this->httpClient = HttpTransporterFactory::createTransporter(); - } - - public function generateTextResult(array $messages): GenerativeAiResult - { - $payload = $this->buildApiPayload($messages); - - try { - $response = $this->httpClient->post('/api/v1/generate', [ - 'json' => $payload, - 'headers' => [ - 'Authorization' => 'Bearer ' . $this->getApiKey(), - 'Content-Type' => 'application/json' - ] - ]); - - return $this->parseApiResponse($response); - } catch (\Exception $e) { - throw new \RuntimeException("Custom AI generation failed: " . $e->getMessage()); - } - } - - private function buildApiPayload(array $messages): array - { - return [ - 'model' => $this->modelId, - 'messages' => array_map(fn($msg) => $this->convertMessage($msg), $messages), - 'temperature' => $this->config->getTemperature(), - 'max_tokens' => $this->config->getMaxTokens(), - 'system_instruction' => $this->config->getSystemInstruction() - ]; - } - - private function parseApiResponse($response): GenerativeAiResult - { - $data = json_decode($response->getBody()->getContents(), true); - - if (!isset($data['choices'][0]['message']['content'])) { - throw new \RuntimeException('Invalid API response format'); - } - - return new GenerativeAiResult( - text: $data['choices'][0]['message']['content'], - usage: new Usage( - promptTokens: $data['usage']['prompt_tokens'] ?? 0, - completionTokens: $data['usage']['completion_tokens'] ?? 0, - totalTokens: $data['usage']['total_tokens'] ?? 0 - ) - ); - } - // Implement other required methods... - public function metadata(): ModelMetadata { /* ... */ } - public function setConfig(ModelConfig $config): void { $this->config = $config; } - public function getConfig(): ModelConfig { return $this->config; } } -``` - -### Provider Registration & Authentication - -```php -registerProvider(CustomAIProvider::class); - -// Set up authentication -$registry->setProviderRequestAuthentication( - 'custom-ai', - new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication( - $_ENV['CUSTOM_AI_API_KEY'] - ) -); - -// Use custom provider -$result = AiClient::prompt('Generate specialized content') - ->usingModel($registry->getProviderModel('custom-ai', 'custom-text-v1')) - ->generateTextResult(); - -echo $result->toText(); +$registry->registerProvider(CustomAiProvider::class); ``` -## Production Configuration - -### Environment-Based Setup +## Configuration ```php -config = [ - 'providers' => [ - 'openai' => [ - 'enabled' => (bool) $_ENV['OPENAI_ENABLED'] ?? true, - 'api_key' => $_ENV['OPENAI_API_KEY'] ?? '', - 'models' => [ - 'primary' => $_ENV['OPENAI_PRIMARY_MODEL'] ?? 'gpt-4o', - 'fallback' => $_ENV['OPENAI_FALLBACK_MODEL'] ?? 'gpt-4o-mini', - ], - 'rate_limits' => [ - 'requests_per_minute' => (int) $_ENV['OPENAI_RPM'] ?? 60, - 'tokens_per_minute' => (int) $_ENV['OPENAI_TPM'] ?? 100000, - ] - ], - 'anthropic' => [ - 'enabled' => (bool) $_ENV['ANTHROPIC_ENABLED'] ?? false, - 'api_key' => $_ENV['ANTHROPIC_API_KEY'] ?? '', - 'models' => [ - 'primary' => $_ENV['ANTHROPIC_PRIMARY_MODEL'] ?? 'claude-3-5-sonnet', - 'fallback' => $_ENV['ANTHROPIC_FALLBACK_MODEL'] ?? 'claude-3-haiku', - ] - ] - ], - 'cache' => [ - 'enabled' => (bool) $_ENV['AI_CACHE_ENABLED'] ?? true, - 'ttl' => (int) $_ENV['AI_CACHE_TTL'] ?? 3600, - 'driver' => $_ENV['AI_CACHE_DRIVER'] ?? 'redis', - ], - 'monitoring' => [ - 'enabled' => (bool) $_ENV['AI_MONITORING_ENABLED'] ?? true, - 'metrics_endpoint' => $_ENV['METRICS_ENDPOINT'] ?? '', - 'log_level' => $_ENV['AI_LOG_LEVEL'] ?? 'info', - ] - ]; - } - - public function setupProviders(): void - { - $registry = AiClient::defaultRegistry(); - - foreach ($this->config['providers'] as $providerId => $config) { - if (!$config['enabled'] || empty($config['api_key'])) { - continue; - } - - $this->registerProvider($registry, $providerId, $config); - } - } - - private function registerProvider(ProviderRegistry $registry, string $providerId, array $config): void - { - $providerClass = match($providerId) { - 'openai' => \WordPress\AiClient\ProviderImplementations\OpenAi\OpenAiProvider::class, - 'anthropic' => \WordPress\AiClient\ProviderImplementations\Anthropic\AnthropicProvider::class, - 'google' => \WordPress\AiClient\ProviderImplementations\Google\GoogleProvider::class, - default => throw new \InvalidArgumentException("Unknown provider: {$providerId}") - }; - - $registry->registerProvider($providerClass); - $registry->setProviderRequestAuthentication( - $providerId, - new \WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication($config['api_key']) - ); - } -} - -// Initialize production configuration -$config = new ProductionAIConfig(); -$config->setupProviders(); -``` - -### High-Availability Setup - -```php -providerPriority = $providerPriority; - $this->registry = $registry; - $this->logger = $logger; - } - - public function generateWithFallback(string $prompt, array $options = []): string - { - $lastException = null; - - foreach ($this->providerPriority as $providerId) { - if (!$this->registry->isProviderConfigured($providerId)) { - continue; - } - - try { - $model = $this->registry->getProviderModel( - $providerId, - $options['model'] ?? $this->getPreferredModel($providerId) - ); - - $result = AiClient::prompt($prompt) - ->usingModel($model) - ->usingTemperature($options['temperature'] ?? 0.7) - ->generateTextResult(); - - $this->logger->info("AI generation successful", [ - 'provider' => $providerId, - 'model' => $model->metadata()->getId(), - 'prompt_length' => strlen($prompt) - ]); - - return $result->toText(); - } catch (\Exception $e) { - $lastException = $e; - $this->logger->warning("AI generation failed, trying next provider", [ - 'provider' => $providerId, - 'error' => $e->getMessage() - ]); - } - } - - throw new \RuntimeException( - 'All AI providers failed. Last error: ' . ($lastException?->getMessage() ?? 'Unknown error') - ); - } - - private function getPreferredModel(string $providerId): string - { - return match($providerId) { - 'openai' => 'gpt-4o', - 'anthropic' => 'claude-3-5-sonnet', - 'google' => 'gemini-2.5-pro', - default => '' - }; - } -} - -// Usage -$haClient = new HighAvailabilityAIClient( - ['openai', 'anthropic', 'google'], // Provider priority order - AiClient::defaultRegistry(), - $logger -); - -$content = $haClient->generateWithFallback('Write about AI resilience'); -``` - -## Monitoring & Observability - -### Metrics Collection - -```php -metrics = $metrics; - $this->logger = $logger; - } - - public function trackGeneration(string $provider, string $model, float $duration, bool $success, array $metadata = []): void - { - // Increment counters - $this->metrics->increment('ai.generations.total', [ - 'provider' => $provider, - 'model' => $model, - 'status' => $success ? 'success' : 'failure' - ]); - - // Record timing - $this->metrics->histogram('ai.generation.duration', $duration, [ - 'provider' => $provider, - 'model' => $model - ]); - - // Track costs (if available) - if (isset($metadata['tokens'])) { - $this->metrics->histogram('ai.generation.tokens', $metadata['tokens'], [ - 'provider' => $provider, - 'model' => $model, - 'type' => $metadata['token_type'] ?? 'total' - ]); - } - - // Log detailed information - $this->logger->info('AI generation tracked', [ - 'provider' => $provider, - 'model' => $model, - 'duration_ms' => $duration * 1000, - 'success' => $success, - 'metadata' => $metadata - ]); - } - - public function trackError(string $provider, string $model, \Exception $error): void - { - $this->metrics->increment('ai.errors.total', [ - 'provider' => $provider, - 'model' => $model, - 'error_type' => get_class($error) - ]); - - $this->logger->error('AI generation error', [ - 'provider' => $provider, - 'model' => $model, - 'error' => $error->getMessage(), - 'trace' => $error->getTraceAsString() - ]); - } -} - -// Instrumented AI client -class InstrumentedAIClient -{ - private ProviderRegistry $registry; - private AIMetricsCollector $metrics; - - public function generateWithMetrics(string $prompt, array $options = []): string - { - $provider = $options['provider'] ?? 'auto'; - $model = $options['model'] ?? 'auto'; - $startTime = microtime(true); - - try { - $builder = AiClient::prompt($prompt); - - if (isset($options['model_instance'])) { - $builder->usingModel($options['model_instance']); - $provider = $options['model_instance']->metadata()->getProvider()->getId(); - $model = $options['model_instance']->metadata()->getId(); - } - - $result = $builder->generateTextResult(); - $duration = microtime(true) - $startTime; - - $this->metrics->trackGeneration($provider, $model, $duration, true, [ - 'tokens' => $result->getUsage()?->getTotalTokens() ?? 0, - 'prompt_length' => strlen($prompt) - ]); - - return $result->toText(); - } catch (\Exception $e) { - $duration = microtime(true) - $startTime; - $this->metrics->trackGeneration($provider, $model, $duration, false); - $this->metrics->trackError($provider, $model, $e); - throw $e; - } - } -} -``` - -### Health Checks - -```php - 'Generate a simple greeting', - 'image' => 'A small blue circle', - ]; - - public function checkProviderHealth(string $providerId): array - { - $results = [ - 'provider' => $providerId, - 'available' => false, - 'configured' => false, - 'models' => [], - 'latency' => null, - 'error' => null - ]; - - try { - // Check if provider is available and configured - $results['available'] = $this->registry->hasProvider($providerId); - $results['configured'] = $this->registry->isProviderConfigured($providerId); - - if (!$results['available'] || !$results['configured']) { - return $results; - } - - // Test text generation - $startTime = microtime(true); - $model = $this->registry->getProviderModel($providerId, $this->getTestModel($providerId)); - - $result = AiClient::prompt($this->testPrompts['text']) - ->usingModel($model) - ->usingMaxTokens(50) - ->generateTextResult(); - - $results['latency'] = microtime(true) - $startTime; - $results['models'][] = [ - 'id' => $model->metadata()->getId(), - 'type' => 'text', - 'status' => 'healthy', - 'response_length' => strlen($result->toText()) - ]; - - } catch (\Exception $e) { - $results['error'] = $e->getMessage(); - $results['latency'] = microtime(true) - ($startTime ?? microtime(true)); - } - - return $results; - } - - public function generateHealthReport(): array - { - $report = [ - 'timestamp' => date('c'), - 'overall_status' => 'healthy', - 'providers' => [] - ]; - - $availableProviders = ['openai', 'anthropic', 'google']; - $healthyCount = 0; - - foreach ($availableProviders as $providerId) { - $providerHealth = $this->checkProviderHealth($providerId); - $report['providers'][$providerId] = $providerHealth; - - if ($providerHealth['configured'] && !$providerHealth['error']) { - $healthyCount++; - } - } - - // Determine overall status - if ($healthyCount === 0) { - $report['overall_status'] = 'critical'; - } elseif ($healthyCount < count($availableProviders)) { - $report['overall_status'] = 'degraded'; - } - - return $report; - } - - private function getTestModel(string $providerId): string - { - return match($providerId) { - 'openai' => 'gpt-4o-mini', - 'anthropic' => 'claude-3-haiku', - 'google' => 'gemini-2.5-flash', - default => '' - }; - } -} - -// Health check endpoint -$healthChecker = new AIHealthChecker(); -$report = $healthChecker->generateHealthReport(); +// Custom model selection +$result = AiClient::prompt('Generate content') + ->usingModel('specific-model-id') + ->usingTemperature(0.8) + ->generateTextResult(); -header('Content-Type: application/json'); -http_response_code($report['overall_status'] === 'healthy' ? 200 : 503); -echo json_encode($report, JSON_PRETTY_PRINT); +// Batch processing +$prompts = ['Prompt 1', 'Prompt 2', 'Prompt 3']; +$results = AiClient::batchProcess($prompts)->generateTextResults(); ``` ## Performance Optimization -### Request Batching - ```php -registry = $registry; - $this->batchSize = $batchSize; - $this->rateLimitDelay = $rateLimitDelay; // milliseconds - } - - public function processBatch(array $prompts, array $options = []): array - { - $results = []; - $batches = array_chunk($prompts, $this->batchSize, true); - - foreach ($batches as $batchIndex => $batch) { - $batchResults = $this->processBatchChunk($batch, $options); - $results = array_merge($results, $batchResults); - - // Rate limiting between batches - if ($batchIndex < count($batches) - 1) { - usleep($this->rateLimitDelay * 1000); - } - } - - return $results; - } - - private function processBatchChunk(array $batch, array $options): array - { - $promises = []; - $results = []; - - // Create concurrent requests if HTTP client supports it - foreach ($batch as $key => $prompt) { - try { - $result = AiClient::prompt($prompt) - ->usingTemperature($options['temperature'] ?? 0.7) - ->usingMaxTokens($options['max_tokens'] ?? 500) - ->generateTextResult(); - - $results[$key] = [ - 'success' => true, - 'content' => $result->toText(), - 'usage' => $result->getUsage() - ]; - } catch (\Exception $e) { - $results[$key] = [ - 'success' => false, - 'error' => $e->getMessage(), - 'content' => null - ]; - } - } - - return $results; - } -} - -// Usage example -$batchProcessor = new BatchAIProcessor(AiClient::defaultRegistry(), 3, 500); - -$prompts = [ - 'product_1' => 'Write a description for wireless headphones', - 'product_2' => 'Write a description for laptop stand', - 'product_3' => 'Write a description for coffee mug', - 'product_4' => 'Write a description for desk lamp', -]; - -$results = $batchProcessor->processBatch($prompts, [ - 'temperature' => 0.8, - 'max_tokens' => 300 +// Connection pooling +$registry->setHttpClientOptions([ + 'pool_size' => 10, + 'timeout' => 30 ]); -foreach ($results as $key => $result) { - if ($result['success']) { - echo "✅ {$key}: {$result['content']}\n\n"; - } else { - echo "❌ {$key}: {$result['error']}\n\n"; - } -} -``` - -### Caching Strategies - -```php -withStreaming() + ->generateTextResult(); -class SmartAICache -{ - private CacheInterface $cache; - private array $config; - - public function __construct(CacheInterface $cache, array $config = []) - { - $this->cache = $cache; - $this->config = array_merge([ - 'default_ttl' => 3600, - 'max_prompt_length' => 1000, - 'cache_images' => true, - 'cache_audio' => false, - 'invalidation_patterns' => [] - ], $config); - } - - public function generateWithCache(string $prompt, array $options = []): string - { - // Skip caching for very long prompts or dynamic content - if (strlen($prompt) > $this->config['max_prompt_length'] || - $this->containsDynamicContent($prompt)) { - return $this->generateDirect($prompt, $options); - } - - $cacheKey = $this->generateCacheKey($prompt, $options); - - // Try to get from cache - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return $cached; - } - - // Generate and cache - $result = $this->generateDirect($prompt, $options); - $ttl = $this->calculateTtl($prompt, $options); - $this->cache->set($cacheKey, $result, $ttl); - - return $result; - } - - private function generateDirect(string $prompt, array $options): string - { - $builder = AiClient::prompt($prompt); - - foreach ($options as $key => $value) { - match($key) { - 'temperature' => $builder->usingTemperature($value), - 'max_tokens' => $builder->usingMaxTokens($value), - 'model' => $builder->usingModel($value), - default => null - }; - } - - return $builder->generateTextResult()->toText(); - } - - private function generateCacheKey(string $prompt, array $options): string - { - $keyData = [ - 'prompt' => md5($prompt), - 'options' => $options, - 'version' => '1.0' - ]; - - return 'ai_content_' . md5(serialize($keyData)); - } - - private function containsDynamicContent(string $prompt): bool - { - $dynamicPatterns = [ - '/\btoday\b/i', - '/\bnow\b/i', - '/\bcurrent\b/i', - '/\d{4}-\d{2}-\d{2}/', // Date patterns - '/\{[^}]+\}/', // Template variables - ]; - - foreach ($dynamicPatterns as $pattern) { - if (preg_match($pattern, $prompt)) { - return true; - } - } - - return false; - } - - private function calculateTtl(string $prompt, array $options): int - { - // Longer TTL for stable content types - if (str_contains(strtolower($prompt), 'definition') || - str_contains(strtolower($prompt), 'explain')) { - return $this->config['default_ttl'] * 24; // 24 hours - } - - // Shorter TTL for creative content - if (isset($options['temperature']) && $options['temperature'] > 0.8) { - return $this->config['default_ttl'] / 2; // 30 minutes - } - - return $this->config['default_ttl']; - } - - public function invalidatePattern(string $pattern): int - { - // Implementation depends on cache backend - // This is a simplified example - $deletedCount = 0; - - // Redis example: - // $keys = $this->cache->keys("ai_content_*{$pattern}*"); - // $deletedCount = $this->cache->del($keys); - - return $deletedCount; - } -} +// Memory management +$result = AiClient::prompt('Generate content') + ->usingMaxTokens(1000) + ->generateTextResult(); ``` -## Security Considerations - -### Input Sanitization +## Monitoring ```php -)<[^<]*)*<\/script>/mi', - '/javascript:/i' - ]; - - private int $maxPromptLength = 10000; - - public function secureGenerate(string $prompt, array $options = []): string - { - // Validate and sanitize prompt - $sanitizedPrompt = $this->sanitizePrompt($prompt); - - // Additional security checks - $this->validatePrompt($sanitizedPrompt); - - // Rate limiting by user/IP - $this->checkRateLimit($options['user_id'] ?? null); - - // Content filtering - $result = AiClient::prompt($sanitizedPrompt) - ->usingTemperature(min($options['temperature'] ?? 0.7, 0.9)) // Cap temperature - ->usingMaxTokens(min($options['max_tokens'] ?? 1000, 2000)) // Cap tokens - ->generateTextResult(); - - // Output filtering - return $this->filterOutput($result->toText()); - } - - private function sanitizePrompt(string $prompt): string - { - // Remove potential injection patterns - foreach ($this->blockedPatterns as $pattern) { - $prompt = preg_replace($pattern, '', $prompt); - } - - // Normalize whitespace - $prompt = preg_replace('/\s+/', ' ', trim($prompt)); - - // Truncate if too long - if (strlen($prompt) > $this->maxPromptLength) { - $prompt = substr($prompt, 0, $this->maxPromptLength) . '...'; - } - - return $prompt; - } - - private function validatePrompt(string $prompt): void - { - if (empty(trim($prompt))) { - throw new \InvalidArgumentException('Prompt cannot be empty'); - } - - // Check for suspicious patterns - foreach ($this->blockedPatterns as $pattern) { - if (preg_match($pattern, $prompt)) { - throw new \SecurityException('Prompt contains blocked content'); - } - } - } - - private function filterOutput(string $output): string - { - // Remove potentially sensitive information - $output = preg_replace('/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/', '[REDACTED]', $output); - $output = preg_replace('/\b[\w\.-]+@[\w\.-]+\.\w+\b/', '[EMAIL]', $output); - - return $output; - } - - private function checkRateLimit(?string $userId): void - { - // Implementation depends on rate limiting system - // This is a placeholder - if ($userId && $this->isRateLimited($userId)) { - throw new \Exception('Rate limit exceeded'); - } - } - - private function isRateLimited(string $userId): bool - { - // Check rate limiting logic - return false; - } -} -``` - -## Deployment Patterns - -### Docker Configuration - -```dockerfile -# Dockerfile for AI-powered PHP application -FROM php:8.2-fpm - -# Install required extensions -RUN apt-get update && apt-get install -y \ - git \ - zip \ - unzip \ - redis-tools \ - && docker-php-ext-install \ - bcmath \ - sockets - -# Install Composer -COPY --from=composer:latest /usr/bin/composer /usr/bin/composer - -# Set working directory -WORKDIR /app - -# Copy composer files -COPY composer.json composer.lock ./ - -# Install PHP dependencies -RUN composer install --no-dev --optimize-autoloader --no-scripts - -# Copy application code -COPY . . - -# Set permissions -RUN chown -R www-data:www-data /app/storage /app/cache - -# Environment configuration -ENV PHP_MEMORY_LIMIT=512M -ENV PHP_MAX_EXECUTION_TIME=300 -ENV AI_CACHE_DRIVER=redis -ENV AI_CACHE_TTL=3600 - -EXPOSE 9000 -CMD ["php-fpm"] -``` - -```yaml -# docker-compose.yml -version: '3.8' - -services: - app: - build: . - volumes: - - .:/app - environment: - - OPENAI_API_KEY=${OPENAI_API_KEY} - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - REDIS_HOST=redis - - DB_HOST=mysql - depends_on: - - redis - - mysql - networks: - - ai-app - - nginx: - image: nginx:alpine - ports: - - "80:80" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - depends_on: - - app - networks: - - ai-app - - redis: - image: redis:alpine - volumes: - - redis_data:/data - networks: - - ai-app - - mysql: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} - MYSQL_DATABASE: ${DB_NAME} - volumes: - - mysql_data:/var/lib/mysql - networks: - - ai-app - -volumes: - redis_data: - mysql_data: - -networks: - ai-app: - driver: bridge -``` - -## Resources & Further Reading - -### Official Documentation -- [PHP AI Client Architecture](ARCHITECTURE.md) -- [Provider Implementation Guide](https://github.com/WordPress/php-ai-client/wiki) -- [Model Capabilities Reference](https://github.com/WordPress/php-ai-client/wiki/capabilities) - - -### Community -- [GitHub Discussions](https://github.com/WordPress/php-ai-client/issues) -- [WordPress AI Team](https://make.wordpress.org/ai/) -- [Contributing Guidelines](https://github.com/WordPress/php-ai-client/blob/trunk/CONTRIBUTING.md) +// Usage tracking +add_action('ai_client_after_generation', function($result, $prompt) { + // Log usage metrics + error_log("AI generation: {$prompt} -> {$result->getTokenUsage()}"); +}); + +// Performance monitoring +$start = microtime(true); +$result = AiClient::prompt('Generate content')->generateTextResult(); +$duration = microtime(true) - $start; +``` \ No newline at end of file