PHP Luminova: AI Client Manager, Method Forwarding, and Multi-Client Registry
Central AI manager for Luminova that forwards method calls to configurable clients like Ollama, OpenAI, and Anthropic, with support for static usage and custom client registration.
The Luminova AI helper (Luminova\AI\AI) class is the central manager that wraps any Luminova\Interface\AIClientInterface implementation and forwards calls to the active provider. It supports both instance-based and static usage patterns, lazy client instantiation from App\Config\AI, and a runtime registry so multiple AI engines can be used side-by-side within the same application.
Configuration
The AI helper reads its default client from Application AI Configuration. Set App\Config\AI::$handler to 'OpenAI', 'Ollama', or 'Anthropic' to control which provider is used when no explicit client is supplied.
// /app/Config/AI.php
namespace App\Config;
public string $handler = 'Anthropic';
public string $apiKey = 'your-key-here';Instantiation
Default Client from Config
use Luminova\AI\AI;
$ai = new AI();
$reply = $ai->message('Tell me a joke!');Inject an Explicit Client
use Luminova\AI\AI;
use Luminova\AI\Client\Ollama;
$ai = new AI(new Ollama('http://localhost:11434/api/'));
$reply = $ai->message('Explain quantum computing in plain language.');Singleton Access
Returns a shared singleton, creating it on the first call using the app's configured default client.
$reply = AI::getInstance()->message('Write a haiku about the ocean.');
echo $reply[0]['text'] ?? '';Swapping Clients on the Singleton
Passing a client to getInstance() after the first call replaces the active client without destroying the singleton.
use Luminova\AI\Client\Ollama;
AI::getInstance(new Ollama())->message('Hello from Ollama!');Static Method Forwarding
Any method defined on an AIClientInterface can be called statically on AI. The configured default client is resolved automatically.
// Equivalent to: (new AI())->message('...')
AI::message('Write a short story about Mars.');AI::chat([
['role' => 'system', 'content' => 'You are a friendly chef.'],
['role' => 'user', 'content' => 'What should I cook tonight?'],
]);Named Client Access
Built-in Clients
The AI class ships with three pre-registered client keys: openai, ollama, and anthropic. Call them as static methods, passing constructor arguments directly.
use Luminova\AI\AI;
// Luminova\AI\Client\Ollama
// https://luminova.ng/docs/0.0.0/ai-client/ollama
$ollama = AI::Ollama('http://localhost:11434/api/');
// Luminova\AI\Client\OpenAI
// https://luminova.ng/docs/0.0.0/ai-client/openai
$openai = AI::Openai('sk-your-key', 'org-abc');
// Luminova\AI\Client\Anthropic
// https://luminova.ng/docs/0.0.0/ai-client/anthropic
$anthropic = AI::Anthropic('sk-ant-your-key');Client Registry
Custom client can be registered in application or controller class onCreate method:
// /app/Application.php
use Luminova\AI\AI;
use App\AI\DeepSeek;
protected function onCreate(): void
{
// Register a live instance
AI::register('deepseek', new DeepSeek(...));
// Register a class name (instantiated on first use)
AI::register('deepseek', DeepSeek::class);
}Retrieve a Registered Client
Once registered, the client can be called like any built-in client:
With initialization arguments:
$myclient = AI::Myclient(...);
$openai->message('Hello from MyClient!');Call AI::client() with no argument to retrieve the default configured client:
$client = AI::client('myclient');
$reply = $client->message('Hello!');List All Registered Clients
foreach (AI::clients() as $name => $client) {
$type = is_string($client) ? $client : get_class($client);
echo $name . ': ' . $type . PHP_EOL;
}Switching Clients at Runtime
Replace the active client on an existing AI instance without creating a new one.
use Luminova\AI\AI;
use Luminova\AI\Client\Ollama;
use Luminova\AI\Client\Anthropic;
$ai = new AI(); // Uses app default
$ai->setClient(new Ollama('http://localhost:11434/api/'));
$reply = $ai->message('Now powered by Ollama!');
$ai->setClient(new Anthropic(env('ANTHROPIC_API_KEY')));
$reply = $ai->message('Now powered by Claude!');Inspect the Active Client
$client = AI::getInstance()->getClient();
echo get_class($client); // e.g. Luminova\AI\Client\OpenAISetting the Default Model
use Luminova\AI\AI;
use Luminova\AI\Model;
$ai = new AI();
$ai->setModel(Model::GPT_4_1_MINI);
$reply = $ai->message('Hello!');Computing Vector Similarity
Compute the cosine similarity between two equal-length embedding vectors. Returns a value in the range [-1, 1] where 1 means identical direction and 0 means orthogonal.
use Luminova\AI\AI;
use Luminova\AI\Client\OpenAI;
$client = new OpenAI(env('OPENAI_KEY'));
$vectors = $client->embed(['cat', 'kitten']);
$score = AI::compareCosineVector($vectors[0], $vectors[1]);
echo $score; // e.g. ~0.94Practical Examples
Chat with the Default Client
use Luminova\AI\AI;
$reply = AI::chat([
['role' => 'user', 'content' => 'What is the speed of light?'],
]);
echo $reply[0]['text'] ?? '';Embed and Compare Texts
use Luminova\AI\AI;
$vectors = AI::embed(['PHP is great', 'PHP is awesome']);
$score = AI::compareCosineVector($vectors[0], $vectors[1]);
echo round($score, 4); // ~0.98Generate an Image
use Luminova\AI\AI;
$images = AI::image('A sunset over a tropical beach, watercolor style.');
echo $images[0]['url'] ?? '';Web Search
use Luminova\AI\AI;
$results = AI::webSearch('Latest Luminova framework release');
print_r($results);Register and Use a Custom Provider
use Luminova\AI\AI;
use App\AI\MyChatClient;
AI::register('mychat', new MyChatClient());
$reply = AI::Mychat()->message('Hello from my custom provider!');Class Definition
- Full namespace:
Luminova\AI\AI - This class is a Final class
Methods
constructor
Create a new AI manager instance.
If no client is supplied, the default client configured inApp\Config\AI::$handler is instantiated automatically.
public __construct(?Luminova\Interface\AIClientInterface $client = null)Parameters:
| Parameter | Type | Description |
|---|---|---|
$client | Luminova\Interface\AIClientInterface|null | Optional custom client instance. |
Examples:
Using the application default client:
use Luminova\AI\AI;
$ai = new AI();
$reply = $ai->message('Tell me a joke!');Injecting an Ollama client explicitly:
use Luminova\AI\AI;
use Luminova\AI\Client\Ollama;
$ai = new AI(new Ollama('http://localhost:11434'));
$reply = $ai->message('Explain quantum computing in plain language.');getInstance
Get (or create) the shared AI singleton instance.
On first call, a new instance is created using the application'sdefault client. Subsequent calls return the same instance.
Passing a $client on any call after the first will replace the active clientwithout destroying the singleton.
public static getInstance(?Luminova\Interface\AIClientInterface $client = null): selfParameters:
| Parameter | Type | Description |
|---|---|---|
$client | Luminova\Interface\AIClientInterface|null | Optional client to set or replace. |
Return Value:
self - Return singleton object of AI class.
Examples:
using default application AI client:
use Luminova\AI\AI;
$reply = AI::getInstance()->message('Write a haiku about the ocean.');Swapping clients on the singleton:
use Luminova\AI\AI;
use Luminova\AI\Client\Ollama;
AI::getInstance(new Ollama())->message('Hello from Ollama!');getClient
Get the currently active AI client.
public getClient(): Luminova\Interface\AIClientInterfaceReturn Value:
Luminova\Interface\AIClientInterface - Return instance of client class.
Example:
use Luminova\AI\AI;
$client = AI::getInstance()->getClient();
echo get_class($client); // Luminova\AI\Client\OpenAIsetClient
Replace the active AI client.
Allows switching engines at runtime without creating a new AI instance.
public setClient(Luminova\Interface\AIClientInterface $client): selfParameters:
| Parameter | Type | Description |
|---|---|---|
$client | Luminova\Interface\AIClientInterface | The new client to use. |
Return Value:
self - Return instance of AI class.
Example:
Change the current AI client:
use Luminova\AI\AI;
use Luminova\AI\Client\Ollama;
$ai = new AI();
$ai->setClient(new Ollama('http://localhost:11434'));
$reply = $ai->message('Now powered by Ollama!');setModel
Set the default model for all subsequent AI requests.
public setModel(BackedEnumLuminova\AI\Model<BackedEnum>|string $model): selfParameters:
| Parameter | Type | Description |
|---|---|---|
$model | BackedEnum|Luminova\AI\Model | AI model string name or enum model (e.g, Model::GPT_4_1_MINI or gpt-4.1-mini). |
Return Value:
self - Return instance of AI class.
Example:
Change the current AI client:
use Luminova\AI\Model;
// Using Model BackedEnum
$ai->setModel(Model::GPT_4_1_MIN);
// Using literal string
$ai->setModel('gpt-4.1-mini');
$reply = $ai->message('What is the weather today like?');register
Register a named client in the global client registry.
Accepts either a pre-built instance or a fully-qualified class name.Once registered, the client can be retrieved via static method callsor AI::client().
public static register(
string $name,
Luminova\Interface\AIClientInterface|class-string<Luminova\Interface\AIClientInterface> $client
): voidParameters:
| Parameter | Type | Description |
|---|---|---|
$name | string | Case-insensitive registry key (e.g. 'openai'). |
$client | AIClientInterface|class-string | Client instance or class name. |
Examples:
Registering a live instance
use Luminova\AI\AI;
AI::register('myclient', new MyCustomProvider(...));Registering a class name (instantiated on first use):
use Luminova\AI\AI;
AI::register('myclient', MyCustomProvider::class);
AI::client('myclient')->message('Hello!');
AI::Myclient(...)->message('Hello from Myclient!');client
Retrieve a client instance from the registry by name.
If no name is given, the application's default client is returned.If the client entry is a class name string, it is instantiated on first access and the instance is cached for subsequent calls.
public static client(?string $name = null): AIClientInterfaceParameters:
| Parameter | Type | Description |
|---|---|---|
$name | string|null | Registry key (case-insensitive), or null for the default. |
Return Value:
Luminova\Interface\AIClientInterface - Return client class object.
Throws:
- \Luminova\Exceptions\RuntimeException - If the requested client is not registered.
Examples:
Get client object:
use Luminova\AI\AI;
$openai = AI::client('openai');
$ollama = AI::client('ollama');
$openai->message('Hello from OpenAI!');
$ollama->message('Hello from Ollama!');clients
Return all registered client entries.
public static clients(): array<string,class-string<AIClientInterface>|AIClientInterface>Values are either class-name strings (not yet instantiated) or liveAIClientInterface objects.
Return Value:
array<string,class-string<Luminova\Interface\AIClientInterface>|Luminova\Interface\AIClientInterface> - Return array of all registered AI client.
Example:
Get array of clients:
use Luminova\AI\AI;
foreach (AI::clients() as $name => $client) {
echo $name . ': ' . (is_string($client) ? $client : get_class($client));
}compareVector
Compute the cosine similarity between two equal-length embedding vectors.
Returns a value in the range [-1, 1] where 1 means identical direction,0 means orthogonal, and -1 means opposite direction. Useful for comparing embeddings produced by AIClientInterface::embed().
public static compareCosineVector(float[] $a, float[] $b): floatParameters:
| Parameter | Type | Description |
|---|---|---|
$a | float[] | First embedding vector. |
$b | float[] | Second embedding vector (must be the same length as $a). |
Return Value:
float - Cosine similarity score.
Example:
Compare embeddings vectors:
use Luminova\AI\AI;
$vectors = $ai->embed(['cat', 'kitten']);
$score = AI::compareCosineVector($vectors[0], $vectors[1]);
// $score → ~0.94 (very similar)