Skip to content
Merged
118 changes: 118 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# The Joomla! AI Package

## Installation via Composer

Add `"joomla/ai": "~4.0"` to the require block in your composer.json and then run `composer install`.

```json
{
"require": {
"joomla/ai": "~4.0"
}
}
```

Alternatively, you can simply run the following from the command line:

```sh
composer require joomla/ai "~4.0"
```

If you want to include the test sources and docs, use

```sh
composer require --prefer-source joomla/ai "~4.0"
```

## Using the AI Framework
The AI Framework provides a straightforward, provider-agnostic interface for working with three main AI providers: OpenAI, Anthropic and Ollama. Instead of writing separate code for each provider’s SDK, developers write once and simply switch providers by changing configuration.

You can find the official provider APIs documentation here:
- OpenAI API: https://platform.openai.com/docs/api-reference
- Anthropic API: https://docs.anthropic.com/claude/docs
- Ollama API: https://github.com/ollama/ollama/blob/main/docs/api.md

The AI framework is built upon the Http package which provides an easy way to consume URLs and web services in a transport independent way. Joomla\Http currently supports streams, sockets and cURL. The framework centralizes HTTP handling in the Abstract Provider. Providers encapsulate:
- Base URLs, headers, and auth (API keys/tokens)
- Request building (JSON/multipart)
- Response normalization and error mapping into framework exceptions

## Instantiating a Provider

Each provider is instantiated with its configuration (API key, defaults such as model or base URL). You can override these defaults per call when needed.

### OpenAI:
```php
use Joomla\AI\AIFactory;

$openai = AIFactory::getAI('openai', [
'api_key' => getenv('OPENAI_API_KEY'),
// Optional defaults:
// 'model' => 'gpt-4o',
// 'base_url' => 'https://api.openai.com/v1',
]);
```

### Anthropic:
```php
use Joomla\AI\AIFactory;

$anthropic = AIFactory::getAI('anthropic', [
'api_key' => getenv('ANTHROPIC_API_KEY'),
// 'model' => 'claude-3-5-sonnet',
]);
```

### Ollama (local):
```php
use Joomla\AI\AIFactory;

$ollama = AIFactory::getAI('ollama', [
// 'base_url' => 'http://localhost:11434',
// 'model' => 'llama3',
]);
```

## Supported Methods

| Provider | Methods |
| --- | --- |
| OpenAI | `chat`, `vision`, `generateImage`, `createImageVariation`, `editImage`, `speech`, `transcribe`, `translate`, `createEmbeddings`, `moderate`, `isContentFlagged`|
| Anthropic | `chat`, `vision`, `getModel`|
| Ollama | `chat`, `generate`, `pullModel`, `copyModel`, `deleteModel`, `checkModelExists`, `getRunningModels`|

Not all providers implement every capability. The framework exposes capabilities via interfaces (e.g. ChatInterface, ImageInterface). Developers can use what each provider supports.

## Making your first request
All providers implement a shared set of capability interfaces (e.g., Chat, Images, Audio). Invoke these methods directly, passing per-call options to override defaults.

```php
// Chat example (OpenAI)
$response = $openai->chat("Write a haiku about Joomla.", [
'model' => 'gpt-4o-mini', // overrides constructor default if set
]);
echo $response->getContent(); // primary content (e.g. text)
$meta = $response->getMetadata(); // metadata content (e.g. model, usage)
```

## Error handling
Provider HTTP errors are mapped to framework exceptions (e.g. auth, rate limit, invalid arguments). Catch and handle them as needed.
```php
try {
$response = $openai->chat("Hello!");
} catch (\Throwable $e) {
// Log and surface a friendly message
}
```

## Documentation

- **[Overview](docs/overview.md)**
Architecture & goals

- **[Getting Started](docs/getting-started.md)**
Install, configure, first provider

- **[Guides](providers/)**
Provider-specific guides with code examples
---
96 changes: 96 additions & 0 deletions Tests/FactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

require_once '../vendor/autoload.php';

use Joomla\AI\AIFactory;
use Joomla\AI\Exception\ProviderException;

$configFile = __DIR__ . '/../config.json';
$config = json_decode(file_get_contents($configFile), true);
$api_key = $config['openai_api_key'] ?? null;
$anthropic_api_key = $config['anthropic_api_key'] ?? null;

echo "=== AI Factory Test Suite ===\n\n";

// Test Case 1: Invalid Provider
echo "1. Testing invalid provider 'abcd':\n";
try {
$options = [
'api_key' => $anthropic_api_key
];

$ai = AIFactory::getAI('abcd', $options);
$response = $ai->chat("Hey");
echo $response->getContent();

} catch (ProviderException $e) {
echo "Caught expected exception: " . $e->getMessage() . "\n";
}
echo "\n";

// Test Case 2: Valid Provider Creation
echo "2. Testing valid provider creation (anthropic):\n";
try {
$options = [
'api_key' => $anthropic_api_key
];

$ai = AIFactory::getAI('anthropic', $options);
echo "Provider name: " . $ai->getProvider()->getName() . "\n";
$response = $ai->chat("Hey");
echo $response->getContent();
} catch (Exception $e) {
echo "Failed to create Anthropic provider: " . $e->getMessage() . "\n";
}
echo "\n";

// Test Case 3: Non-existent Method Call
echo "3. Testing non-existent method call:\n";
try {
$options = [
'api_key' => $anthropic_api_key
];

$ai = AIFactory::getAI('anthropic', $options);
$response = $ai->nonExistentMethod("test");
echo $response->getContent();
} catch (ProviderException $e) {
echo "Caught expected Exception for non-existent method: " . $e->getMessage() . "\n";
}
echo "\n";

// Test Case 4: Available Providers
echo "4. Testing available providers:\n";
try {
$availableProviders = AIFactory::getAvailableProviders();
echo "Available providers: " . implode(', ', $availableProviders) . "\n";

// Test each provider availability
foreach ($availableProviders as $provider) {
$isAvailable = AIFactory::isProviderAvailable($provider);
echo "Provider '$provider' is available: " . ($isAvailable ? 'Yes' : 'No') . "\n";
}

// Test non-existent provider
$isAvailable = AIFactory::isProviderAvailable('non-existent');
echo "Provider 'non-existent' is available: " . ($isAvailable ? 'Yes' : 'No') . "\n";
} catch (Exception $e) {
echo "Failed to get available providers: " . $e->getMessage() . "\n";
}
echo "\n";

// Test Case 5: Valid Method Call
echo "5. Testing valid method calls:\n";
try {
$options = [
'api_key' => $anthropic_api_key
];

$ai = AIFactory::getAI('anthropic', $options);
$response = $ai->chat("Hey");
echo $response->getContent();
} catch (Exception $e) {
echo "Test Failed: " . $e->getMessage() . "\n";
}

echo "\n=== Test Suite Complete ===\n";
Loading
Loading